Draw order and depth state
Open the live demo · Read the source · View on GitHub
Most scenes never need to say in which order things are drawn: the renderer sorts them. Sometimes you need to overrule it, for a backdrop, a sky or an overlay. A material has three fields for that, and the render settings have a switch for triangle backs. This page puts an orange panel in front of a blue box and lets you decide whether the box shows through.
Step 1: A box to hide #
The box is an ordinary material. It draws in bucket zero, writes depth and passes the normal depth test, like nearly everything else.
_box = MeshNode(
DeviceMesh.upload(
context.device,
CuboidShape(size: Vector3(1.6, 1.6, 1.6)).build(),
),
Material(name: 'box', baseColor: Vector4(0.25, 0.45, 0.85, 1.0)),
name: 'box',
);
Step 2: A panel that steps aside #
The panel is nearer to the camera than the box, so by default it would hide it.
Three fields change that. drawBucket outranks every other sort term, so a
negative bucket draws before the rest of the scene. depthWrite: false makes it
leave the depth buffer alone. depthCompare replaces the depth test, and always
means the panel does not depend on what is already there.
_panel = Material(
name: 'panel',
baseColor: Vector4(0.95, 0.55, 0.2, 1.0),
drawBucket: bucket,
depthWrite: panelWritesDepth ? null : false,
depthCompare: testChoice == 0 ? null : CompareFunction.always,
);
final MeshNode panel =
MeshNode(
DeviceMesh.upload(
context.device,
const PlaneShape(width: 5, depth: 3).build(),
),
_panel,
name: 'panel',
)
..setRotation(upright)
..setPosition(0.0, 0.0, 1.8);
With bucket -1 and no depth writing, the box draws after the panel and finds an empty depth buffer, so it appears in front of a panel that is physically nearer. That is how a sky dome or a backdrop works.
Step 3: Take it apart #
Turn Panel writes depth on. The panel now records how near it is, the box fails
the test against it, and the box disappears. Move Panel draw bucket to 0 and the
renderer's own sorting takes over again. Change Panel depth test to always and the
panel is drawn over anything drawn before it.
_panel
..drawBucket = bucket
..depthWrite = panelWritesDepth ? null : false
..depthCompare = testChoice == 0 ? null : CompareFunction.always;
Step 4: The backs of triangles #
The small red square to the left is turned away from the camera. A triangle seen
from behind is normally skipped, which is what backfaceCulling does, and it is
why the square is invisible. Turn Back-face culling off and it appears.
final MeshNode flag =
MeshNode(
DeviceMesh.upload(
context.device,
const PlaneShape(width: 1.4, depth: 1.4).build(),
),
Material(name: 'flag', baseColor: Vector4(0.8, 0.15, 0.2, 1.0)),
name: 'flag',
)
..setRotation(
Quaternion.axisAngle(Vector3(1.0, 0.0, 0.0), -math.pi / 2),
)
..setPosition(-2.9, 0.0, -0.5);
The switch is a render setting, read every frame:
backfaceCulling: cullBackFaces,
Note. A material with
doubleSided: trueis never culled, whatever the setting says. Use it for thin things such as leaves and cloth.