Search and browse the Moray docs

Shadow catcher

A shadow catcher is stand-in geometry for a surface in the live-action plate (a floor, a table, a wall). It is never in the final image itself. It gathers what the CG does to the plate: shadows, bounce light, reflections and the light of CG lamps. Moray writes those as raw passes for Nuke, following Arnold, RenderMan and V-Ray conventions, and with Film Transparent it also holds the catcher out of Combined, as Cycles does.

Set up

  1. Model the stand-in geometry and give it a material close to the plate surface (a rough diffuse for a floor, glossy or mirror for a reflective one).
  2. Object Properties > Moray > Shadow Catcher. An object already marked as a shadow catcher in Cycles is honoured too.
  3. Decide which lights are already in the plate. A light with Plate Light (Shadow Catcher) on (Object Properties > Moray on the light) is part of the plate: its shadows are what the passes measure. Blender ticks this on new lights. Untick it for CG-only lights, such as a lamp that exists only in CG; their light on the catcher goes to the CGLight pass instead of into the shadow. The World is always a plate light. Emissive meshes are always CG.
  4. Optional, next to Shadow Catcher (scene-wide):
    • Catch Indirect (on): CG bounce and reflections of CG objects go into the held-out Combined, as in Cycles.
    • Indirect Shadows (on): CG objects also block bounce light between catcher surfaces and what a glossy or mirror catcher reflects (RenderMan / V-Ray style). It costs a few percent on catcher pixels.
  5. Turn on Film Transparent if you want Combined held out (see below). Moray does not show Blender's Film panel yet: tick Render Properties > Film > Transparent while the scene is on Cycles or EEVEE, or run bpy.context.scene.render.film_transparent = True in the Python console.

The passes are written only for view layers that contain a catcher.

Passes

All passes are raw scene-linear, like every Moray AOV.

PassWhat it holdsUse
ShadowCatcherMaskCatcher coverageMatte for the catcher area
ShadowCatcherShadowShadow density per channel: mask * (1 - Lit / Unoccluded)Coloured shadow matte
ShadowCatcherLitPlate light on the catcher, with CG shadowsShadow ratio (numerator)
ShadowCatcherUnoccludedPlate light on the catcher, without CG shadowsShadow ratio (denominator)
...LitDiffuse / ...UnoccludedDiffuseDiffuse part of the pairGrade the ground shadow
...LitSpecular / ...UnoccludedSpecularGlossy part of the pairGrade the reflection occlusion
ShadowCatcherLit_<group> / Unoccluded_<group>One pair per light groupShadow per light
ShadowCatcherLitIndirect / UnoccludedIndirectPlate light bounced off another catcher surfaceBounce shadows (Indirect Shadows)
ShadowCatcherLitReflection / UnoccludedReflectionPlate seen in a glossy or mirror catcherReflection shadows (Indirect Shadows)
ShadowCatcherCGLightDirect light of CG lights and emitters on the catcherAdd over the plate
ShadowCatcherIndirectCGBounce light from CG surfaces onto the catcherAdd over the plate
ShadowCatcherReflectionCGReflections of CG objects in the catcherAdd over the plate
ShadowCatcherIndirectAll bounce light on the catcherReference
ShadowCatcherReflectionAll indirect reflection on the catcherReference
ShadowCatcherVisibility of plate light samplesSimple shadow matte
ShadowDiffUnoccluded - LitArnold-style subtractive shadow

With Indirect Shadows on, the Indirect and Reflection pairs are folded into the shadow ratio, ShadowCatcherShadow, ShadowDiff and the held-out alpha. The per-group pairs stay direct light only.

Light AOVs and light groups do not include the catcher's own shading: they add up to the held-out Combined minus the captured CG light.

Held-out Combined (Film Transparent)

With Film Transparent on, Combined is ready to go over the plate:

rgb   = rgb * (1 - mask) + CGLight [+ IndirectCG + ReflectionCG with Catch Indirect]
alpha = alpha * (1 - mask) + mask * (1 - luminance(Lit / Unoccluded))

It is premultiplied, and on catcher pixels alpha carries only the shadow (Arnold's convention). F12 and the renderer's EXR both get it, so farm renders match. With Film Transparent off, Combined is left alone and only the passes are written.

Nuke recipes

Over (Cycles / Arnold style)

Combined over plate

The catcher pixels carry the shadow in alpha and the CG light in RGB. To grade the shadow, use ShadowCatcherShadow, or rebuild alpha from Lit / Unoccluded.

Multiply (RenderMan style), with a held-out or a plain beauty

plate * (1 - mask + mask * Lit / Unoccluded) + CGLight + ReflectionCG + IndirectCG

then the CG beauty over it.

  • Divide per channel: Lit / Unoccluded keeps the shadow's colour (a blue sky fill under a yellow sun).
  • The CG passes are already weighted by coverage. Do not multiply them by the mask.
  • The held-out Combined already contains CGLight (and IndirectCG + ReflectionCG with Catch Indirect). If you merge it, add only the passes it lacks.
  • Grade the ground and its reflection separately with the Diffuse and Specular pairs, or each light with the _<group> pairs.

Subtract (Arnold shadow_diff)

plate - ShadowDiff * k

A quick, gradeable darkening. ShadowDiff can go slightly negative in noise; clamp if needed.

Holdouts

Holdouts cut CG out of the image without a catcher: Blender's object Holdout (Object Properties > Visibility > Mask), the collection Holdout toggle in the Outliner, and the Holdout shader node. A held-out object is transparent to the camera (alpha 0) but still casts shadows, bounces light and shows in reflections. Holdouts work on the CPU and the GPU.

Limits

  • Indirect Shadows covers one bounce off a catcher surface; light reaching the catcher by two or more catcher bounces is in neither half of the ratio.
  • Bounce shadows do not pass through partly transparent surfaces, volumes or subsurface objects; a holdout counts as CG there.