blob: 4021327e26a437db65eba2b7b80503867ef9753f [file] [log] [blame]
Ben Clayton73ced332022-01-21 22:38:16 +00001let GAMMA = 2.200000048;
2
3fn linearTosRGB(linear : vec3<f32>) -> vec3<f32> {
4 let INV_GAMMA = (1.0 / GAMMA);
5 return pow(linear, vec3(INV_GAMMA));
6}
7
8fn sRGBToLinear(srgb : vec3<f32>) -> vec3<f32> {
9 return pow(srgb, vec3(GAMMA));
10}
11
12struct Camera {
13 projection : mat4x4<f32>;
14 inverseProjection : mat4x4<f32>;
15 view : mat4x4<f32>;
16 position : vec3<f32>;
17 time : f32;
18 outputSize : vec2<f32>;
19 zNear : f32;
20 zFar : f32;
21}
22
23@binding(0) @group(0) var<uniform> camera : Camera;
24
25struct ClusterLights {
26 offset : u32;
27 count : u32;
28}
29
30struct ClusterLightGroup {
31 offset : u32;
32 lights : array<ClusterLights, 27648>;
33 indices : array<u32, 1769472>;
34}
35
36@binding(1) @group(0) var<storage, read> clusterLights : ClusterLightGroup;
37
38struct Light {
39 position : vec3<f32>;
40 range : f32;
41 color : vec3<f32>;
42 intensity : f32;
43}
44
45struct GlobalLights {
46 ambient : vec3<f32>;
47 dirColor : vec3<f32>;
48 dirIntensity : f32;
49 dirDirection : vec3<f32>;
50 lightCount : u32;
51 lights : @stride(32) array<Light>;
52}
53
54@binding(2) @group(0) var<storage, read> globalLights : GlobalLights;
55
56let tileCount = vec3(32u, 18u, 48u);
57
58fn linearDepth(depthSample : f32) -> f32 {
59 return ((camera.zFar * camera.zNear) / fma(depthSample, (camera.zNear - camera.zFar), camera.zFar));
60}
61
62fn getTile(fragCoord : vec4<f32>) -> vec3<u32> {
63 let sliceScale = (f32(tileCount.z) / log2((camera.zFar / camera.zNear)));
64 let sliceBias = -(((f32(tileCount.z) * log2(camera.zNear)) / log2((camera.zFar / camera.zNear))));
65 let zTile = u32(max(((log2(linearDepth(fragCoord.z)) * sliceScale) + sliceBias), 0.0));
66 return vec3(u32((fragCoord.x / (camera.outputSize.x / f32(tileCount.x)))), u32((fragCoord.y / (camera.outputSize.y / f32(tileCount.y)))), zTile);
67}
68
69fn getClusterIndex(fragCoord : vec4<f32>) -> u32 {
70 let tile = getTile(fragCoord);
71 return ((tile.x + (tile.y * tileCount.x)) + ((tile.z * tileCount.x) * tileCount.y));
72}
73
74@binding(3) @group(0) var defaultSampler : sampler;
75
76@binding(4) @group(0) var shadowTexture : texture_depth_2d;
77
78@binding(5) @group(0) var shadowSampler : sampler_comparison;
79
80struct LightShadowTable {
81 light : array<i32>;
82}
83
84@binding(6) @group(0) var<storage, read> lightShadowTable : LightShadowTable;
85
86var<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));
87
88let shadowSampleCount = 16u;
89
90struct ShadowProperties {
91 viewport : vec4<f32>;
92 viewProj : mat4x4<f32>;
93}
94
95struct LightShadows {
96 properties : array<ShadowProperties>;
97}
98
99@binding(7) @group(0) var<storage, read> shadow : LightShadows;
100
101fn dirLightVisibility(worldPos : vec3<f32>) -> f32 {
102 let shadowIndex = lightShadowTable.light[0u];
103 if ((shadowIndex == -1)) {
104 return 1.0;
105 }
106 let viewport = shadow.properties[shadowIndex].viewport;
107 let lightPos = (shadow.properties[shadowIndex].viewProj * vec4(worldPos, 1.0));
108 let shadowPos = vec3((((lightPos.xy / lightPos.w) * vec2(0.5, -0.5)) + vec2(0.5, 0.5)), (lightPos.z / lightPos.w));
109 let viewportPos = vec2((viewport.xy + (shadowPos.xy * viewport.zw)));
110 let texelSize = (1.0 / vec2<f32>(textureDimensions(shadowTexture, 0)));
111 let clampRect = vec4((viewport.xy - texelSize), ((viewport.xy + viewport.zw) + texelSize));
112 var visibility = 0.0;
113 for(var i = 0u; (i < shadowSampleCount); i = (i + 1u)) {
114 visibility = (visibility + textureSampleCompareLevel(shadowTexture, shadowSampler, clamp((viewportPos + (shadowSampleOffsets[i] * texelSize)), clampRect.xy, clampRect.zw), (shadowPos.z - 0.003)));
115 }
116 return (visibility / f32(shadowSampleCount));
117}
118
119fn getCubeFace(v : vec3<f32>) -> i32 {
120 let vAbs = abs(v);
121 if (((vAbs.z >= vAbs.x) && (vAbs.z >= vAbs.y))) {
122 if ((v.z < 0.0)) {
123 return 5;
124 }
125 return 4;
126 }
127 if ((vAbs.y >= vAbs.x)) {
128 if ((v.y < 0.0)) {
129 return 3;
130 }
131 return 2;
132 }
133 if ((v.x < 0.0)) {
134 return 1;
135 }
136 return 0;
137}
138
139fn pointLightVisibility(lightIndex : u32, worldPos : vec3<f32>, pointToLight : vec3<f32>) -> f32 {
140 var shadowIndex = lightShadowTable.light[(lightIndex + 1u)];
141 if ((shadowIndex == -1)) {
142 return 1.0;
143 }
144 shadowIndex = (shadowIndex + getCubeFace((pointToLight * -1.0)));
145 let viewport = shadow.properties[shadowIndex].viewport;
146 let lightPos = (shadow.properties[shadowIndex].viewProj * vec4(worldPos, 1.0));
147 let shadowPos = vec3((((lightPos.xy / lightPos.w) * vec2(0.5, -0.5)) + vec2(0.5, 0.5)), (lightPos.z / lightPos.w));
148 let viewportPos = vec2((viewport.xy + (shadowPos.xy * viewport.zw)));
149 let texelSize = (1.0 / vec2<f32>(textureDimensions(shadowTexture, 0)));
150 let clampRect = vec4(viewport.xy, (viewport.xy + viewport.zw));
151 var visibility = 0.0;
152 for(var i = 0u; (i < shadowSampleCount); i = (i + 1u)) {
153 visibility = (visibility + textureSampleCompareLevel(shadowTexture, shadowSampler, clamp((viewportPos + (shadowSampleOffsets[i] * texelSize)), clampRect.xy, clampRect.zw), (shadowPos.z - 0.01)));
154 }
155 return (visibility / f32(shadowSampleCount));
156}
157
158struct VertexOutput {
159 @builtin(position)
160 position : vec4<f32>;
161 @location(0)
162 worldPos : vec3<f32>;
163 @location(1)
164 view : vec3<f32>;
165 @location(2)
166 texcoord : vec2<f32>;
167 @location(3)
168 texcoord2 : vec2<f32>;
169 @location(4)
170 color : vec4<f32>;
171 @location(5)
172 instanceColor : vec4<f32>;
173 @location(6)
174 normal : vec3<f32>;
175 @location(7)
176 tangent : vec3<f32>;
177 @location(8)
178 bitangent : vec3<f32>;
179}
180
181struct Material {
182 baseColorFactor : vec4<f32>;
183 emissiveFactor : vec3<f32>;
184 occlusionStrength : f32;
185 metallicRoughnessFactor : vec2<f32>;
186 alphaCutoff : f32;
187}
188
189@binding(8) @group(0) var<uniform> material : Material;
190
191@binding(9) @group(0) var baseColorTexture : texture_2d<f32>;
192
193@binding(10) @group(0) var baseColorSampler : sampler;
194
195@binding(11) @group(0) var normalTexture : texture_2d<f32>;
196
197@binding(12) @group(0) var normalSampler : sampler;
198
199@binding(13) @group(0) var metallicRoughnessTexture : texture_2d<f32>;
200
201@binding(14) @group(0) var metallicRoughnessSampler : sampler;
202
203@binding(15) @group(0) var occlusionTexture : texture_2d<f32>;
204
205@binding(16) @group(0) var occlusionSampler : sampler;
206
207@binding(17) @group(0) var emissiveTexture : texture_2d<f32>;
208
209@binding(18) @group(0) var emissiveSampler : sampler;
210
211struct SurfaceInfo {
212 baseColor : vec4<f32>;
213 albedo : vec3<f32>;
214 metallic : f32;
215 roughness : f32;
216 normal : vec3<f32>;
217 f0 : vec3<f32>;
218 ao : f32;
219 emissive : vec3<f32>;
220 v : vec3<f32>;
221}
222
223fn GetSurfaceInfo(input : VertexOutput) -> SurfaceInfo {
224 var surface : SurfaceInfo;
225 surface.v = normalize(input.view);
226 let tbn = mat3x3(input.tangent, input.bitangent, input.normal);
227 let normalMap = textureSample(normalTexture, normalSampler, input.texcoord).rgb;
228 surface.normal = normalize((tbn * ((2.0 * normalMap) - vec3(1.0))));
229 let baseColorMap = textureSample(baseColorTexture, baseColorSampler, input.texcoord);
230 surface.baseColor = ((input.color * material.baseColorFactor) * baseColorMap);
231 if ((surface.baseColor.a < material.alphaCutoff)) {
232 discard;
233 }
234 surface.albedo = surface.baseColor.rgb;
235 let metallicRoughnessMap = textureSample(metallicRoughnessTexture, metallicRoughnessSampler, input.texcoord);
236 surface.metallic = (material.metallicRoughnessFactor.x * metallicRoughnessMap.b);
237 surface.roughness = (material.metallicRoughnessFactor.y * metallicRoughnessMap.g);
238 let dielectricSpec = vec3(0.039999999);
239 surface.f0 = mix(dielectricSpec, surface.albedo, vec3(surface.metallic));
240 let occlusionMap = textureSample(occlusionTexture, occlusionSampler, input.texcoord);
241 surface.ao = (material.occlusionStrength * occlusionMap.r);
242 let emissiveMap = textureSample(emissiveTexture, emissiveSampler, input.texcoord);
243 surface.emissive = (material.emissiveFactor * emissiveMap.rgb);
244 if ((input.instanceColor.a == 0.0)) {
245 surface.albedo = (surface.albedo + input.instanceColor.rgb);
246 } else {
247 surface.albedo = (surface.albedo * input.instanceColor.rgb);
248 }
249 return surface;
250}
251
252let PI = 3.141592741;
253
254let LightType_Point = 0u;
255
256let LightType_Spot = 1u;
257
258let LightType_Directional = 2u;
259
260struct PuctualLight {
261 lightType : u32;
262 pointToLight : vec3<f32>;
263 range : f32;
264 color : vec3<f32>;
265 intensity : f32;
266}
267
268fn FresnelSchlick(cosTheta : f32, F0 : vec3<f32>) -> vec3<f32> {
269 return (F0 + ((vec3(1.0) - F0) * pow((1.0 - cosTheta), 5.0)));
270}
271
272fn DistributionGGX(N : vec3<f32>, H : vec3<f32>, roughness : f32) -> f32 {
273 let a = (roughness * roughness);
274 let a2 = (a * a);
275 let NdotH = max(dot(N, H), 0.0);
276 let NdotH2 = (NdotH * NdotH);
277 let num = a2;
278 let denom = ((NdotH2 * (a2 - 1.0)) + 1.0);
279 return (num / ((PI * denom) * denom));
280}
281
282fn GeometrySchlickGGX(NdotV : f32, roughness : f32) -> f32 {
283 let r = (roughness + 1.0);
284 let k = ((r * r) / 8.0);
285 let num = NdotV;
286 let denom = ((NdotV * (1.0 - k)) + k);
287 return (num / denom);
288}
289
290fn GeometrySmith(N : vec3<f32>, V : vec3<f32>, L : vec3<f32>, roughness : f32) -> f32 {
291 let NdotV = max(dot(N, V), 0.0);
292 let NdotL = max(dot(N, L), 0.0);
293 let ggx2 = GeometrySchlickGGX(NdotV, roughness);
294 let ggx1 = GeometrySchlickGGX(NdotL, roughness);
295 return (ggx1 * ggx2);
296}
297
298fn lightAttenuation(light : PuctualLight) -> f32 {
299 if ((light.lightType == LightType_Directional)) {
300 return 1.0;
301 }
302 let distance = length(light.pointToLight);
303 if ((light.range <= 0.0)) {
304 return (1.0 / pow(distance, 2.0));
305 }
306 return (clamp((1.0 - pow((distance / light.range), 4.0)), 0.0, 1.0) / pow(distance, 2.0));
307}
308
309fn lightRadiance(light : PuctualLight, surface : SurfaceInfo) -> vec3<f32> {
310 let L = normalize(light.pointToLight);
311 let H = normalize((surface.v + L));
312 let NDF = DistributionGGX(surface.normal, H, surface.roughness);
313 let G = GeometrySmith(surface.normal, surface.v, L, surface.roughness);
314 let F = FresnelSchlick(max(dot(H, surface.v), 0.0), surface.f0);
315 let kD = ((vec3(1.0) - F) * (1.0 - surface.metallic));
316 let NdotL = max(dot(surface.normal, L), 0.0);
317 let numerator = ((NDF * G) * F);
318 let denominator = max(((4.0 * max(dot(surface.normal, surface.v), 0.0)) * NdotL), 0.001);
319 let specular = (numerator / vec3(denominator));
320 let radiance = ((light.color * light.intensity) * lightAttenuation(light));
321 return (((((kD * surface.albedo) / vec3(PI)) + specular) * radiance) * NdotL);
322}
323
324@binding(19) @group(0) var ssaoTexture : texture_2d<f32>;
325
326struct FragmentOutput {
327 @location(0)
328 color : vec4<f32>;
329 @location(1)
330 emissive : vec4<f32>;
331}
332
333@stage(fragment)
334fn fragmentMain(input : VertexOutput) -> FragmentOutput {
335 let surface = GetSurfaceInfo(input);
336 var Lo = vec3(0.0, 0.0, 0.0);
337 if ((globalLights.dirIntensity > 0.0)) {
338 var light : PuctualLight;
339 light.lightType = LightType_Directional;
340 light.pointToLight = globalLights.dirDirection;
341 light.color = globalLights.dirColor;
342 light.intensity = globalLights.dirIntensity;
343 let lightVis = dirLightVisibility(input.worldPos);
344 Lo = (Lo + (lightRadiance(light, surface) * lightVis));
345 }
346 let clusterIndex = getClusterIndex(input.position);
347 let lightOffset = clusterLights.lights[clusterIndex].offset;
348 let lightCount = clusterLights.lights[clusterIndex].count;
349 for(var lightIndex = 0u; (lightIndex < lightCount); lightIndex = (lightIndex + 1u)) {
350 let i = clusterLights.indices[(lightOffset + lightIndex)];
351 var light : PuctualLight;
352 light.lightType = LightType_Point;
353 light.pointToLight = (globalLights.lights[i].position.xyz - input.worldPos);
354 light.range = globalLights.lights[i].range;
355 light.color = globalLights.lights[i].color;
356 light.intensity = globalLights.lights[i].intensity;
357 let lightVis = pointLightVisibility(i, input.worldPos, light.pointToLight);
358 Lo = (Lo + (lightRadiance(light, surface) * lightVis));
359 }
360 let ssaoCoord = (input.position.xy / vec2<f32>(textureDimensions(ssaoTexture).xy));
361 let ssaoFactor = textureSample(ssaoTexture, defaultSampler, ssaoCoord).r;
362 let ambient = (((globalLights.ambient * surface.albedo) * surface.ao) * ssaoFactor);
363 let color = linearTosRGB(((Lo + ambient) + surface.emissive));
364 var out : FragmentOutput;
365 out.color = vec4(color, surface.baseColor.a);
366 out.emissive = vec4(surface.emissive, surface.baseColor.a);
367 return out;
368}