run method
Returns linear RGBA for attachment zero. Null discards the fragment.
Implementation
@override
Vector4? run(Float32List v, ShaderBindings b, FragmentContext c) {
final picture = b.textures['reflection_texture'];
if (picture == null) return Vector4.zero();
final view = b.vec4('PlanarReflectionInfo', 'view', Vector4(0, 0, 1, 1));
final params = b.vec4('PlanarReflectionInfo', 'params', Vector4.zero());
final tint = b.vec4('PlanarReflectionInfo', 'tint', Vector4.zero());
// `FragCoordFromTop`, and the texture's rows turned back where its first
// row is the bottom of the picture.
final rows = params.z;
final pixelY = rows > 0.0 ? rows - c.coord.y : c.coord.y;
final u = (c.coord.x - view.x) / view.z;
final top = (pixelY - view.y) / view.w;
final texel = picture.sample(u, rows > 0.0 ? 1.0 - top : top);
final reflected = Vector3(
texel.x * tint.x,
texel.y * tint.y,
texel.z * tint.z,
);
final normal = Vector3(v[kVNormal], v[kVNormal + 1], v[kVNormal + 2])
..normalize();
if (!c.frontFacing) normal.negate();
final toEye = towardsEye(v, b);
final cosine = normal.dot(toEye).clamp(0.0, 1.0);
final f0 = params.x;
final grazing = 1.0 - cosine;
final grazing2 = grazing * grazing;
final fresnel = f0 + (1.0 - f0) * grazing2 * grazing2 * grazing;
final alpha = (fresnel * params.y).clamp(0.0, 1.0);
final fogged = applyFog(reflected, v, b);
return Vector4(fogged.x * alpha, fogged.y * alpha, fogged.z * alpha, alpha);
}