uvFootprint function

UvFootprint uvFootprint(
  1. FragmentContext c, {
  2. double bias = 0.0,
})

How far the texture coordinate moves per screen pixel, for mip selection.

Every map below sampled the base level until this existed, whatever mip chain the texture had been uploaded with, because CpuTexture.sample selects level zero when it is given no derivatives and nothing here was giving it any. The particle stage had been passing them since it was written; the lit path never adopted it, so a normal map read at full resolution wherever the hardware backends read a blurred level. That shows as the fine detail being sharper and darker than it should be — it made normal-mapping the widest disagreement this backend had with Impeller — and as aliasing that moves when the camera does.

The larger of the two directions per axis, which is what the particle stage takes and for the same reason: a hardware sampler uses the longer side of the footprint parallelogram, and the maximum of the axis-aligned components is that for a surface facing the camera.

bias is texture's third argument, materialLodBias in the GLSL — R2: a level of detail is the log of the footprint, so a bias of b is the footprint times 2^b. Nought leaves it exactly as it was.

Implementation

UvFootprint uvFootprint(FragmentContext c, {double bias = 0.0}) {
  final ddx = c.ddx;
  final ddy = c.ddy;
  if (ddx == null || ddy == null) {
    return (du: 0.0, dv: 0.0, dudx: 0.0, dvdx: 0.0, dudy: 0.0, dvdy: 0.0);
  }
  if (bias != 0.0) {
    final scale = math.pow(2.0, bias).toDouble();
    final plain = uvFootprint(c);
    return (
      du: plain.du * scale,
      dv: plain.dv * scale,
      dudx: plain.dudx * scale,
      dvdx: plain.dvdx * scale,
      dudy: plain.dudy * scale,
      dvdy: plain.dvdy * scale,
    );
  }
  // **Both vectors, as well as the axis maxima** — `gfx-02n`. `du`/`dv` are
  // what every caller here has always used and what the mip level is still
  // chosen from, unchanged: the maximum of the axis-aligned components, which
  // is the longer side of the footprint for a surface facing the camera.
  //
  // The four below are the footprint itself, before that collapse, and they
  // are why anisotropy needed them: a floor at a grazing angle has a long,
  // thin parallelogram, and `max` over each axis separately throws away
  // exactly the *ratio* between its sides. The sampler reads them only when a
  // sampler asked for taps, so nothing that did not ask can move.
  return (
    du: math.max(ddx[kVUv].abs(), ddy[kVUv].abs()),
    dv: math.max(ddx[kVUv + 1].abs(), ddy[kVUv + 1].abs()),
    dudx: ddx[kVUv],
    dvdx: ddx[kVUv + 1],
    dudy: ddy[kVUv],
    dvdy: ddy[kVUv + 1],
  );
}