Jazz2/Rendering/SoftwareLightSplat.h file

Namespaces

namespace Jazz2
Root namespace of Jazz² Resurrection.
namespace Jazz2::Rendering
Scene rendering and viewport management.
namespace Jazz2::Rendering::SoftwareLighting

Functions

auto Distance(float d2) -> float
Reciprocal square root estimate of the platform, where it has one worth using.
void SplatLight(float* lightmap, std::int32_t lmW, std::int32_t lmH, float cx, float cy, float rLm, float radiusNearNorm, float intensity, float brightness)
Adds one light to a two-channel (intensity, brightness) float lightmap.

Function documentation

float Distance(float d2)

Reciprocal square root estimate of the platform, where it has one worth using.

The falloff needs one square root per texel of the lit annulus, and on the consoles without a pipelined one that is most of the splat: the Gekko/Broadway have no fsqrt at all, so std::sqrt is a software routine of a few hundred cycles, and the SH-4's fsqrt takes about 22 cycles and blocks the pipeline. Both do have a fast estimate of 1/sqrt(x) - frsqrte (the PowerPC architecture guarantees only 1 part in 32, so it is refined with two Newton steps, which lands within about 3e-6 from any estimate at least that good) and fsrra (accurate to the last bits on its own). The lightmap is quantized to 8 bits per channel before anything sees it, so a relative error of that size cannot change a single output value except at a quantization boundary.

Returns $ \sqrt{d2} $ for a squared distance, as cheaply as the platform allows

void SplatLight(float* lightmap, std::int32_t lmW, std::int32_t lmH, float cx, float cy, float rLm, float radiusNearNorm, float intensity, float brightness)

Adds one light to a two-channel (intensity, brightness) float lightmap.

Parameters
lightmap lmW * lmH texels of two floats each, row-major
lmW Lightmap width in texels
lmH Lightmap height in texels
cx Light centre in lightmap texels
cy Light centre in lightmap texels
rLm Far radius in lightmap texels (at least 0.5)
radiusNearNorm Near radius as a fraction of the far radius (may exceed 1: no falloff)
intensity Added to the first channel of every texel the light reaches, scaled by the falloff
brightness Added to the second channel the same way

The falloff is the one in LightingFs.inc: strength is 1 out to the near radius and then ((1 - dist) / (1 - near))^3 to the far radius, in units of the far radius. Instead of visiting the whole bounding box and classifying every texel with two compares and a branch, each row is cut analytically into the texels inside the disc - |x - cx| <= sqrt(1 - dy^2) * rLm - and the flat core inside those - |x - cx| <= sqrt(near^2 - dy^2) * rLm. Texels outside the disc are never touched, core texels get two additions, and only the annulus pays for a square root. It is the same set of texels the per-texel tests select, up to floating-point rounding of a texel exactly on a boundary, where the strength is 0 or 1 to within ~1e-7 either way - except for a light with no falloff (near >= far), whose edge is a hard step and so lands one texel differently now and then; a host harness over 196 million texel values found no other difference above 1e-3. The annulus loops are branch-free so that the compiler can interleave several texels for the in-order cores this runs on; dx is recomputed from x rather than accumulated, so no iteration depends on the previous one.