Nodes/radiance/Multipass Relight
ComfyUI Node

Multipass Relight

Move the key light in comp instead of re-rendering

By FXTD-Studios·Created 8 months ago·Updated about 18 hours ago· 246
Multipass Relight
  • albedo
  • normal_map
  • beauty
  • roughness
  • metallic
  • specular
  • ao
  • alpha
  • shadow_mask
  • depth_map
  • world_position
  • relit
  • diffuse_light
  • specular_light
  • lighting
  • alpha
  • relight_info
◄normal_conventionOpenGL (Y-Up)►
◄light_typeDirectional►
◄light_x-0.35►
◄light_y0.45►
◄light_z1.00►
◄light_r1.00►
◄light_g1.00►
◄light_b1.00►
◄intensity1.00►
◄ambient0.03►
◄specular_intensity1.00►
◄depth_scale10.0►
◄depth_near_is_whitetrue►
◄mix_with_beauty0.00►
◄output_premultipliedfalse►

Multipass Relight is the payoff node for anyone who went to the trouble of getting real passes. Give it albedo and normals and it lights the shot again from scratch - Lambert diffuse plus GGX specular - with one light you can place and colour. Change the key direction, warm the sun, kill the specular, all without touching the original render or the generator.

That's the classic comp move, and it only works if the passes mean something. Which is why this node is really a companion to Multipass Estimate (predicts albedo, normals, roughness from a plate with MoGe-2 and Marigold) or Read AOVs (actual renderer passes - always better if you have them). Don't feed it the retired Multipass Extract's outputs; those passes were filters pretending to be measurements, and relighting a fake albedo gives you a fake render.

How it actually works

albedo and normal_map are the only required inputs, and the normals are forgiving: 0..1-encoded maps are decoded to -1..1 automatically, signed passes are used as is, and normal_convention (OpenGL Y-Up or DirectX Y-Down) flips the green channel to match what you rendered. View direction is +Z toward the camera.

Everything else is optional and each one genuinely changes the maths: roughness (defaults to 0.5 when absent, clamped to a 0.045 minimum so you never get a perfect mirror), metallic (0 = dielectric, 1 = metal, where albedo tints the reflection and diffuse drops out), specular (scales the 4% dielectric reflectance, so 1 = 4%), ao (multiplies the ambient term), and shadow_mask (blocks direct light only - ambient survives, which is correct and a thing people forget). alpha passes through and drives output_premultiplied.

The light is two modes. Directional treats light_x/light_y/light_z as a direction with no falloff - your sun or a big soft box, and what you'll use most of the time. Point treats the same three numbers as a position with a soft 1 / (1 + 0.08·d²) falloff, and needs depth: connect world_position (camera-space metres, OpenGL axes) if you have it, otherwise depth_map plus depth_scale and depth_near_is_white to synthesise a Z. light_r/light_g/light_b tint both the direct light and the ambient; intensity scales direct light only; ambient is your flat fill and it's multiplied by AO.

Wire the six outputs where they belong. relit is the composite-ready result, scene-linear. diffuse_light and specular_light are the separated terms, so you can grade one without the other. lighting is the combined light pass. alpha is the matte. relight_info is a string report - worth reading when a result looks wrong, because it tells you what it actually used. mix_with_beauty blends back toward the original (needs beauty connected), which is how you sneak a 30% relight past a director.

Install

Ships with Radiance. In ComfyUI Manager search Radiance, install, restart, refresh. Manual route:

cd ComfyUI/custom_nodes
git clone https://github.com/fxtd-studios/radiance.git
cd radiance
python -m pip install -r requirements.txt

No models of its own - it's maths over the tensors you hand it, so it's fast. The downloads belong to Multipass Estimate, which pulls MoGe-2 geometry and Marigold materials and lighting through diffusers.

Where people get burned

  • Estimating an albedo from a beauty and relighting it gives you double lighting, because the original light is baked into the albedo. If all you have is a beauty, you want a relight that estimates the split, not this node.
  • Normals that aren't normals. A ControlNet-style normal map from a preprocessor is not a true surface normalisation. It'll render something. It won't be right.
  • Point light with no depth falls back to synthetic screen-space Z, so your light moves when the camera does. Use directional unless you've connected real depth.
  • Double premultiply. If your albedo is already multiplied by alpha, unpremultiply upstream and leave output_premultiplied off, or you'll halo every edge.

