accumulateLights function

Vector3 accumulateLights(
  1. Surface s,
  2. ShaderBindings b,
  3. FragmentContext c, {
  4. required Vector3 shade(
    1. Surface s,
    2. LightSample light
    ),
  5. required bool shadowed,
})

AccumulateLights, with the model's own response passed in.

The GLSL achieves this with two function prototypes each shader defines; here it is a callback, which is the same shape and one fewer file.

Implementation

Vector3 accumulateLights(
  Surface s,
  ShaderBindings b,
  FragmentContext c, {
  required Vector3 Function(Surface s, LightSample light) shade,
  required bool shadowed,
}) {
  var total = Vector3.zero();
  final count = lightCount(b, s.world);
  for (var i = 0; i < count; i++) {
    final light = sampleLight(b, i, s);
    if (light == null) continue;
    // `LightHasShadow` — `gfx-74n`. Only the first eight carry one: the atlas
    // has six rows and the slot table eight entries, so a light from the list
    // has no row to read and asking would index past the table.
    var visibility = 1.0;
    shadowTransmittance.setValues(1.0, 1.0, 1.0);
    if (i < kMaxLights) {
      visibility = shadowed ? shadowFactor(s, b, i, light.nDotL, c) : 1.0;
      visibility *= pointShadowFactor(b, s.world, s.normal, i, c);
    }
    if (visibility <= 0.0) continue;
    final response = shade(s, light);
    total += Vector3(
      response.x * light.radiance.x * shadowTransmittance.x,
      response.y * light.radiance.y * shadowTransmittance.y,
      response.z * light.radiance.z * shadowTransmittance.z,
    )..scale(light.nDotL * visibility);
  }
  return total;
}