struct Camera { projection : mat4x4, inverseProjection : mat4x4, view : mat4x4, position : vec3, time : f32, outputSize : vec2, zNear : f32, zFar : f32, } @group(0) @binding(0) var camera : Camera; struct ClusterBounds { minAABB : vec3, maxAABB : vec3, } struct Clusters { bounds : array, } @group(0) @binding(1) var clusters : Clusters; struct ClusterLights { offset : u32, count : u32, } struct ClusterLightGroup { offset : atomic, lights : array, indices : array, } @group(0) @binding(2) var clusterLights : ClusterLightGroup; struct Light { position : vec3, range : f32, color : vec3, intensity : f32, } struct GlobalLights { ambient : vec3, dirColor : vec3, dirIntensity : f32, dirDirection : vec3, lightCount : u32, lights : array, } @group(0) @binding(3) var globalLights : GlobalLights; const tileCount = vec3(32u, 18u, 48u); fn linearDepth(depthSample : f32) -> f32 { return ((camera.zFar * camera.zNear) / fma(depthSample, (camera.zNear - camera.zFar), camera.zFar)); } fn getTile(fragCoord : vec4) -> vec3 { let sliceScale = (f32(tileCount.z) / log2((camera.zFar / camera.zNear))); let sliceBias = -(((f32(tileCount.z) * log2(camera.zNear)) / log2((camera.zFar / camera.zNear)))); let zTile = u32(max(((log2(linearDepth(fragCoord.z)) * sliceScale) + sliceBias), 0.0)); return vec3(u32((fragCoord.x / (camera.outputSize.x / f32(tileCount.x)))), u32((fragCoord.y / (camera.outputSize.y / f32(tileCount.y)))), zTile); } fn getClusterIndex(fragCoord : vec4) -> u32 { let tile = getTile(fragCoord); return ((tile.x + (tile.y * tileCount.x)) + ((tile.z * tileCount.x) * tileCount.y)); } fn sqDistPointAABB(point : vec3, minAABB : vec3, maxAABB : vec3) -> f32 { var sqDist = 0.0; for(var i : i32 = 0; (i < 3); i = (i + 1)) { let v = point[i]; if ((v < minAABB[i])) { sqDist = (sqDist + ((minAABB[i] - v) * (minAABB[i] - v))); } if ((v > maxAABB[i])) { sqDist = (sqDist + ((v - maxAABB[i]) * (v - maxAABB[i]))); } } return sqDist; } @compute @workgroup_size(4, 2, 4) fn computeMain(@builtin(global_invocation_id) global_id : vec3) { let tileIndex = ((global_id.x + (global_id.y * tileCount.x)) + ((global_id.z * tileCount.x) * tileCount.y)); var clusterLightCount = 0u; var cluserLightIndices : array; for(var i = 0u; (i < globalLights.lightCount); i = (i + 1u)) { let range = globalLights.lights[i].range; var lightInCluster : bool = (range <= 0.0); if (!(lightInCluster)) { let lightViewPos = (camera.view * vec4(globalLights.lights[i].position, 1.0)); let sqDist = sqDistPointAABB(lightViewPos.xyz, clusters.bounds[tileIndex].minAABB, clusters.bounds[tileIndex].maxAABB); lightInCluster = (sqDist <= (range * range)); } if (lightInCluster) { cluserLightIndices[clusterLightCount] = i; clusterLightCount = (clusterLightCount + 1u); } if ((clusterLightCount == 256u)) { break; } } let lightCount = clusterLightCount; var offset = atomicAdd(&(clusterLights.offset), lightCount); if ((offset >= 1769472u)) { return; } for(var i = 0u; (i < clusterLightCount); i = (i + 1u)) { clusterLights.indices[(offset + i)] = cluserLightIndices[i]; } clusterLights.lights[tileIndex].offset = offset; clusterLights.lights[tileIndex].count = clusterLightCount; }