sampleIrradiance function

Vector3 sampleIrradiance(
  1. ShaderBindings b,
  2. Vector3 world,
  3. Vector3 normal,
  4. Vector3 view,
)

SampleIrradiance(world, normal, view).

Implementation

Vector3 sampleIrradiance(
  ShaderBindings b,
  Vector3 world,
  Vector3 normal,
  Vector3 view,
) {
  final atlas = b.textures['irradiance_texture']!;
  final origin4 = b.vec4(_block, 'origin', Vector4.zero());
  final spacing4 = b.vec4(_block, 'spacing', Vector4.zero());
  final counts4 = b.vec4(_block, 'counts', Vector4.zero());
  final tiles = b.vec4(_block, 'tiles', Vector4.zero());
  final size = b.vec4(_block, 'atlas', Vector4.zero());
  final origin = Vector3(origin4.x, origin4.y, origin4.z);
  final spacing = Vector3(spacing4.x, spacing4.y, spacing4.z);
  final irradianceTile = tiles.x;
  final depthTile = tiles.y;
  final columns = tiles.z;
  final momentsTop = tiles.w;

  Vector4 texel(double x, double y) =>
      atlas.sample((x + 0.5) * size.x, (y + 0.5) * size.y);

  Vector4 bilinear(
    double cornerX,
    double cornerY,
    double interior,
    Vector2 uv,
  ) {
    final ax = 1.0 + uv.x * interior - 0.5;
    final ay = 1.0 + uv.y * interior - 0.5;
    final lx = ax.floorToDouble();
    final ly = ay.floorToDouble();
    final fx = ax - lx;
    final fy = ay - ly;
    final a = texel(cornerX + lx, cornerY + ly);
    final bb = texel(cornerX + lx + 1.0, cornerY + ly);
    final c = texel(cornerX + lx, cornerY + ly + 1.0);
    final d = texel(cornerX + lx + 1.0, cornerY + ly + 1.0);
    final top = a + (bb - a) * fx;
    final bottom = c + (d - c) * fx;
    return top + (bottom - top) * fy;
  }

  final unit = normal.normalized();
  final biased = world + unit * spacing4.w + view * counts4.w;
  final grid = Vector3(
    (biased.x - origin.x) / spacing.x,
    (biased.y - origin.y) / spacing.y,
    (biased.z - origin.z) / spacing.z,
  );
  final base = Vector3(
    grid.x.floorToDouble().clamp(0.0, counts4.x - 2.0),
    grid.y.floorToDouble().clamp(0.0, counts4.y - 2.0),
    grid.z.floorToDouble().clamp(0.0, counts4.z - 2.0),
  );
  final f = Vector3(
    (grid.x - base.x).clamp(0.0, 1.0),
    (grid.y - base.y).clamp(0.0, 1.0),
    (grid.z - base.z).clamp(0.0, 1.0),
  );

  final total = Vector3.zero();
  var weights = 0.0;
  for (var corner = 0; corner < 8; corner++) {
    final ox = (corner & 1).toDouble();
    final oy = ((corner >> 1) & 1).toDouble();
    final oz = ((corner >> 2) & 1).toDouble();
    final cx = base.x + ox;
    final cy = base.y + oy;
    final cz = base.z + oz;
    final probe = (cz * counts4.y + cy) * counts4.x + cx;
    final tileX = probe % columns;
    final tileY = (probe / columns).floorToDouble();

    final irradianceX = tileX * (irradianceTile + 2.0);
    final irradianceY = tileY * (irradianceTile + 2.0);
    final momentX = tileX * (depthTile + 2.0);
    final momentY = momentsTop + tileY * (depthTile + 2.0);

    if (texel(irradianceX + 1.0, irradianceY + 1.0).w < 0.5) continue;

    final wx = ox == 1.0 ? f.x : 1.0 - f.x;
    final wy = oy == 1.0 ? f.y : 1.0 - f.y;
    final wz = oz == 1.0 ? f.z : 1.0 - f.z;
    var weight = math.max(wx * wy * wz, 0.001);

    final probePosition = Vector3(
      origin.x + spacing.x * cx,
      origin.y + spacing.y * cy,
      origin.z + spacing.z * cz,
    );
    final toProbe = probePosition - biased;
    final distance = toProbe.length;
    if (distance > 1e-6) {
      final direction = toProbe / distance;
      final facing = unit.dot((probePosition - world).normalized()) * 0.5 + 0.5;

      final moments = bilinear(
        momentX,
        momentY,
        depthTile,
        _octahedral(-direction),
      );
      final chebyshev = _chebyshevCubed(moments.x, moments.y, distance);
      final floored = math.max(
        (facing * facing + 0.2) * math.max(chebyshev, 0.05),
        1e-6,
      );
      weight *= floored < 0.2 ? floored * floored * floored * 25.0 : floored;
    }

    final light = bilinear(
      irradianceX,
      irradianceY,
      irradianceTile,
      _octahedral(unit),
    );
    total.add(Vector3(light.x, light.y, light.z) * weight);
    weights += weight;
  }
  return weights > 0.0 ? total / weights : Vector3.zero();
}