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 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);
}