1 The idea
At the robot’s edge, depth jumps from near to far. Defocus the merged image once and either the robot’s colour smears into the blurred background, or a blurred robot keeps a sharp outline.
Set the focus to “on the robot” and compare our result (left) with Blender’s real depth of field (right). Try both methods (“one defocus after the merge” and “defocus each layer, then merge”) and read each one’s edge-band error. The robot’s depth pass is premultiplied like its colour, so an edge pixel at alpha 0.5 holds about 4.8 m while the robot is at about 9.0 m: a distance where nothing exists. The “depth (merged)” view (brighter = nearer) shows the merged edge mixing the robot’s depth with the ground behind it.
CoC ∝ |z − focus| / z2 Hands-on
Task: with the focus on the robot, bring the edge-band error below the goal in the panel (halfway between the two methods).
Compare the shoulders and head, left and right. Ask which image really holds the background behind the robot, and which layers need blurring at all.
3 Deep dive
Why the edge breaks
In the depth pass, the robot’s edge is where values jump from near to far. An anti-aliased edge pixel averages near and far depth, giving a distance where nothing actually exists.
The merged image adds a second problem: there is no background behind the robot to blur, so the defocus uses robot colour instead. In the figure, compare the two readouts: the single pass stays clearly above the per-layer method.
| Focus | Symptom with one defocus | Cause |
|---|---|---|
| On the robot | Robot-coloured halo around a sharp robot | The blurred background gathers robot colour |
| Far | Sharp outline on a blurred robot | Edge pixels carry the background’s depth |
Split the layers, then merge
Defocus the plate with its own depth and the robot with its own depth pass, then merge back to front. The plate holds the real background behind the robot, so its blur never pulls in robot colour.
A layer that sits in the focal plane needs no defocus at all: keep it sharp and merge it on top. With several layers, link the focus and blur size of every ZDefocus to one master.
- 1Plate + set depth
- 2Defocus plate
- 3Robot + depth pass
- 4Defocus if out of focus
- 5Robot over plate
Know the depth encoding first
Renderers such as Cycles write depth as distance from the camera in scene units, with inf or a very large value in empty areas. Nuke’s ScanlineRender and DepthGenerator write 1/Z by default: high when near, falling toward 0 far away.
ZDefocus2’s math knob must match the encoding; the wrong choice gives no defocus or garbled defocus. Sample near and far values first to see whether they rise or fall with distance. To turn 1/Z into distance, use an Expression of 1/z.
| Depth | Near / far | ZDefocus2 math |
|---|---|---|
| Camera distance (Cycles and most renderers) | Small / large | depth |
| 1/Z (ScanlineRender, DepthGenerator) | Large / toward 0 | far=0 (Nuke’s default) |
Depth must be solid to the edge
Depth is data, not a picture. Never premult it, because it falls to 0 at the edges and breaks the defocus, and never grade it to look nice: the defocus needs the real distances.
If a blurred robot still shows a sharp outline, make the near depth win at the edge: EdgeExtend on the depth, or a Dilate with a negative size (a min filter) before ZDefocus for distance depth; use a positive size for 1/Z. fill_foreground guesses the area revealed when a foreground blurs, but the real image behind is unknown, so it stays a guess.
Switch the output to focal plane setup and check the focus band before you judge any blur.
4 In real work
In Nuke
- Shuffle2 each layer’s depth into depth.Z, then sample near and far values.
- EdgeExtend (channels Z) or Dilate on the depth so it is solid to the edge with near winning; Copy it back into depth.Z if you built it separately.
- ZDefocus2 on the plate, with math set to the encoding (depth for metres, far=0 for 1/Z), checked with focal plane setup.
- ZDefocus2 on the premultiplied robot only when it is out of focus, with focus and size linked to the master.
- Merge (over) with the robot as A over the defocused plate as B, back to front.
Common mistakes → the fix
- ✗ Defocusing the merged comp with one depth✓ Defocus each layer on its own, then merge back to front.
- ✗ Using premultiplied or anti-aliased depth at the edges✓ Make the depth solid to the edge with EdgeExtend or a Dilate where near wins.
- ✗ A math setting that doesn’t match the depth encoding✓ Sample near and far values first, then pick depth or far=0 to match.
- ✗ Grading the depth so it looks nice in the viewer✓ Keep the depth raw; adjust the viewer if you need to see it.
- ✗ Running a layer in the focal plane through a defocus✓ Keep it sharp and merge it over the blurred layers.
Before you send the shot
- Every ZDefocus math setting matches its depth encoding.
- Depth is not premultiplied, not graded, and solid to the rgb edge.
- With gain pushed at the edge, no robot-coloured halo sits in the blurred background.
- A blurred robot shows no leftover sharp outline.
- The focal plane setup sits on the subject on every frame.
- Layers merge back to front, with focus linked to one master.
5 Check yourself
Focus is on the robot, but you defocus the merged image once. Why does an orange halo of robot colour appear around the sharp robot?
🔒 Answer the question above and this card opens
1. The robot’s Cycles depth is camera distance in metres. How do you set ZDefocus2’s math?
2. Focus is far. The robot blurs, but a sharp outline runs around it. What’s the fix?
Where the numbers come from: Every picture is a Blender 5.2 Cycles render of our own desert and robot (tools/assets/passes_cg.py), computed in your browser. The reference is a render of the plate and robot with Blender’s camera depth of field, at an aperture far wider than a real 35 mm lens (f/0.18) so the blur shows at 640 px (a teaching aperture). Our defocus is a teaching disc scatter sized by each pixel’s depth, not Nuke’s ZDefocus2, so neither method reaches zero; the score is the mean error in a band around the robot’s edge, and the goal is measured live when the images load.