Light channels
Open the live demo · Read the source · View on GitHub
LightNode.channels and SceneNode.lightChannels are two bit masks that
meet in the middle: a light reaches an object only when
light.channels & node.lightChannels is not zero. Both default to every bit
set, so a scene that has never heard of channels is lit exactly as it
always was. Nothing here is a check the shader makes at the last minute; a
light that cannot reach an object never takes one of its eight slots
either.
Step 1: Two balls, two channels #
The near ball is on channel 0, the far ball on channel 1, and a red
light is restricted to channel 0 as well. Only the near ball sees it; a
plain fill light keeps the far one visible.
// Channel 0 for the near ball and its own light, channel 1 for the far
// one: the mask a light and a node meet on decides whether it reaches it,
// never distance or anything the shader has to guess about.
_redBall = MeshNode(
sphere,
Material(baseColor: Vector4(0.8, 0.8, 0.8, 1.0)),
name: 'near ball',
)..lightChannels = LightChannels.only(0);
final MeshNode farBall =
MeshNode(
sphere,
Material(baseColor: Vector4(0.8, 0.8, 0.8, 1.0)),
name: 'far ball',
)
..setPosition(2.4, 0.0, 0.0)
..lightChannels = LightChannels.only(1);
_redLight = LightNode(name: 'red', type: LightType.point, intensity: 6.0)
..color.setValues(1.0, 0.25, 0.2)
..channels = LightChannels.only(0)
..setPosition(0.0, 1.6, 1.2);
Step 2: Open the second channel #
The toggle adds the far ball's channel to the near ball's mask, so the same red light that only reached the near ball now reaches both.
// With the toggle on, the near ball opens its second bit too and the
// light starts reaching it exactly as it always reached the far one.
_redBall.lightChannels = sameChannel
? (LightChannels.only(0) | LightChannels.only(1))
: LightChannels.only(0);
Step 3: What this page checks #
The claim is the mask itself: the red light's channel and the ball it is meant to light have to share a bit, and the light has to actually be restricted rather than left on every channel by accident.
if (!LightBuffer.reaches(_redLight, _redBall.lightChannels)) {
throw StateError('the red light does not reach the ball it lights');
}
if (_redLight.channels == LightChannels.all) {
throw StateError('the light was never restricted to a channel');
}
if (frame.drawCalls < 2) {
throw StateError('not every ball was drawn');
}
Tip.
LightChannels.only(n)is the nth bit, counting from zero.LightChannels.allandLightChannels.noneare the two values a caller does not have to invent themselves.