Normal Map Relighter (IBL)
Relight From an Environment Map
- image
- normal_map
- environment_map
- relit_image
- diffuse_lighting
- specular_lighting
Here's the one that feels like cheating: you can relight an image from an equirectangular HDRI environment map, entirely in ComfyUI, no 3D renderer, no diffusion model, no sampling. Normal Map Relighter (IBL) does image-based lighting with your normal map as the geometry and your environment map as the sky.
The directional relighters are one light. This one is a whole scene. The environment map provides both the diffuse color that wraps around the surface and the specular reflections that slide across it as the surface angles change - which is the thing that makes materials look real instead of just lit. If you've got a glossy surface in a brick or fabric PBR set and want to see it under actual studio lighting before you ship it, this is the preview node.
How it works
Image-based lighting in miniature. The node:
- Decodes your normal map to vectors (with the OpenGL/DirectX green-flip handled by
normal_format). - Converts each pixel's normal direction into an equirectangular UV coordinate and samples the environment map with bilinear interpolation - that's the diffuse lighting, so the "sky" colors wrap correctly around the surface.
- Computes a reflection vector
R = 2(N·V)N − Vfor each pixel, samples the environment at that reflected direction, and applies a Fresnel-style falloff (more reflection at grazing angles) - that's the specular.
Both passes come out separately: diffuse_lighting, specular_lighting, and the combined relit_image. Because the environment drives both, rotating the scene is as simple as dragging environment_rotation (−180 to 180) instead of re-aiming a light.
The inputs that actually matter
- image and normal_map - the flat albedo/color and the normals.
- environment_map - your HDRI. Equirectangular (2:1) images work directly; anything else gets sampled based on its own dimensions, so a 1:1 or non-equirect image will look wrong.
- diffuse_intensity / specular_intensity (0-10) - how much of each pass.
- specular_power (1-128) - and here's a fun quirk: the tooltip calls it "blur amount - lower = blurrier." Lower values give broad, soft reflections (rough-looking), higher values give sharp, mirror-like ones. That's the roughness knob in disguise.
- ambient_light (0-1) - a brightness floor so shadows don't crush to black.
Installing it
Part of TextureAlchemy. ComfyUI Manager (search "TextureAlchemy"), or:
cd ComfyUI/custom_nodes
git clone https://github.com/amtarr/ComfyUI-TextureAlchemy
Restart, and find it under Texture Alchemist → Lighting. No model downloads, no pip dependencies - you're just feeding images in.
Gotchas
Honest caveats, because this is a simplified IBL: the specular is a softened sample rather than a true prefiltered mipmap, so don't expect renderer-grade roughness response. And if your relit result looks flat, your normal map is probably weak - IBL is brutally honest about that. The biggest practical trap is feeding it a normal map in the wrong format; a DirectX map decoded as OpenGL (or vice versa) makes reflections sit in the wrong places. When in doubt, check normal_format before you start blaming the HDRI. If you want roughness and metallic maps wired in for a proper PBR pass, that's the IBL + PBR sibling node's job.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| normal_map | IMAGE | — | |
| environment_map | IMAGE | — | |
| environment_rotation | FLOAT | 0-180–180 | Rotate environment map (degrees) |
| diffuse_intensity | FLOAT | 1.00–10 | Diffuse lighting intensity |
| specular_intensity | FLOAT | 1.00–10 | Specular reflection intensity |
| specular_power | FLOAT | 321–128 | Specular power (blur amount - lower = blurrier) |
| ambient_light | FLOAT | 0.100–1 | Ambient light level |
| normal_format | COMBO | OpenGL | Normal map format |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| relit_image | IMAGE | — |
| diffuse_lighting | IMAGE | — |
| specular_lighting | IMAGE | — |