Variable PUNCTUAL_LIGHT_WGSLConst
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'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.