Nodes/ComfyUI-DiLightNet/Prepare DiLight CN Image
ComfyUI Node

Prepare DiLight CN Image

Six images in, one 16-channel lighting condition out

By logtd·Created 2 years ago·Updated 2 years ago· 11
Prepare DiLight CN Image
  • mask
  • image
  • diff_image
  • ggx5
  • ggx13
  • ggx34
  • IMAGE

What this actually is

This is the "build the input" node for the DiLightNet controlnet in the same pack. DiLightNet is a SIGGRAPH 2024 method that conditions an SD 2.1 generator on the lighting itself: instead of describing light with words, you feed it rendered shading images and it reproduces that lighting on your subject. Prepare DiLight CN Image is the glue that packs six images into the single 16-channel tensor that lighting-conditioned controlnet reads. Its sibling, Load DiLight ControlNet, loads the weights; this node builds the hint those weights were trained on.

What each input is for

All six are required, and they form a strict recipe:

  • image - your reference or provisional image: the object you want to relight.
  • mask - the foreground mask, 1 where the subject is, 0 elsewhere.
  • diff_image - a diffuse shading pass of the object under your target lighting.
  • ggx5, ggx13, ggx34 - GGX specular renderings at roughness values 0.05, 0.13 and 0.34.

Those last four are the "radiance hints" and they're the heart of the method. They're renderings of the object's geometry under the lighting environment you want to impose - the model literally reads the light off them. The roughness ladder (shiny to rough) is what lets it disentangle specular highlights from diffuse color. The example workflow names them futuristic_soldier_diffuse.png, futuristic_soldier_ggx0.05.png and so on, so you can see the mapping.

How the packing works

The node does two concatenations and a multiply, nothing more:

cond = cat([diff_image, ggx5, ggx13, ggx34]) * mask   # 12 shading channels, background zeroed
out  = cat([mask, image, cond])                        # 16 channels total

The mask multiplies into the radiance hints so the background contributes no lighting signal, then rides along as its own channel so the network knows where the subject actually is. The result is a 16-channel IMAGE tensor whose order - mask, RGB reference, 12 shading channels - matches exactly what the pack's loader and the upstream DiLightNet model expect.

Wiring it

Single output, IMAGE, and it goes into the image slot of ControlNetApplyAdvanced - not into a Load Image or an encode. The same node's control_net slot gets the CONTROL_NET from Load DiLight ControlNet, and its outputs feed KSampler. The example workflow builds the mask by routing a LoadImage's alpha output through InvertMask, so if you load an image with transparency you get the mask for free.

The parts that will trip you up

  • The hints aren't generated by this pack. The README is explicit: the Blender/Dust3r code that renders diffuse and GGX passes isn't included, "expect those to be added to separate repos when time allows." You need a renderer (Blender, via the upstream DiLightNet repo's scripts, or your own) to produce them. That's the steep part of the whole stack - this node itself is trivial.
  • Everything must be 512×512 and pixel-aligned. The hints get multiplied against the mask and concatenated channel-wise; if one is a different size, a different crop, or not rendered from the same camera as the reference image, the condition is garbage. You'll get artifacts, not an error. The example pairs a 512×512 EmptyLatentImage with 512×512 inputs, so keep it all at 512.
  • The mask is expected 1-for-foreground. Feed it a 1-for-background mask and the network sees an inverted subject. The InvertMask node in the example exists precisely for this.
  • Zero widgets. There are no settings here, so if output looks wrong it's almost always the inputs: misaligned hints, inverted mask polarity, or a value that wandered outside the 0–1 range. All of those present as "bad lighting" rather than a clear error.

It's a fiddly little node in a genuinely obscure pipeline, and it only makes sense as the front half of Load DiLight ControlNet. But it's also the honest version of a "pipeline node": dead simple, well named, and the difficulty lives exactly where it should - in getting the renders right before the node ever sees them.

Categorydilight

Inputs (6)

NameTypeDefaultDescription
maskMASK
imageIMAGE
diff_imageIMAGE
ggx5IMAGE
ggx13IMAGE
ggx34IMAGE

Outputs (1)

NameTypeDescription
IMAGEIMAGE