Minecraft PBR - Derive AO + Height
Your normal map hides two extra maps — this node digs them out
- normal
- height_mask
- ao
- height
A material estimator gives you four maps, but two of them - ambient occlusion and parallax height - usually aren't among them. Minecraft PBR - Derive AO + Height manufactures both from a single normal map, and it's the same math the labPBR packer runs internally when you leave derive_ao_height on. The point of the standalone node is control: you get to preview and tune the AO and height before they get baked into the _n texture, or feed the outputs somewhere else entirely.
How it works
- AO comes from the divergence of the tangent-space normal field - basically how much the normals splay apart, which is positive in crevices and negative on bumps. A Sobel pass plus a Gaussian blur turns that into an occlusion value (1 = fully unoccluded).
ao_strength(default 2) controls how deep the effect bites;ao_blur(default 5) smooths the result. - Height is reconstructed by Frankot–Chellappa integration: the normal's X/Y gradients get integrated in frequency space with an FFT into a height field, then remapped into
[height_min, 1].height_mindefaults to 0.25 - the author's deliberate Minecraft choice, since 0.25 = 25% block depth for parallax occlusion mapping.height_invert(default on) flips the direction so bumps rise instead of sink. - seamless (default off) mirrors the gradients into a 2×2 tile before integrating, so a tileable texture comes back without a seam at the edges. For game textures, this is the switch you'll almost always want.
Outputs are two MASKs, ao and height, which wire straight into the packer's optional ao / height inputs as overrides.
Inputs that matter
Only a handful matter day to day: normal (the only required input), seamless, ao_strength, ao_blur, height_min (leave at 0.25 for Minecraft), height_invert, plus the optional height_mask - where the mask is 1, height is flattened to height_min, handy for carving out flat spots like table tops or steps.
Install
ComfyUI Manager → Install Custom Nodes → search "comfy-ui-minecraft-pbr", install, restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/jasonjgardner/comfy-ui-minecraft-pbr
# then restart ComfyUI
No model downloads, no pip extras - torch does the FFTs.
Gotchas
Derived AO and height are approximations, and they're honest about it. They're plenty for the AO baked into _n and for POM parallax, but don't feed the height into a displacement map and expect true sculpting detail - FFT integration smears high-frequency noise. And if your normal isn't tileable but you skip seamless, you'll see the integration seam down one edge. One more: because the packer derives these automatically, you only need this node when you want to see and edit the channels first. For a straight Chord-to-shader run, you can skip it entirely and let the packer do it invisibly.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| normal | IMAGE | Tangent-space normal map (OpenGL Y-up). | |
| seamless | BOOLEAN | false | Treat the input as tileable. |
| ao_strength | FLOAT | 2.00–10 | — |
| ao_blur | INT | 50–64 | — |
| height_intensity | FLOAT | 1.000–1 | — |
| height_min | FLOAT | 0.250–1 | Deepest POM value; 0.25 = 25% block depth for Minecraft. |
| height_invert | BOOLEAN | true | Flip POM direction so bumps rise. |
| height_maskopt | MASK | Where 1, flatten height to height_min. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| ao | MASK | — |
| height | MASK | — |