Video is the honest caveat: if the passes came from Multipass Estimate they were predicted per frame, so a fixed seed limits flicker rather than removing it. Real AOVs don't have that problem.

CategoryFXTD STUDIOS/Radiance/VFX

Inputs (26)

NameTypeDefaultDescription
albedoIMAGEDiffuse albedo (base colour) pass, scene-linear with no lighting baked in. Also used as the specular colour where metallic is 1.
normal_mapIMAGENormal pass: 0..1 encoded values are decoded to -1..1 automatically, signed passes are used as is. View direction is +Z toward the camera.
beautyoptIMAGEOriginal beauty render, scene-linear. Only used when mix_with_beauty is above 0.
roughnessoptIMAGERoughness pass, first channel, 0 = mirror, 1 = fully rough (clamped to 0.045 minimum). Defaults to 0.5 when not connected.
metallicoptIMAGEMetalness pass, first channel, 0 = dielectric, 1 = metal (no diffuse, albedo tints the reflection). Defaults to 0 when not connected.
specularoptIMAGEDielectric specular level pass, 0..1, scaling the default 4% reflectance at normal incidence (1 = 4%). Defaults to 1 when not connected.
aooptIMAGEAmbient occlusion as renderers write it: 1 = open, 0 = fully occluded.
alphaoptIMAGECoverage matte, first channel, 0..1. Passed through to the alpha output and used for output_premultiplied; defaults to 1.
shadow_maskoptIMAGEShadow pass, first channel: 1 = fully shadowed, 0 = lit. Blocks direct diffuse and specular light but not ambient.
depth_mapoptIMAGENormalised 0..1 depth pass. Only used by a Point light when world_position is not connected, to give pixels a synthetic Z.
world_positionoptIMAGEPosition pass in camera space metres (OpenGL axes: X right, Y up, +Z toward the camera). Only used by a Point light, and overrides depth_map.
normal_conventionoptCOMBOOpenGL (Y-Up)Green channel convention of the normal pass. DirectX (Y-Down) flips the Y component before lighting.
light_typeoptCOMBODirectionalDirectional: light_x/y/z is a direction, no falloff. Point: light_x/y/z is a position, with soft distance falloff 1 / (1 + 0.08 d^2).
light_xoptFLOAT-0.35-10–10Light X (+ = screen right). Directional: component of the direction toward the light. Point: position in world_position units, or in screen units (X spans +/- aspect ratio) without it.
light_yoptFLOAT0.45-10–10Light Y (+ = up). Directional: component of the direction toward the light. Point: position in world_position units, or in screen units (Y spans -1..1) without it.
light_zoptFLOAT1.00-10–10Light Z (+ = toward the camera). Directional: component of the direction toward the light, so positive values light the front. Point: position along the camera axis.
light_roptFLOAT1.000–8Linear red multiplier of the light colour. Also tints the ambient term.
light_goptFLOAT1.000–8Linear green multiplier of the light colour. Also tints the ambient term.
light_boptFLOAT1.000–8Linear blue multiplier of the light colour. Also tints the ambient term.
intensityoptFLOAT1.000–20Linear multiplier on the direct light (diffuse and specular). Does not affect ambient.
ambientoptFLOAT0.030–4Flat fill light level, tinted by the light colour and multiplied by the AO pass. Applied to diffuse only.
specular_intensityoptFLOAT1.000–8Linear multiplier on the specular term only. 0 gives a purely diffuse relight.
depth_scaleoptFLOAT10.00.01–1000Z range, in screen units (image height = 2), that the 0..1 depth pass spans around mid grey. Only used for a Point light with depth_map and no world_position.
depth_near_is_whiteoptBOOLEANtrueSet on when near pixels are white in depth_map. Only used for a Point light with depth_map and no world_position.
mix_with_beautyoptFLOAT0.000–1Linear blend of the relit result toward the beauty input: 0 = relight only, 1 = original beauty. Needs beauty connected.
output_premultipliedoptBOOLEANfalseMultiply the relit output (and the beauty mix) by alpha. Off leaves it unpremultiplied.

Outputs (6)

NameTypeDescription
relitIMAGE—
diffuse_lightIMAGE—
specular_lightIMAGE—
lightingIMAGE—
alphaIMAGE—
relight_infoSTRING—