@@ -104,10 +104,12 @@ struct Light { // -------------------------
104104 color2: vec3<f32>, // 80* 16 12
105105 nearPlane: f32, // 92 4 4
106106 texCoords: vec4<f32>, // 96* 16 16
107- texOffset: vec4<f32>, // 112* 16 12
108- texScale: vec4<f32>, // 128* 16 12
107+ texOffset: vec4<f32>, // 112* 16 16
108+ texScale: vec4<f32>, // 128* 16 16
109+ hidden: f32, // 144* 16 4
110+ _padding: f32, // 148 4 12
109111} // -------------------------
110- // 16 144
112+ // 16 160
111113
112114 // offset align size
113115struct Hittable { // -------------------------
@@ -1214,11 +1216,15 @@ fn hitYzRect(id: u32, ray: Ray, tMin: f32, tMax: f32, hitRecord: ptr<function, H
12141216 return true;
12151217}
12161218
1217- fn hitLights(ray: Ray, hitRecord: ptr<function, HitRecord>, seed: ptr<function, u32>) -> vec3<f32> {
1219+ fn hitLights(ray: Ray, hitRecord: ptr<function, HitRecord>, seed: ptr<function, u32>, depth: u32 ) -> vec3<f32> {
12181220 var hit: bool;
12191221 var color = vec3<f32>(0f, 0f, 0f);
12201222 for (var i: u32 = 0u; i < arrayLength(&lightBuffer.lights); i++) {
12211223 let light = lightBuffer.lights[i];
1224+
1225+ // Skip hidden lights on primary rays
1226+ if (depth == 0u && light.hidden > 0f) { continue; }
1227+
12221228 let lightTypeId = u32(light.typeId);
12231229
12241230 // TODO: Split into separate direct and indirect functions
@@ -1237,7 +1243,10 @@ fn hitLights(ray: Ray, hitRecord: ptr<function, HitRecord>, seed: ptr<function,
12371243 if (hitSpotLight(i, ray, &color, hitRecord, seed)) { hit = true; }
12381244 }
12391245
1240- // Indirect lighting
1246+ // Indirect lighting (area lights)
1247+ case 1u: {
1248+ if (hitDiskLight(i, ray, &color, hitRecord)) { hit = true; }
1249+ }
12411250 case 5u: {
12421251 if (hitRectLight(i, ray, &color, hitRecord)) { hit = true; }
12431252 }
@@ -1253,6 +1262,8 @@ fn hitLights(ray: Ray, hitRecord: ptr<function, HitRecord>, seed: ptr<function,
12531262 return max(uniforms.ambientColor, color);
12541263 }
12551264
1265+ // No light hit: background for primary rays, ambient for bounced rays
1266+ if (depth == 0u) { return uniforms.backgroundColor.xyz; }
12561267 return uniforms.ambientColor;
12571268}
12581269
@@ -1275,6 +1286,25 @@ fn hitRectLight(id: u32, ray: Ray, color: ptr<function, vec3<f32>>, hitRecord: p
12751286 return true;
12761287}
12771288
1289+ fn hitDiskLight(id: u32, ray: Ray, color: ptr<function, vec3<f32>>, hitRecord: ptr<function, HitRecord>) -> bool {
1290+ let disk = &lightBuffer.lights[id];
1291+ let center = (*disk).center;
1292+ let rotation = (*disk).rotation;
1293+ let invRotation = conjugate(rotation);
1294+ var rotatedRay: Ray;
1295+ rotatedRay.origin = rotateQuat(ray.origin - center, invRotation) + center;
1296+ rotatedRay.direction = rotateQuat(ray.direction, invRotation);
1297+ if (dot(rotatedRay.direction, vec3<f32>(0f, 0f, 1f)) > 0f) { return false; } // Front face only
1298+ let oc = rotatedRay.origin - center;
1299+ let t = -oc.z / rotatedRay.direction.z;
1300+ if (t < 0f) { return false; }
1301+ let p = oc + t * rotatedRay.direction;
1302+ let radius = (*disk).size.x * 0.5f;
1303+ if (dot(p.xy, p.xy) > radius * radius) { return false; }
1304+ *color += (*disk).color;
1305+ return true;
1306+ }
1307+
12781308fn hitSphereLight(id: u32, ray: Ray, color: ptr<function, vec3<f32>>, hitRecord: ptr<function, HitRecord>) -> bool {
12791309 let sphere = &lightBuffer.lights[id];
12801310 let radius = (*sphere).size.x * 0.5f;
@@ -1632,14 +1662,11 @@ fn rayColor(ray: ptr<function, Ray>, seed: ptr<function, u32>) -> vec3<f32> {
16321662 }
16331663 else {
16341664 // Miss
1635- if (depth > 0u) {
1636- // Bounced ray: sample direct lights
1637- return hitLights(*ray, &hitRecord, seed) * color;
1638- }
1639- else {
1640- // Primary ray: background only (lights hidden on first bounce)
1641- return uniforms.backgroundColor.xyz;
1665+ let lightColor = hitLights(*ray, &hitRecord, seed, depth);
1666+ if (depth == 0u) {
1667+ return lightColor;
16421668 }
1669+ return lightColor * color;
16431670 }
16441671 }
16451672}
0 commit comments