| let GAMMA = 2.200000048; | 
 |  | 
 | fn linearTosRGB(linear : vec3<f32>) -> vec3<f32> { | 
 |   let INV_GAMMA = (1.0 / GAMMA); | 
 |   return pow(linear, vec3(INV_GAMMA)); | 
 | } | 
 |  | 
 | fn sRGBToLinear(srgb : vec3<f32>) -> vec3<f32> { | 
 |   return pow(srgb, vec3(GAMMA)); | 
 | } | 
 |  | 
 | struct Camera { | 
 |   projection : mat4x4<f32>, | 
 |   inverseProjection : mat4x4<f32>, | 
 |   view : mat4x4<f32>, | 
 |   position : vec3<f32>, | 
 |   time : f32, | 
 |   outputSize : vec2<f32>, | 
 |   zNear : f32, | 
 |   zFar : f32, | 
 | } | 
 |  | 
 | @binding(0) @group(0) var<uniform> camera : Camera; | 
 |  | 
 | struct ClusterLights { | 
 |   offset : u32, | 
 |   count : u32, | 
 | } | 
 |  | 
 | struct ClusterLightGroup { | 
 |   offset : u32, | 
 |   lights : array<ClusterLights, 27648>, | 
 |   indices : array<u32, 1769472>, | 
 | } | 
 |  | 
 | @binding(1) @group(0) var<storage, read> clusterLights : ClusterLightGroup; | 
 |  | 
 | struct Light { | 
 |   position : vec3<f32>, | 
 |   range : f32, | 
 |   color : vec3<f32>, | 
 |   intensity : f32, | 
 | } | 
 |  | 
 | struct GlobalLights { | 
 |   ambient : vec3<f32>, | 
 |   dirColor : vec3<f32>, | 
 |   dirIntensity : f32, | 
 |   dirDirection : vec3<f32>, | 
 |   lightCount : u32, | 
 |   lights : array<Light>, | 
 | } | 
 |  | 
 | @binding(2) @group(0) var<storage, read> globalLights : GlobalLights; | 
 |  | 
 | let 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<f32>) -> vec3<u32> { | 
 |   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<f32>) -> u32 { | 
 |   let tile = getTile(fragCoord); | 
 |   return ((tile.x + (tile.y * tileCount.x)) + ((tile.z * tileCount.x) * tileCount.y)); | 
 | } | 
 |  | 
 | @binding(3) @group(0) var defaultSampler : sampler; | 
 |  | 
 | @binding(4) @group(0) var shadowTexture : texture_depth_2d; | 
 |  | 
 | @binding(5) @group(0) var shadowSampler : sampler_comparison; | 
 |  | 
 | struct LightShadowTable { | 
 |   light : array<i32>, | 
 | } | 
 |  | 
 | @binding(6) @group(0) var<storage, read> lightShadowTable : LightShadowTable; | 
 |  | 
 | var<private> shadowSampleOffsets : array<vec2<f32>, 16> = array<vec2<f32>, 16>(vec2(-1.5, -1.5), vec2(-1.5, -0.5), vec2(-1.5, 0.5), vec2(-1.5, 1.5), vec2(-0.5, -1.5), vec2(-0.5, -0.5), vec2(-0.5, 0.5), vec2(-0.5, 1.5), vec2(0.5, -1.5), vec2(0.5, -0.5), vec2(0.5, 0.5), vec2(0.5, 1.5), vec2(1.5, -1.5), vec2(1.5, -0.5), vec2(1.5, 0.5), vec2(1.5, 1.5)); | 
 |  | 
 | let shadowSampleCount = 16u; | 
 |  | 
 | struct ShadowProperties { | 
 |   viewport : vec4<f32>, | 
 |   viewProj : mat4x4<f32>, | 
 | } | 
 |  | 
 | struct LightShadows { | 
 |   properties : array<ShadowProperties>, | 
 | } | 
 |  | 
 | @binding(7) @group(0) var<storage, read> shadow : LightShadows; | 
 |  | 
 | fn dirLightVisibility(worldPos : vec3<f32>) -> f32 { | 
 |   let shadowIndex = lightShadowTable.light[0u]; | 
 |   if ((shadowIndex == -1)) { | 
 |     return 1.0; | 
 |   } | 
 |   let viewport = shadow.properties[shadowIndex].viewport; | 
 |   let lightPos = (shadow.properties[shadowIndex].viewProj * vec4(worldPos, 1.0)); | 
 |   let shadowPos = vec3((((lightPos.xy / lightPos.w) * vec2(0.5, -0.5)) + vec2(0.5, 0.5)), (lightPos.z / lightPos.w)); | 
 |   let viewportPos = vec2((viewport.xy + (shadowPos.xy * viewport.zw))); | 
 |   let texelSize = (1.0 / vec2<f32>(textureDimensions(shadowTexture, 0))); | 
 |   let clampRect = vec4((viewport.xy - texelSize), ((viewport.xy + viewport.zw) + texelSize)); | 
 |   var visibility = 0.0; | 
 |   for(var i = 0u; (i < shadowSampleCount); i = (i + 1u)) { | 
 |     visibility = (visibility + textureSampleCompareLevel(shadowTexture, shadowSampler, clamp((viewportPos + (shadowSampleOffsets[i] * texelSize)), clampRect.xy, clampRect.zw), (shadowPos.z - 0.003))); | 
 |   } | 
 |   return (visibility / f32(shadowSampleCount)); | 
 | } | 
 |  | 
 | fn getCubeFace(v : vec3<f32>) -> i32 { | 
 |   let vAbs = abs(v); | 
 |   if (((vAbs.z >= vAbs.x) && (vAbs.z >= vAbs.y))) { | 
 |     if ((v.z < 0.0)) { | 
 |       return 5; | 
 |     } | 
 |     return 4; | 
 |   } | 
 |   if ((vAbs.y >= vAbs.x)) { | 
 |     if ((v.y < 0.0)) { | 
 |       return 3; | 
 |     } | 
 |     return 2; | 
 |   } | 
 |   if ((v.x < 0.0)) { | 
 |     return 1; | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | fn pointLightVisibility(lightIndex : u32, worldPos : vec3<f32>, pointToLight : vec3<f32>) -> f32 { | 
 |   var shadowIndex = lightShadowTable.light[(lightIndex + 1u)]; | 
 |   if ((shadowIndex == -1)) { | 
 |     return 1.0; | 
 |   } | 
 |   shadowIndex = (shadowIndex + getCubeFace((pointToLight * -1.0))); | 
 |   let viewport = shadow.properties[shadowIndex].viewport; | 
 |   let lightPos = (shadow.properties[shadowIndex].viewProj * vec4(worldPos, 1.0)); | 
 |   let shadowPos = vec3((((lightPos.xy / lightPos.w) * vec2(0.5, -0.5)) + vec2(0.5, 0.5)), (lightPos.z / lightPos.w)); | 
 |   let viewportPos = vec2((viewport.xy + (shadowPos.xy * viewport.zw))); | 
 |   let texelSize = (1.0 / vec2<f32>(textureDimensions(shadowTexture, 0))); | 
 |   let clampRect = vec4(viewport.xy, (viewport.xy + viewport.zw)); | 
 |   var visibility = 0.0; | 
 |   for(var i = 0u; (i < shadowSampleCount); i = (i + 1u)) { | 
 |     visibility = (visibility + textureSampleCompareLevel(shadowTexture, shadowSampler, clamp((viewportPos + (shadowSampleOffsets[i] * texelSize)), clampRect.xy, clampRect.zw), (shadowPos.z - 0.01))); | 
 |   } | 
 |   return (visibility / f32(shadowSampleCount)); | 
 | } | 
 |  | 
 | struct VertexOutput { | 
 |   @builtin(position) | 
 |   position : vec4<f32>, | 
 |   @location(0) | 
 |   worldPos : vec3<f32>, | 
 |   @location(1) | 
 |   view : vec3<f32>, | 
 |   @location(2) | 
 |   texcoord : vec2<f32>, | 
 |   @location(3) | 
 |   texcoord2 : vec2<f32>, | 
 |   @location(4) | 
 |   color : vec4<f32>, | 
 |   @location(5) | 
 |   instanceColor : vec4<f32>, | 
 |   @location(6) | 
 |   normal : vec3<f32>, | 
 |   @location(7) | 
 |   tangent : vec3<f32>, | 
 |   @location(8) | 
 |   bitangent : vec3<f32>, | 
 | } | 
 |  | 
 | struct Material { | 
 |   baseColorFactor : vec4<f32>, | 
 |   emissiveFactor : vec3<f32>, | 
 |   occlusionStrength : f32, | 
 |   metallicRoughnessFactor : vec2<f32>, | 
 |   alphaCutoff : f32, | 
 | } | 
 |  | 
 | @binding(8) @group(0) var<uniform> material : Material; | 
 |  | 
 | @binding(9) @group(0) var baseColorTexture : texture_2d<f32>; | 
 |  | 
 | @binding(10) @group(0) var baseColorSampler : sampler; | 
 |  | 
 | @binding(11) @group(0) var normalTexture : texture_2d<f32>; | 
 |  | 
 | @binding(12) @group(0) var normalSampler : sampler; | 
 |  | 
 | @binding(13) @group(0) var metallicRoughnessTexture : texture_2d<f32>; | 
 |  | 
 | @binding(14) @group(0) var metallicRoughnessSampler : sampler; | 
 |  | 
 | @binding(15) @group(0) var occlusionTexture : texture_2d<f32>; | 
 |  | 
 | @binding(16) @group(0) var occlusionSampler : sampler; | 
 |  | 
 | @binding(17) @group(0) var emissiveTexture : texture_2d<f32>; | 
 |  | 
 | @binding(18) @group(0) var emissiveSampler : sampler; | 
 |  | 
 | struct SurfaceInfo { | 
 |   baseColor : vec4<f32>, | 
 |   albedo : vec3<f32>, | 
 |   metallic : f32, | 
 |   roughness : f32, | 
 |   normal : vec3<f32>, | 
 |   f0 : vec3<f32>, | 
 |   ao : f32, | 
 |   emissive : vec3<f32>, | 
 |   v : vec3<f32>, | 
 | } | 
 |  | 
 | fn GetSurfaceInfo(input : VertexOutput) -> SurfaceInfo { | 
 |   var surface : SurfaceInfo; | 
 |   surface.v = normalize(input.view); | 
 |   let tbn = mat3x3(input.tangent, input.bitangent, input.normal); | 
 |   let normalMap = textureSample(normalTexture, normalSampler, input.texcoord).rgb; | 
 |   surface.normal = normalize((tbn * ((2.0 * normalMap) - vec3(1.0)))); | 
 |   let baseColorMap = textureSample(baseColorTexture, baseColorSampler, input.texcoord); | 
 |   surface.baseColor = ((input.color * material.baseColorFactor) * baseColorMap); | 
 |   if ((surface.baseColor.a < material.alphaCutoff)) { | 
 |     discard; | 
 |   } | 
 |   surface.albedo = surface.baseColor.rgb; | 
 |   let metallicRoughnessMap = textureSample(metallicRoughnessTexture, metallicRoughnessSampler, input.texcoord); | 
 |   surface.metallic = (material.metallicRoughnessFactor.x * metallicRoughnessMap.b); | 
 |   surface.roughness = (material.metallicRoughnessFactor.y * metallicRoughnessMap.g); | 
 |   let dielectricSpec = vec3(0.039999999); | 
 |   surface.f0 = mix(dielectricSpec, surface.albedo, vec3(surface.metallic)); | 
 |   let occlusionMap = textureSample(occlusionTexture, occlusionSampler, input.texcoord); | 
 |   surface.ao = (material.occlusionStrength * occlusionMap.r); | 
 |   let emissiveMap = textureSample(emissiveTexture, emissiveSampler, input.texcoord); | 
 |   surface.emissive = (material.emissiveFactor * emissiveMap.rgb); | 
 |   if ((input.instanceColor.a == 0.0)) { | 
 |     surface.albedo = (surface.albedo + input.instanceColor.rgb); | 
 |   } else { | 
 |     surface.albedo = (surface.albedo * input.instanceColor.rgb); | 
 |   } | 
 |   return surface; | 
 | } | 
 |  | 
 | let PI = 3.141592741; | 
 |  | 
 | let LightType_Point = 0u; | 
 |  | 
 | let LightType_Spot = 1u; | 
 |  | 
 | let LightType_Directional = 2u; | 
 |  | 
 | struct PuctualLight { | 
 |   lightType : u32, | 
 |   pointToLight : vec3<f32>, | 
 |   range : f32, | 
 |   color : vec3<f32>, | 
 |   intensity : f32, | 
 | } | 
 |  | 
 | fn FresnelSchlick(cosTheta : f32, F0 : vec3<f32>) -> vec3<f32> { | 
 |   return (F0 + ((vec3(1.0) - F0) * pow((1.0 - cosTheta), 5.0))); | 
 | } | 
 |  | 
 | fn DistributionGGX(N : vec3<f32>, H : vec3<f32>, roughness : f32) -> f32 { | 
 |   let a = (roughness * roughness); | 
 |   let a2 = (a * a); | 
 |   let NdotH = max(dot(N, H), 0.0); | 
 |   let NdotH2 = (NdotH * NdotH); | 
 |   let num = a2; | 
 |   let denom = ((NdotH2 * (a2 - 1.0)) + 1.0); | 
 |   return (num / ((PI * denom) * denom)); | 
 | } | 
 |  | 
 | fn GeometrySchlickGGX(NdotV : f32, roughness : f32) -> f32 { | 
 |   let r = (roughness + 1.0); | 
 |   let k = ((r * r) / 8.0); | 
 |   let num = NdotV; | 
 |   let denom = ((NdotV * (1.0 - k)) + k); | 
 |   return (num / denom); | 
 | } | 
 |  | 
 | fn GeometrySmith(N : vec3<f32>, V : vec3<f32>, L : vec3<f32>, roughness : f32) -> f32 { | 
 |   let NdotV = max(dot(N, V), 0.0); | 
 |   let NdotL = max(dot(N, L), 0.0); | 
 |   let ggx2 = GeometrySchlickGGX(NdotV, roughness); | 
 |   let ggx1 = GeometrySchlickGGX(NdotL, roughness); | 
 |   return (ggx1 * ggx2); | 
 | } | 
 |  | 
 | fn lightAttenuation(light : PuctualLight) -> f32 { | 
 |   if ((light.lightType == LightType_Directional)) { | 
 |     return 1.0; | 
 |   } | 
 |   let distance = length(light.pointToLight); | 
 |   if ((light.range <= 0.0)) { | 
 |     return (1.0 / pow(distance, 2.0)); | 
 |   } | 
 |   return (clamp((1.0 - pow((distance / light.range), 4.0)), 0.0, 1.0) / pow(distance, 2.0)); | 
 | } | 
 |  | 
 | fn lightRadiance(light : PuctualLight, surface : SurfaceInfo) -> vec3<f32> { | 
 |   let L = normalize(light.pointToLight); | 
 |   let H = normalize((surface.v + L)); | 
 |   let NDF = DistributionGGX(surface.normal, H, surface.roughness); | 
 |   let G = GeometrySmith(surface.normal, surface.v, L, surface.roughness); | 
 |   let F = FresnelSchlick(max(dot(H, surface.v), 0.0), surface.f0); | 
 |   let kD = ((vec3(1.0) - F) * (1.0 - surface.metallic)); | 
 |   let NdotL = max(dot(surface.normal, L), 0.0); | 
 |   let numerator = ((NDF * G) * F); | 
 |   let denominator = max(((4.0 * max(dot(surface.normal, surface.v), 0.0)) * NdotL), 0.001); | 
 |   let specular = (numerator / vec3(denominator)); | 
 |   let radiance = ((light.color * light.intensity) * lightAttenuation(light)); | 
 |   return (((((kD * surface.albedo) / vec3(PI)) + specular) * radiance) * NdotL); | 
 | } | 
 |  | 
 | @binding(19) @group(0) var ssaoTexture : texture_2d<f32>; | 
 |  | 
 | struct FragmentOutput { | 
 |   @location(0) | 
 |   color : vec4<f32>, | 
 |   @location(1) | 
 |   emissive : vec4<f32>, | 
 | } | 
 |  | 
 | @fragment | 
 | fn fragmentMain(input : VertexOutput) -> FragmentOutput { | 
 |   let surface = GetSurfaceInfo(input); | 
 |   var Lo = vec3(0.0, 0.0, 0.0); | 
 |   if ((globalLights.dirIntensity > 0.0)) { | 
 |     var light : PuctualLight; | 
 |     light.lightType = LightType_Directional; | 
 |     light.pointToLight = globalLights.dirDirection; | 
 |     light.color = globalLights.dirColor; | 
 |     light.intensity = globalLights.dirIntensity; | 
 |     let lightVis = dirLightVisibility(input.worldPos); | 
 |     Lo = (Lo + (lightRadiance(light, surface) * lightVis)); | 
 |   } | 
 |   let clusterIndex = getClusterIndex(input.position); | 
 |   let lightOffset = clusterLights.lights[clusterIndex].offset; | 
 |   let lightCount = clusterLights.lights[clusterIndex].count; | 
 |   for(var lightIndex = 0u; (lightIndex < lightCount); lightIndex = (lightIndex + 1u)) { | 
 |     let i = clusterLights.indices[(lightOffset + lightIndex)]; | 
 |     var light : PuctualLight; | 
 |     light.lightType = LightType_Point; | 
 |     light.pointToLight = (globalLights.lights[i].position.xyz - input.worldPos); | 
 |     light.range = globalLights.lights[i].range; | 
 |     light.color = globalLights.lights[i].color; | 
 |     light.intensity = globalLights.lights[i].intensity; | 
 |     let lightVis = pointLightVisibility(i, input.worldPos, light.pointToLight); | 
 |     Lo = (Lo + (lightRadiance(light, surface) * lightVis)); | 
 |   } | 
 |   let ssaoCoord = (input.position.xy / vec2<f32>(textureDimensions(ssaoTexture).xy)); | 
 |   let ssaoFactor = textureSample(ssaoTexture, defaultSampler, ssaoCoord).r; | 
 |   let ambient = (((globalLights.ambient * surface.albedo) * surface.ao) * ssaoFactor); | 
 |   let color = linearTosRGB(((Lo + ambient) + surface.emissive)); | 
 |   var out : FragmentOutput; | 
 |   out.color = vec4(color, surface.baseColor.a); | 
 |   out.emissive = vec4(surface.emissive, surface.baseColor.a); | 
 |   return out; | 
 | } |