PUNCTUAL_LIGHT_WGSL:"struct PunctualLight {\n irradiance : vec3<f32>,\n direction : vec3<f32>,\n};\n\nfn punctualLight(index : i32, p : vec3<f32>) -> PunctualLight {\n let offset = lights.punctualPosition[index].xyz - p;\n let distanceSquared = max(dot(offset, offset), 1e-8);\n let d = sqrt(distanceSquared);\n let l = offset / d;\n let params = lights.punctualParams[index];\n var attenuation = 1.0 / distanceSquared;\n if (params.x > 0.0) {\n let ratio = d / params.x;\n let squared = ratio * ratio;\n attenuation = attenuation * clamp(1.0 - squared * squared, 0.0, 1.0);\n }\n if (params.w > 0.0) {\n let cosTheta = dot(lights.punctualDirection[index].xyz, -l);\n attenuation = attenuation * clamp((cosTheta - params.y) * params.z, 0.0, 1.0);\n }\n var result : PunctualLight;\n result.irradiance = lights.punctualColor[index].xyz * attenuation;\n result.direction = l;\n return result;\n}"
The light model both shaded fragment stages share — the WGSL port of
gl-program.ts's PUNCTUAL_LIGHT_GLSL, one string spliced into both
families for the recorded reason: the falloff and the cone are one model,
and a scene mixing a LitMaterial with a StandardMaterial under the same
lamp must not be able to disagree about it.
The arithmetic is the GLSL chunk's, line for line — inverse-square with the
max(d², 1e-8) guard where the division happens, KHR_lights_punctual's
range window and cone ramp, the precomputed params.z reciprocal — with
one syntactic difference: WGSL has no out parameters, so the direction
travels back in a two-member struct instead of an out vec3 l.
No 1/π appears here or in either lobe that consumes this — the engine's
light colour × intensity is an irradiance already divided by π (R-13), which
is what lets a point light and the directional light add up to one lighting
model across both backends.
The light model both shaded fragment stages share — the WGSL port of
gl-program.ts'sPUNCTUAL_LIGHT_GLSL, one string spliced into both families for the recorded reason: the falloff and the cone are one model, and a scene mixing aLitMaterialwith aStandardMaterialunder the same lamp must not be able to disagree about it.The arithmetic is the GLSL chunk's, line for line — inverse-square with the
max(d², 1e-8)guard where the division happens,KHR_lights_punctual's range window and cone ramp, the precomputedparams.zreciprocal — with one syntactic difference: WGSL has nooutparameters, so the direction travels back in a two-member struct instead of anout vec3 l.No
1/πappears here or in either lobe that consumes this — the engine's light colour × intensity is an irradiance already divided by π (R-13), which is what lets a point light and the directional light add up to one lighting model across both backends.