run method

  1. @override
Vector4? run(
  1. Float32List v,
  2. ShaderBindings b,
  3. FragmentContext c
)
override

Returns linear RGBA for attachment zero. Null discards the fragment.

Implementation

@override
Vector4? run(Float32List v, ShaderBindings b, FragmentContext c) {
  final capture = b.textures['capture_texture'];
  // No cube bound: black rather than a crash, and black rather than a
  // plausible reflection, which is what makes it reportable.
  if (capture == null) return Vector4(0.0, 0.0, 0.0, 1.0);

  final params = b.vec4('ProbeInfo', 'params', Vector4.zero());
  final face = (params.x + 0.5).floor();
  final roughness = params.y;
  final lod = params.z;
  final samples = (params.w + 0.5).floor().clamp(1, 128);

  final axis = probeFaceDirection(face, v[0], v[1]);

  if (samples == 1) {
    final texel = capture.sampleCube(axis.x, axis.y, axis.z, lod);
    return Vector4(texel.x, texel.y, texel.z, 1.0);
  }

  final alpha = math.max(roughness * roughness, 1e-3);
  final alpha2 = alpha * alpha;

  final up = axis.z.abs() < 0.999
      ? Vector3(0.0, 0.0, 1.0)
      : Vector3(1.0, 0.0, 0.0);
  final right = up.cross(axis)..normalize();
  final ahead = axis.cross(right);

  final golden = math.pi * (3.0 - math.sqrt(5.0));
  var r = 0.0;
  var g = 0.0;
  var bl = 0.0;
  var weight = 0.0;
  for (var i = 0; i < samples; i++) {
    final e = (i + 0.5) / samples;
    final theta = golden * i;
    // A GGX half vector about the axis and the axis reflected about it;
    // its sine is the rest of the unit length and nothing smaller.
    final cosH = math.sqrt((1.0 - e) / (1.0 + (alpha2 - 1.0) * e));
    final sinH = math.sqrt(math.max(1.0 - cosH * cosH, 0.0));
    final tap = Vector3(
      2.0 * cosH * sinH * math.cos(theta),
      2.0 * cosH * sinH * math.sin(theta),
      2.0 * cosH * cosH - 1.0,
    );
    final dir = right * tap.x + ahead * tap.y + axis * tap.z;
    final cosine = dir.dot(axis);
    if (cosine <= 0.0) continue;
    final texel = capture.sampleCube(dir.x, dir.y, dir.z, lod);
    r += texel.x * cosine;
    g += texel.y * cosine;
    bl += texel.z * cosine;
    weight += cosine;
  }

  final scale = weight > 0.0 ? 1.0 / weight : 0.0;
  return Vector4(r * scale, g * scale, bl * scale, 1.0);
}