Nodes/ComfyUI_yanc/๐Ÿ˜ผ> Normal Map Lighting
ComfyUI Node

๐Ÿ˜ผ> Normal Map Lighting

Relight an image with a virtual light source, game-engine style

By ALatentPlaceยทCreated 2 years agoยทUpdated 2 years agoยท 80
๐Ÿ˜ผ> Normal Map Lighting
  • diffuse_map
  • normal_map
  • specular_map
  • mask
  • IMAGE
โ—„light_yaw45โ–บ
โ—„light_pitch30โ–บ
โ—„specular_power32โ–บ
โ—„ambient_light0.50โ–บ
โ—„NormalDiffuseStrength1.00โ–บ
โ—„SpecularHighlightsStrength1.00โ–บ
โ—„TotalGain1.00โ–บ
โ—„color16777215โ–บ

This node borrows a technique straight out of 3D rendering and game engines: if you have a diffuse (base color) map, a normal map (surface direction per pixel), and a specular map (shininess/reflectivity per pixel), you can compute how light would actually fall across that surface from any direction - without re-rendering anything, just math on the three maps. Normal Map Lighting brings that into ComfyUI, letting you reposition a virtual light source over an image and get a relit result back.

It's the odd one out in the pack in that it assumes you already have (or can generate) a proper normal and specular map for your subject - this isn't extracting lighting info from a flat photo, it's compositing lighting from maps you supply. The author has a dedicated YouTube tutorial on the workflow, which is the better starting point than the node's parameter list alone if you're new to normal-map-based relighting.

How it works

For every pixel, it reads the surface direction from normal_map and the shininess from specular_map, then computes standard diffuse + specular lighting math against a virtual light positioned by light_yaw and light_pitch, applied on top of the base colors in diffuse_map. ambient_light adds a flat fill so surfaces facing away from the light aren't pure black. The various strength/gain parameters let you push the diffuse response, the specular highlights, and the overall output brightness independently.

The inputs and outputs that matter

  • diffuse_map, normal_map, specular_map (all IMAGE, required) - the three source maps. All three are required; there's no fallback for a missing one.
  • light_yaw (default 45, โˆ’180 to 180) and light_pitch (default 30, โˆ’90 to 90) - the light's horizontal and vertical position. These are the two you'll actually move around to "aim" the light.
  • specular_power (default 32, 1โ€“200) - controls how tight and bright the specular highlight is; higher values give a smaller, sharper glint (like a glossier surface), lower values spread it out softer.
  • ambient_light (default 0.5, 0โ€“1) - the fill light level for surfaces not facing the main light.
  • NormalDiffuseStrength, SpecularHighlightsStrength, TotalGain - independent intensity multipliers for the diffuse response, the specular highlights, and the overall result respectively. Useful for toning down an overly dramatic effect without changing the light's position.
  • color (default 16777215, i.e. pure white as a packed RGB integer) - the light's color.
  • mask (optional) - restrict the relighting effect to a masked region rather than the whole image.

One output: IMAGE, the relit result.

How to install it

Via ComfyUI Manager: search "yanc" or "YANC - Yet Another Node Collection," install, restart. Manually:

cd ComfyUI/custom_nodes
git clone https://github.com/ALatentPlace/ComfyUI_yanc

then restart. No extra dependencies to install - it's image-tensor math, though getting good input maps in the first place may involve a separate normal-map estimation node elsewhere in your graph if you're not sourcing them from a 3D asset.

Common issues & troubleshooting

Result looks flat or has no visible highlight. Check specular_map actually has meaningful variation - a flat gray or black specular map gives the node nothing to compute a highlight from, so a shiny-looking result depends on that map having real shininess data, not just being any IMAGE-shaped placeholder.

Everything looks blown out or the highlight is a huge white blob. Try lowering TotalGain or SpecularHighlightsStrength first before touching light position - a too-high gain or a very low specular_power (a wide, soft highlight) both commonly read as "overexposed."

Not sure where to get a normal map from a regular photo. This node consumes normal maps, it doesn't generate them - if you're starting from a flat photo rather than 3D-rendered maps, you'll need a normal-map estimation node/model earlier in the graph to produce one first; this node's job starts after that.

CategoryYANC/๐Ÿ˜ผ Image

Inputs (12)

NameTypeDefaultDescription
diffuse_mapIMAGEโ€”
normal_mapIMAGEโ€”
specular_mapIMAGEโ€”
light_yawFLOAT45-180โ€“180โ€”
light_pitchFLOAT30-90โ€“90โ€”
specular_powerFLOAT321โ€“200โ€”
ambient_lightFLOAT0.500โ€“1โ€”
NormalDiffuseStrengthFLOAT1.000โ€“5โ€”
SpecularHighlightsStrengthFLOAT1.000โ€“5โ€”
TotalGainFLOAT1.000โ€“2โ€”
colorINT167772150โ€“16777215โ€”
maskoptMASKโ€”

Outputs (1)

NameTypeDescription
IMAGEIMAGEโ€”