Exact MaterialsBlender's node maths, running inside Unreal.
The usual way to get a Blender material into Unreal is to bake it into flat textures. Venom Bridge Link doesn't bake. It compiles the node tree itself into one Unreal shader that runs the same maths as Blender's Cycles renderer. Here is how it works, with a test you can run in this page.
Baking: a snapshot
- Render the procedural nodes into images at a fixed size
- Unreal gets pictures, not maths: up close they run out of pixels
- Change a value and the bake is out of date until you bake again
- Noise, ramps and maths only survive as far as the image resolution allows
Exact: the maths itself
- The node tree becomes one Unreal shader, evaluated for every pixel
- The node code is Cycles' own, ported to HLSL, so the numbers match
- Values are shader parameters: change one and Unreal updates without a compile
- Sharp at any distance, because nothing was rendered into a texture
The test bench, live in your browser.
The plugin is checked by rendering a material in Cycles and in Unreal and comparing every pixel. This page does the same with one material, Noise Texture into a Color Ramp, computed three ways on your own machine. Move the sliders and watch the numbers.
The yardstick: every pixel of the other two is compared with this.
- Largest difference
- …
- Average
- …
- Largest difference
- …
- Average
- …
The bench starts when it scrolls into view.– integer hashes per pixel
Drag a slider
The exact shader changes in the same frame. Nothing is rebuilt: the new value is handed to the shader, the way the plugin updates a material instance's parameters in Unreal. The bake stays as it was until you bake again.
Zoom in
The reference and the exact shader stay sharp, because they are worked out for every pixel. The bake has 64×64 pixels to stretch: the fine detail it never captured turns to blur up close, and no zoom brings it back.
Read the numbers
The exact shader's difference is tiny, usually well under 0.001, and it is only rounding: 32-bit floats on the GPU against a 64-bit reference. The plugin's own bench holds Unreal to 0.002 of Cycles, about half of one step of an 8-bit screen.
This page runs the node maths as WebGL. In Unreal the plugin generates HLSL from the same Cycles code. The noise is Cycles' Perlin noise (BSD-3-Clause, Sony Pictures Imageworks and the Blender Foundation); the notice is in the page's script.
From a node tree to one shader.
Five things happen between Blender and Unreal. Step through them, or press Play.
Read the node tree
- Groups are opened up, reroutes followed and muted nodes passed through, as Cycles does.
- Only nodes that reach the active Material Output are kept. Anything else stays in Blender.
- Values, links, ramps, curves and images are read straight from Blender.
{"v": 1,
"nodes": [
{ "id": "n0", "type": "TEX_COORD" },
{ "id": "n1", "type": "TEX_NOISE",
"props": { "dimensions": "3D", "type": "FBM" },
"in": { "Vector": { "link": ["n0", "Generated"] },
"Scale": { "v": 5.0 }, "Detail": { "v": 4.0 } } },
{ "id": "n2", "type": "VALTORGB", "lut": { "row": 0 } },
{ "id": "n3", "type": "BSDF_PRINCIPLED" }
],
"out": { "surface": ["n3", "BSDF"] } }Simplified for the page.
One canonical graph
- The tree becomes plain data, with node ids given in a fixed order from the output.
- Node names, the order they were made in and group wrappers don't change it. Only the structure does.
- So two materials laid out the same way give the same graph, and Unreal can tell.
- Constant maths is worked out in Blender beforehand and sent as live values.
// one Custom expression in the master material
struct BLL {
uint hash_uint3(uint kx, uint ky, uint kz) { … } // Cycles' hash
float perlin_3d(float x, float y, float z) { … } // Cycles' noise
float noise_fbm3(float3 p, …) { … }
};
BLL b;
float3 p = Generated * Scale;
float fac = b.noise_fbm3(p, Detail, Roughness, 2.0, 1.0);
float3 col = BLL_LUT.Sample(…, float2(fac, Row0)); // the ramp
return col;Simplified for the page. Highlighted names are live parameters.
Generate one shader
- The plugin carries Cycles' node maths ported to HLSL: hashes, noise, Voronoi, Gabor, colour, vector and maths nodes.
- Each material gets only the functions it uses, in one Custom expression.
- Structure and constants are kept as in Cycles, so the same input gives the same number.
this layout · compiled once
new layout · compiles once
Share the compiled master
- Materials laid out the same way (the same nodes, settings and links) use one compiled master. Names, values and images may differ.
- A new material with a layout Unreal already has is ready in well under a second, with no shader compile.
- Only a new layout compiles, once, in a few seconds.
Values stay live
- Any unlinked value, Value and RGB nodes, Mapping, maths on constants and IOR are parameters on the material instance. Changing one compiles nothing.
- Color Ramps and curves are rows of one small lookup texture, filled from Blender's own 257-entry tables, so every interpolation and colour mode matches.
- The one exception: a value that switches part of the shader on or off, such as alpha or emission, rebuilds it once.
Shared masters, ramps and curves as lookup rows, and live Mapping and maths came in version 1.15.
What the plugin's test bench measures.
Two benches, run against Cycles. The image bench renders each node graph in Cycles as a 64×64 image and the same graph in Unreal, and reads both back as exact floats. The mesh bench renders a rotated, non-uniformly scaled sphere, moved off its pivot, in both, and compares every covered pixel.
From the plugin's own test bench, version 1.17. The mesh tests that aren't within 0.002 are under Known differences.
Where it isn't 1:1, and why.
The bench reports these as they are.
Measured differences
- With Unreal's default GPU texture filtering, sRGB colour images seen magnified can differ by up to about 0.27. Turn on Cycles Texture Filtering and they are within 0.002.
- Box-projected images: about 0.003.
- Strong tangent-space normal maps on synthetic test images: up to about 0.05. On a real 2048 px normal map, 99% of pixels are within 0.0007.
- Lighting is Unreal's own (Lumen): the material matches, and the final image follows Unreal's lights.
Not possible in a real-time shader
- Sky and IES textures, OSL Script, Raycast, Wireframe, Pointiness, Random Per Island and volume shaders. Each material lists any it uses.
- The Principled BSDF's subsurface, sheen, coat tint, coat normal, thin film and diffuse roughness: Unreal's Default Lit has no such layers, and a note says so for each one in use.
We couldn't find anyone else doing it.
We went through the Blender ⇄ Unreal exporters, live-sync add-ons and bridges on sale. The material converters we found rebuild a Principled BSDF and bake procedural nodes into images. We couldn't find another that runs the node tree itself in Unreal, with Cycles' own maths, and measures the result against Cycles.
See your own materials in Unreal.
Every feature free for 7 days, Exact Materials included.