The Custom node is a text box that accepts HLSL and quietly becomes part of your material shader. Used well, it is a superpower. Used badly, it is an unreadable black box a colleague inherits. This is the sane version: small snippets, material functions, and master-material discipline.
Add a Custom node, and in the Details panel: write your HLSL in Code, declare Inputs (each becomes a pin), set the Output Type. The code's final line must return something matching that type. The dissolve from the previous page, wired for real:
float cut = step(Noise, Progress); float edge = smoothstep(Progress - 0.06, Progress, Noise) * (1.0 - cut); // pack: rgb = emissive edge glow, a = opacity mask return float4(edge * EdgeColor * 8.0, 1.0 - cut);
Output Type CMOT Float4, then split downstream: rgb -> Emissive, a -> Opacity Mask. Iteration is live - edit code, the preview recompiles. For anything longer than ~20 lines, write in a real editor and paste; the text box has the ergonomics of a postage stamp.
CU_DissolveEdge), name inputs like parameters, and leave a // one-line what & why at the top of the code. The colleague who inherits it may be you at year's end.Tex.SampleLevel(TexSampler, UV, 0) - most of the time it's cleaner to sample with a native TextureSample node and pipe the resulting float4 in.A Material Function is a reusable subgraph with defined inputs/outputs - the shader equivalent of a Python function, and the difference between a shader library and forty materials with copy-pasted fresnel that all drifted apart. The discipline that makes them work:
MF_WorldAlignedNoise, MF_DetectUpFacing, MF_TriplanarSample - if the name needs "And", split it./Game/Materials/Functions/MF_*, enforced like any convention - a pre-submit regex validator keeps it honest.The architecture pattern from the interview bank, in practice: a handful of master materials (opaque-standard, foliage, glass, VFX) with parameters, and every asset material an instance overriding textures and scalars. Instances share the master's compiled shader - thirty looks, one compile - and artists get sliders instead of graphs. Design the parameter surface like tool UX, because it is: group parameters (Base / Wear / Advanced), name them in artist ("Dirt Amount", not "NoiseRemapMax"), clamp ranges to values that can't look broken, and keep the count under ~15 visible - past that, add a second master, not a second page of sliders.
A Static Switch parameter doesn't branch at runtime - it compiles both versions of the shader, and every combination multiplies: five independent switches = 2^5 = 32 shaders from one master. Ten = 1,024. This is how a studio wakes up to six-hour shader compiles and a gigabyte of pipeline cache, and it lands on the TA's desk when it happens.
Budget rules: reach for a lerp with a scalar parameter when the cost of computing both paths is small (it usually is) - instances changing a scalar don't add permutations; save switches for genuinely heavyweight optional features (parallax, extra texture sets); and audit occasionally - Unreal's material stats list permutation counts, and the materials performance guide covers the wider budget. When the AD asks for "just one more toggle" on the master, this paragraph is your costed yes: "Sure - that doubles the shader count. Or it can be a scalar blend at about 4 instructions. Which would you like?"
I inherited a project whose "master material" had 23 static switches - over 8 million theoretical permutations, of which the build farm was dutifully compiling the ~40,000 combinations actually used. First shader build after a clean DDC took most of a night. We rebuilt it as four masters and lerp-blends, cut compile time to 40 minutes, and the visual diff was literally imperceptible. The artist who built the original wasn't wrong to want flexibility - nobody had ever told them switches multiply. Now you've been told.