Nodes/ComfyUI-TextureAlchemy/Normal Map Relighter
ComfyUI Node

Normal Map Relighter

Relight Any Texture With a Normal Map and Zero Diffusion Models

By amtarr·Created 9 months ago·Updated 4 months ago· 58
Normal Map Relighter
  • image
  • normal_map
  • relit_image
  • lighting_mask
light_x0.00
light_y0.00
light_z1.00
light_intensity1.0
ambient_light0.20
normal_formatOpenGL

Everyone who discovers IC-Light and its cousins wants to relight their images with a model. But sometimes you don't need a diffusion model at all - you need the deterministic version: point a virtual light at a normal map and let the math do the shading. That's exactly what Normal Map Relighter does, and it's instant, free, and perfectly reproducible.

The trick it exploits is that a normal map already encodes the surface direction of every pixel. If you multiply a flat albedo by how "facing" each pixel is toward your light, you get a convincingly lit surface with zero sampling. It's the same trick Blender's Normal node shader uses - the docstring straight up says so.

How it works

The node decodes your normal map from color space (0-1) into vector space (-1/1), flips the green channel if you tell it the map is DirectX format, normalizes the vectors, then computes the dot product between each surface normal and your light direction. Front-facing pixels get lit, side-facing pixels fall off, back-facing pixels clamp to your ambient floor.

Two details worth knowing:

  • The light_x/y/z inputs form a 3D direction vector that gets normalized internally, so you don't have to keep it unit length - (0, 0, 1) means "light coming straight at the camera," which is the default and a fine starting point.
  • ambient_light (0-1) is the minimum brightness so nothing goes fully black. Bump it to 0.3-0.4 if you want a softer, less contrasty look.

The output relit_image is your albedo multiplied by that lighting, and lighting_mask is the grayscale shading itself - which is genuinely useful to grab on its own, since it's a ready-made light mask you can reuse elsewhere in the graph.

Inputs worth your time

  • normal_format - OpenGL or DirectX. This is the one that bites people. Pick the wrong one and everything looks inverted, like a reversed bump.
  • light_x / light_y / light_z - the light direction. Start from (0,0,1) and nudge X for left/right and Y for up/down.
  • light_intensity (0-10) and ambient_light (0-1) - brightness and floor.

Why you'd actually use this

This node shines when you want to preview what a normal map will do before you commit to a render, or when you want to bake a lighting pass into an asset without leaving ComfyUI. It's also a great debug tool: flat-looking output usually means your normal map is weak or wrong, and this node will tell you immediately. If your normal map decodes cleanly, the lighting looks like a proper shaded surface; if it looks like noise, the map is broken.

Installing it

It's part of TextureAlchemy. Either ComfyUI Manager (search "TextureAlchemy") or:

cd ComfyUI/custom_nodes
git clone https://github.com/amtarr/ComfyUI-TextureAlchemy

Restart, and you'll find the node under Texture Alchemist → Lighting. No extra dependencies, no model files - pure PyTorch math on the two images you plug in.

Gotchas

The big one is the DirectX/OpenGL flip mentioned above - if the shading looks "sunk in" rather than raised, flip normal_format. Also note this is a directional light only: no distance falloff, no specular. If you want highlights and light color, the Advanced sibling node adds those; if you want true HDRI-style environment lighting, the IBL version is the one. Think of this as the cheap and cheerful starter in the relight family.

CategoryTexture Alchemist/Lighting

Inputs (8)

NameTypeDefaultDescription
imageIMAGE
normal_mapIMAGE
light_xFLOAT0.00-1–1Light direction X component
light_yFLOAT0.00-1–1Light direction Y component
light_zFLOAT1.00-1–1Light direction Z component (pointing toward camera)
light_intensityFLOAT1.00–10Light intensity multiplier
ambient_lightFLOAT0.200–1Ambient light level (minimum brightness)
normal_formatCOMBOOpenGLNormal map format (affects Y channel)

Outputs (2)

NameTypeDescription
relit_imageIMAGE
lighting_maskIMAGE