sampleIrradiance function
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();
}