Nodes/ComfyUI LC123 Nodes/LC Lighting Control πŸ”¦
ComfyUI Node

LC Lighting Control πŸ”¦

Relight a finished image without regenerating a single pixel

By lonecatone23Β·Created 2 months agoΒ·Updated 3 days agoΒ· 18
LC Lighting Control πŸ”¦
  • image
  • normal_map
  • depth_map
  • mask
  • image
  • debug_mask
β—„ambient_light0.25β–Ί
β—„depth_scale0.50β–Ί
β—„light1_x0.00β–Ί
β—„light1_y0.50β–Ί
β—„light1_z0.90β–Ί
β—„light1_intensity1.00β–Ί
β—„light1_size1.00β–Ί
β—„enable_light_2falseβ–Ί
β—„light2_x-1.00β–Ί
β—„light2_y0.00β–Ί
β—„light2_z0.50β–Ί
β—„light2_intensity1.00β–Ί
β—„light2_size0.50β–Ί
β—„cast_shadowstrueβ–Ί
β—„shadow_strength0.50β–Ί
β—„shadow_softness0.30β–Ί
β—„mask_enabledtrueβ–Ί
β—„mask_blend0.45β–Ί

The scenario: you generated a portrait, and the light is boring. Flat, even, no drama. Your options used to be IC-Light (a whole diffusion model, a re-generation, a seed lottery) or a color overlay that looks painted-on. LC Lighting Control is the third way: a deterministic post-process that relights the pixels you already have using a normal map and a depth map. Same image, new shadows, zero re-generation.

It's the headline node of the LC123 pack, and it deserves the spot. You feed it your image, a normal map, a depth map (and optionally a subject mask), then aim up to two lights and watch the on-node preview. The math is plain 3D lighting: each pixel is multiplied by how much light hits it based on its surface normal, with depth deciding how far into the beam it falls and an ambient floor keeping shadows from crushing to black. No diffusion, no color tint - it's a render, not a filter.

The inputs that matter

The three images come first, and the maps are the difference between "looks right" and "looks inside-out":

  • image - the photo or render to relight.
  • normal_map - surface facing. The README recommends BAE or DSINE output.
  • depth_map - near vs far; Depth Anything V2 is the suggestion. If bright = near and the lighting looks inverted, invert the depth.
  • mask (optional) - a subject matte, only used when mask_enabled is on.

Then the light rig: each light has X (+X = from the right of frame), Y (+Y = from above), Z (0 = side plane, 1 = front), intensity (0 = that light is off), and size (cone width: small = spot, large = flood). ambient_light is the shadow floor, depth_scale controls how far pixels fall off inside the beam. cast_shadows, shadow_strength, and shadow_softness handle the screen-space shadow march.

Where people get burned

  • Grey fringe around the subject. That's mask_blend too high - the README suggests ~0.2–0.45 or just leave the mask off. High blend is the classic first-mistake.
  • Depth backwards. Bright-as-near makes the light look like it's coming from behind. Flip the depth.
  • Normal maps with inverted X. The node accounts for DirectX-style normal maps (it negates X in the NΒ·L term), but if your preprocessor stores things oddly, compare against a known-good map.

The light stage under the sliders is worth knowing: white orb is light 1, red is light 2; drag for XY, shift+drag or wheel for Z. There's an example workflow at workflows/LC Lighting Control (BETA).json in the repo.

Install

Part of LC123 - ComfyUI Manager (search "LC123") or:

cd ComfyUI/custom_nodes
git clone https://github.com/lonecatone23/ComfyUI_LC123_nodes

Restart after. The pack itself needs no extra packages, but the maps are another story: Depth Anything V2, a normal-map preprocessor (BAE/DSINE), and optionally remBG for the mask are all separate installs, per the README. Cloud-hosted ComfyUI won't have LC123 until requested, so plan for local or a pack request.

Outputs are image (the relit frame) and debug_mask (ignore it - it's for troubleshooting the subject mask). Start at intensity ~1.0–1.3, ambient ~0.25–0.4, shadow strength ~0.4, and you're in the ballpark.

CategoryLC123/image

Inputs (22)

NameTypeDefaultDescription
imageIMAGEβ€”
normal_mapIMAGENormal map (RGB). Same subject framing as the image.
depth_mapIMAGEDepth map. Same subject framing as the image.
ambient_lightFLOAT0.250–1.5Shadow floor. 0 = pure key.
depth_scaleFLOAT0.500–2Far pixels fall off inside the beam (0 = no depth falloff).
light1_xFLOAT0.00-1–1+X = from the right of the frame.
light1_yFLOAT0.50-1–1+Y = from above.
light1_zFLOAT0.900–10 = side plane, 1 = front. Negative is ignored.
light1_intensityFLOAT1.000–4β€”
light1_sizeFLOAT1.000.05–1.5Cone width: small = spot, large = flood.
enable_light_2BOOLEANfalseβ€”
light2_xFLOAT-1.00-1–1β€”
light2_yFLOAT0.00-1–1β€”
light2_zFLOAT0.500–1β€”
light2_intensityFLOAT1.000–4β€”
light2_sizeFLOAT0.500.05–1.5β€”
cast_shadowsBOOLEANtrueβ€”
shadow_strengthFLOAT0.500–1β€”
shadow_softnessFLOAT0.300–1β€”
mask_enabledBOOLEANtrueβ€”
mask_blendFLOAT0.450–1β€”
maskoptMASKOptional subject mask. Enables virtual-dome normals when mask_enabled.

Outputs (2)

NameTypeDescription
imageIMAGEβ€”
debug_maskMASKβ€”