Minecraft PBR - Pack labPBR
The node that turns PBR maps into labPBR textures your shader will actually read
- basecolor
- normal
- roughness
- metalness
- ao
- height
- emission
- porosity
- sss
- metal_mask
- height_mask
- albedo
- specular
- normal
If you've ever fed a PBR map set into a Minecraft shader and gotten a texture that looks like painted plastic, this is the missing step. Minecraft PBR - Pack labPBR takes the four maps a material estimator spits out - basecolor, normal, roughness, metalness - and repacks them into labPBR 1.3, the channel-coded format that Iris and OptiFine shaders actually read. It doesn't run any model, it doesn't call any API, and it needs no key. It's a packer: the boring, essential job of translating your AI output into something a game engine believes.
What it produces
Feed it the standard four maps and you get three RGBA textures out: albedo, specular (the _s texture), and normal (the _n texture). That's it. Wire all three into the pack's Save labPBR node and you have a working resource pack set.
The clever bit is what goes inside those alpha channels. labPBR is a channel-code standard, and this node does the encoding for you:
_s: R = perceptual smoothness (1 − √roughness), G = F0 / metal id (dielectric ≈ byte 10, named metals 230–237, custom 255), B = porosity (0–64) or SSS (65–255 - SSS wins per-pixel), A = emission (255 = none, 0–254 = level)._n: R/G = normal X/Y in DirectX Y-down, B = ambient occlusion, A = POM height for parallax (0.25 = 25% block depth).
Everything stays linear - the pack applies no gamma anywhere, which is what shaders expect from these channels.
Inputs that matter
- basecolor, normal, roughness, metalness - the big four.
roughnessandmetalnessaccept an IMAGE or a MASK, so Chord's 1-channel outputs wire straight in. - derive_ao_height (default on) - computes AO and height from the normal when you don't supply them. Leave it on.
- flip_normal_y (default on) - converts an OpenGL Y-up normal to DirectX Y-down. Turn it off only if your input is already DirectX.
- hardcoded_metal - the F0 id for metallic pixels (none/custom/iron/gold/aluminum/chrome/copper/lead/platinum/silver). "none" uses custom (255), where the shader reads F0 from the albedo.
- compute_porosity / compute_sss / compute_emission - auto-derive those channels, or pass your own via the optional
ao,height,emission,porosity,sss,metal_mask, andheight_maskinputs. - seamless - treat the input as tileable; turn this on for anything that will repeat, which for game textures is most things.
Install
ComfyUI Manager → Install Custom Nodes → search "comfy-ui-minecraft-pbr" (or "Minecraft PBR"), install, restart. Or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/jasonjgardner/comfy-ui-minecraft-pbr
# then restart ComfyUI
No model downloads, no extra Python packages - torch, numpy and Pillow already ship with ComfyUI. This is one of the rare packs you can install and forget.
Gotchas
Where people get burned: the default assumes an OpenGL Y-up normal, which is what Chord and most generators emit - flip it only if your source is already DirectX. And don't pipe the specular or normal outputs into the stock SaveImage node; it drops alpha, which corrupts the channel-coded textures. Use the pack's Save labPBR node, which writes faithful linear RGBA PNGs. Finally, remember roughness/metalness images get collapsed to luminance if they're multi-channel - if your roughness lives in one RGB channel, extract it first.
Reference for the standard itself: shaderlabs.org/wiki/LabPBR_Material_Standard.
Inputs (26)
| Name | Type | Default | Description |
|---|---|---|---|
| basecolor | IMAGE | — | |
| normal | IMAGE | Tangent-space normal (OpenGL Y-up). | |
| roughness | IMAGE,MASK | — | |
| metalness | IMAGE,MASK | — | |
| hardcoded_metal | COMBO | none | Metal id for metallic pixels; 'none' uses custom (255). |
| flip_normal_y | BOOLEAN | true | labPBR _n stores DirectX Y-down; flip an OpenGL Y-up normal (default). Disable only if your input is already DirectX. |
| swap_normal_xy | BOOLEAN | false | — |
| derive_ao_height | BOOLEAN | true | Derive AO + height from the normal when not supplied. |
| include_height | BOOLEAN | true | Write derived height into _n alpha (POM). |
| seamless | BOOLEAN | false | — |
| compute_porosity | BOOLEAN | false | — |
| compute_sss | BOOLEAN | false | — |
| compute_emission | BOOLEAN | false | — |
| emission_threshold | FLOAT | 0.850–1 | — |
| emission_knee | FLOAT | 0.100–1 | — |
| ao_strength | FLOAT | 2.00–10 | — |
| ao_blur | INT | 50–64 | — |
| height_intensity | FLOAT | 1.000–1 | — |
| height_invert | BOOLEAN | true | — |
| aoopt | IMAGE,MASK | Override derived AO. | |
| heightopt | IMAGE,MASK | Override derived height (e.g. Chord 'Normal to Height' IMAGE). | |
| emissionopt | IMAGE,MASK | Override extracted emission. | |
| porosityopt | IMAGE,MASK | — | |
| sssopt | IMAGE,MASK | — | |
| metal_maskopt | IMAGE,MASK | Per-pixel metal-id map (from Pack Metal Masks); overrides hardcoded_metal/metalness on painted pixels. | |
| height_maskopt | MASK | Where 1, flatten POM height. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| albedo | IMAGE | — |
| specular | IMAGE | — |
| normal | IMAGE | — |