run method
Returns linear RGBA for attachment zero. Null discards the fragment.
Implementation
@override
Vector4? run(Float32List v, ShaderBindings b, FragmentContext c) {
final sceneTexture = b.textures['scene_texture'];
if (sceneTexture == null) return Vector4(0.0, 0.0, 0.0, 1.0);
final u = v[0];
final w = v[1];
final scene = sceneTexture.sample(u, w);
final look = b.vec4(_block, 'look', Vector4.zero());
final edges = b.vec4(_block, 'edges', Vector4.zero());
final line = b.vec4(_block, 'line', Vector4.zero());
final screen = b.vec4(_block, 'screen', Vector4.zero());
final surfaceTexture = screen.z > 0.5
? b.textures['surface_texture']
: null;
final maskTexture = screen.w > 0.5 ? b.textures['mask_texture'] : null;
final surface = surfaceTexture?.sample(u, w) ?? Vector4.zero();
final depth = surface.w;
final normal = depth > 0.0
? decodeSurfaceNormal(surface.x, surface.y)
: Vector3(0.0, 0.0, 1.0);
final bend = math.max(edges.x, 1e-4);
final turn = math.max(edges.y, 1e-4);
var color = Vector3(scene.x, scene.y, scene.z);
// Flatten: the mean of the taps on this pixel's surface.
final flatten = look.x.clamp(0.0, 1.0);
if (surfaceTexture != null && depth > 0.0 && flatten > 0.0) {
final spacing = math.max(edges.w, 1.0);
final sum = Vector3.zero();
var count = 0;
for (var y = -2; y <= 2; y++) {
for (var x = -2; x <= 2; x++) {
final tu = u + x * screen.x * spacing;
final tv = w + y * screen.y * spacing;
final tap = surfaceTexture.sample(tu, tv);
final ring = math.max(math.max(x.abs(), y.abs()), 1);
final same =
tap.w > 0.0 &&
(tap.w - depth).abs() <= bend * depth * ring &&
1.0 - decodeSurfaceNormal(tap.x, tap.y).dot(normal) < turn;
if (same) {
final texel = sceneTexture.sample(tu, tv);
sum.add(Vector3(texel.x, texel.y, texel.z));
count++;
}
}
}
if (count > 0) {
final mean = sum / count.toDouble();
color = color + (mean - color) * flatten;
}
}
// Tone: toward the luma, then apart about mid grey.
final luma = color.x * 0.2126 + color.y * 0.7152 + color.z * 0.0722;
final keep = look.z.clamp(0.0, 1.0);
final gain = math.max(look.y, 0.0);
double toned(double channel) =>
((luma + (channel - luma) * keep - 0.5) * gain + 0.5).clamp(0.0, 1.0);
color = Vector3(toned(color.x), toned(color.y), toned(color.z));
// Outline, wherever the geometry steps or turns.
final reach = edges.z;
if (surfaceTexture != null && depth > 0.0 && reach > 0.0) {
final dx = screen.x * reach;
final dy = screen.y * reach;
final left = surfaceTexture.sample(u - dx, w);
final right = surfaceTexture.sample(u + dx, w);
final up = surfaceTexture.sample(u, w - dy);
final down = surfaceTexture.sample(u, w + dy);
// An axis whose tap falls off the frame is left out: clamped, it is
// this pixel again, and the bend becomes the step.
final across = u - dx >= 0.0 && u + dx <= 1.0;
final along = w - dy >= 0.0 && w + dy <= 1.0;
final bent = math.max(
across ? _bend(left.w, depth, right.w) : 0.0,
along ? _bend(up.w, depth, down.w) : 0.0,
);
final turned = <Vector4>[left, right, up, down]
.where((tap) => tap.w > 0.0)
.map((tap) => 1.0 - decodeSurfaceNormal(tap.x, tap.y).dot(normal))
.fold(0.0, math.max);
if (bent >= bend || turned >= turn) {
color = Vector3(line.x, line.y, line.z);
}
}
// Roles: a share of the mark's colour inside, a ring of it outside.
final ringWidth = line.w;
if (maskTexture != null) {
final own = maskTexture.sample(u, w);
if (own.w > 0.5) {
final fill = look.w.clamp(0.0, 1.0);
color = color + (Vector3(own.x, own.y, own.z) - color) * fill;
} else if (ringWidth > 0.0) {
Vector4? found;
for (var i = 1; i <= 4 && found == null; i++) {
if (i > ringWidth) break;
for (final (ox, oy) in _directions) {
final tap = maskTexture.sample(
u + ox * screen.x * i,
w + oy * screen.y * i,
);
if (found == null && tap.w > 0.5) found = tap;
}
}
if (found != null) color = Vector3(found.x, found.y, found.z);
}
}
return Vector4(color.x, color.y, color.z, scene.w);
}