@luma.gl/shaders 10.0.0-alpha.10 → 10.0.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/dist.dev.js +1776 -58
  2. package/dist/dist.min.js +1394 -147
  3. package/dist/index.cjs +35 -0
  4. package/dist/index.cjs.map +3 -3
  5. package/dist/index.d.ts +17 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +9 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.cjs +40 -0
  10. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.d.ts +6 -0
  11. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.d.ts.map +1 -0
  12. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.js +15 -0
  13. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.js.map +1 -0
  14. package/dist/modules/lighting/contact-shadow/contact-shadow.cjs +348 -0
  15. package/dist/modules/lighting/contact-shadow/contact-shadow.d.ts +188 -0
  16. package/dist/modules/lighting/contact-shadow/contact-shadow.d.ts.map +1 -0
  17. package/dist/modules/lighting/contact-shadow/contact-shadow.js +307 -0
  18. package/dist/modules/lighting/contact-shadow/contact-shadow.js.map +1 -0
  19. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.cjs +87 -0
  20. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.d.ts +21 -0
  21. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.d.ts.map +1 -0
  22. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.js +61 -0
  23. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.js.map +1 -0
  24. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.cjs +87 -0
  25. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.d.ts +21 -0
  26. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.d.ts.map +1 -0
  27. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.js +61 -0
  28. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.js.map +1 -0
  29. package/dist/modules/lighting/pbr-material/pbr-material-glsl.cjs +33 -11
  30. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts +2 -2
  31. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts.map +1 -1
  32. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js +33 -11
  33. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js.map +1 -1
  34. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.cjs +33 -11
  35. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts +1 -1
  36. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts.map +1 -1
  37. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js +33 -11
  38. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js.map +1 -1
  39. package/dist/modules/lighting/pbr-material/pbr-material.d.ts +3 -3
  40. package/dist/modules/lighting/pbr-shadow/pbr-shadow.cjs +97 -0
  41. package/dist/modules/lighting/pbr-shadow/pbr-shadow.d.ts +15 -0
  42. package/dist/modules/lighting/pbr-shadow/pbr-shadow.d.ts.map +1 -0
  43. package/dist/modules/lighting/pbr-shadow/pbr-shadow.js +69 -0
  44. package/dist/modules/lighting/pbr-shadow/pbr-shadow.js.map +1 -0
  45. package/dist/modules/lighting/pbr-surface/pbr-surface.cjs +114 -0
  46. package/dist/modules/lighting/pbr-surface/pbr-surface.d.ts +15 -0
  47. package/dist/modules/lighting/pbr-surface/pbr-surface.d.ts.map +1 -0
  48. package/dist/modules/lighting/pbr-surface/pbr-surface.js +88 -0
  49. package/dist/modules/lighting/pbr-surface/pbr-surface.js.map +1 -0
  50. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.cjs +379 -0
  51. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.d.ts +41 -0
  52. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.d.ts.map +1 -0
  53. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.js +345 -0
  54. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.js.map +1 -0
  55. package/dist/modules/lighting/shadow/shadow-glsl.cjs +228 -0
  56. package/dist/modules/lighting/shadow/shadow-glsl.d.ts +3 -0
  57. package/dist/modules/lighting/shadow/shadow-glsl.d.ts.map +1 -0
  58. package/dist/modules/lighting/shadow/shadow-glsl.js +204 -0
  59. package/dist/modules/lighting/shadow/shadow-glsl.js.map +1 -0
  60. package/dist/modules/lighting/shadow/shadow-types.cjs +15 -0
  61. package/dist/modules/lighting/shadow/shadow-types.d.ts +63 -0
  62. package/dist/modules/lighting/shadow/shadow-types.d.ts.map +1 -0
  63. package/dist/modules/lighting/shadow/shadow-types.js +5 -0
  64. package/dist/modules/lighting/shadow/shadow-types.js.map +1 -0
  65. package/dist/modules/lighting/shadow/shadow.cjs +458 -0
  66. package/dist/modules/lighting/shadow/shadow.d.ts +117 -0
  67. package/dist/modules/lighting/shadow/shadow.d.ts.map +1 -0
  68. package/dist/modules/lighting/shadow/shadow.js +450 -0
  69. package/dist/modules/lighting/shadow/shadow.js.map +1 -0
  70. package/dist/modules/lighting/unlit-material/unlit-material.cjs +60 -0
  71. package/dist/modules/lighting/unlit-material/unlit-material.d.ts +9 -0
  72. package/dist/modules/lighting/unlit-material/unlit-material.d.ts.map +1 -0
  73. package/dist/modules/lighting/unlit-material/unlit-material.js +40 -0
  74. package/dist/modules/lighting/unlit-material/unlit-material.js.map +1 -0
  75. package/package.json +3 -3
  76. package/src/index.ts +48 -0
  77. package/src/modules/lighting/clustered-light-grid/clustered-light-grid.ts +17 -0
  78. package/src/modules/lighting/contact-shadow/contact-shadow.ts +365 -0
  79. package/src/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.ts +78 -0
  80. package/src/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.ts +78 -0
  81. package/src/modules/lighting/pbr-material/pbr-material-glsl.ts +33 -11
  82. package/src/modules/lighting/pbr-material/pbr-material-wgsl.ts +33 -11
  83. package/src/modules/lighting/pbr-shadow/pbr-shadow.ts +85 -0
  84. package/src/modules/lighting/pbr-surface/pbr-surface.ts +103 -0
  85. package/src/modules/lighting/prefiltered-environment/prefiltered-environment.ts +370 -0
  86. package/src/modules/lighting/shadow/shadow-glsl.ts +204 -0
  87. package/src/modules/lighting/shadow/shadow-types.ts +68 -0
  88. package/src/modules/lighting/shadow/shadow.ts +536 -0
  89. package/src/modules/lighting/unlit-material/unlit-material.ts +44 -0
@@ -0,0 +1,458 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var shadow_exports = {};
19
+ __export(shadow_exports, {
20
+ shadow: () => shadow
21
+ });
22
+ module.exports = __toCommonJS(shadow_exports);
23
+ var import_shadow_glsl = require("./shadow-glsl.cjs");
24
+ const MAX_CASCADES = 4;
25
+ const MAX_SPOT_LIGHTS = 4;
26
+ const MAX_POINT_LIGHTS = 4;
27
+ const MAX_POINT_MATRICES = 24;
28
+ const DIRECTIONAL_LIGHT_UNIFORM_TYPE = {
29
+ direction: "vec3<f32>",
30
+ strength: "f32",
31
+ normalBias: "f32",
32
+ sourceAngularRadius: "f32",
33
+ cascadeBlendFraction: "f32",
34
+ farFadeFraction: "f32",
35
+ shadowDistance: "f32"
36
+ };
37
+ const SPOT_LIGHT_UNIFORM_TYPE = {
38
+ position: "vec3<f32>",
39
+ range: "f32",
40
+ direction: "vec3<f32>",
41
+ outerConeCos: "f32",
42
+ sourceRadius: "f32",
43
+ normalBias: "f32",
44
+ strength: "f32",
45
+ nearPlane: "f32"
46
+ };
47
+ const POINT_LIGHT_UNIFORM_TYPE = {
48
+ position: "vec3<f32>",
49
+ range: "f32",
50
+ sourceRadius: "f32",
51
+ normalBias: "f32",
52
+ strength: "f32",
53
+ nearPlane: "f32"
54
+ };
55
+ const SHADOW_WGSL = (
56
+ /* wgsl */
57
+ `const SHADOW_PI: f32 = 3.141592653589793;
58
+
59
+ struct DirectionalShadowLightUniform {
60
+ direction: vec3f,
61
+ strength: f32,
62
+ normalBias: f32,
63
+ sourceAngularRadius: f32,
64
+ cascadeBlendFraction: f32,
65
+ farFadeFraction: f32,
66
+ shadowDistance: f32,
67
+ };
68
+
69
+ struct SpotShadowLightUniform {
70
+ position: vec3f,
71
+ range: f32,
72
+ direction: vec3f,
73
+ outerConeCos: f32,
74
+ sourceRadius: f32,
75
+ normalBias: f32,
76
+ strength: f32,
77
+ nearPlane: f32,
78
+ };
79
+
80
+ struct PointShadowLightUniform {
81
+ position: vec3f,
82
+ range: f32,
83
+ sourceRadius: f32,
84
+ normalBias: f32,
85
+ strength: f32,
86
+ nearPlane: f32,
87
+ };
88
+
89
+ struct ShadowUniforms {
90
+ directionalLightCount: i32,
91
+ spotLightCount: i32,
92
+ pointLightCount: i32,
93
+ cascadeCount: i32,
94
+ blockerSampleCount: i32,
95
+ filterSampleCount: i32,
96
+ cascadeSplits: vec4f,
97
+ directionalViewProjectionMatrices: array<mat4x4f, 4>,
98
+ spotViewProjectionMatrices: array<mat4x4f, 4>,
99
+ pointViewProjectionMatrices: array<mat4x4f, 24>,
100
+ directionalLights: array<DirectionalShadowLightUniform, 1>,
101
+ spotLights: array<SpotShadowLightUniform, 4>,
102
+ pointLights: array<PointShadowLightUniform, 4>,
103
+ };
104
+
105
+ @group(2) @binding(auto) var<uniform> shadow: ShadowUniforms;
106
+ @group(2) @binding(auto) var directionalShadowTexture: texture_depth_2d_array;
107
+ @group(2) @binding(auto) var spotShadowTexture: texture_depth_2d_array;
108
+ @group(2) @binding(auto) var pointShadowTexture: texture_depth_cube_array;
109
+ @group(2) @binding(auto) var directionalShadowTextureSampler: sampler;
110
+ @group(2) @binding(auto) var shadowComparisonSampler: sampler_comparison;
111
+
112
+ fn shadow_hash(position: vec3f) -> f32 {
113
+ return fract(sin(dot(position, vec3f(12.9898, 78.233, 37.719))) * 43758.5453);
114
+ }
115
+
116
+ fn shadow_diskSample(index: i32, count: i32, rotation: f32) -> vec2f {
117
+ let fraction = (f32(index) + 0.5) / max(f32(count), 1.0);
118
+ let angle = f32(index) * 2.39996323 + rotation;
119
+ return sqrt(fraction) * vec2f(cos(angle), sin(angle));
120
+ }
121
+
122
+ fn shadow_project(matrix: mat4x4f, worldPosition: vec3f) -> vec3f {
123
+ let clip = matrix * vec4f(worldPosition, 1.0);
124
+ let ndc = clip.xyz / max(abs(clip.w), 0.00001);
125
+ return vec3f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5, ndc.z);
126
+ }
127
+
128
+ fn shadow_validProjection(projected: vec3f) -> bool {
129
+ return all(projected.xy >= vec2f(0.0)) && all(projected.xy <= vec2f(1.0)) &&
130
+ projected.z >= 0.0 && projected.z <= 1.0;
131
+ }
132
+
133
+ fn shadow_directionalCascadeIndex(viewDepth: f32) -> i32 {
134
+ var cascadeIndex = 0;
135
+ for (var index: i32 = 0; index < 4; index++) {
136
+ if (index >= shadow.cascadeCount) { break; }
137
+ cascadeIndex = index;
138
+ if (viewDepth <= shadow.cascadeSplits[index]) { break; }
139
+ }
140
+ return cascadeIndex;
141
+ }
142
+
143
+ fn shadow_getDirectionalCascadeIndex(viewDepth: f32) -> i32 {
144
+ if (shadow.directionalLightCount == 0) { return -1; }
145
+ return shadow_directionalCascadeIndex(viewDepth);
146
+ }
147
+
148
+ fn shadow_directionalPCSS(cascadeIndex: i32, worldPosition: vec3f, viewDepth: f32) -> f32 {
149
+ let projected = shadow_project(shadow.directionalViewProjectionMatrices[cascadeIndex], worldPosition);
150
+ if (!shadow_validProjection(projected)) { return 1.0; }
151
+ let dimensions = vec2f(textureDimensions(directionalShadowTexture));
152
+ let texel = 1.0 / dimensions;
153
+ let light = shadow.directionalLights[0];
154
+ let rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;
155
+ let searchRadius = max(texel.x, light.sourceAngularRadius * viewDepth / max(light.shadowDistance, 0.001));
156
+ var blockerDepth = 0.0;
157
+ var blockerCount = 0.0;
158
+ for (var index: i32 = 0; index < 24; index++) {
159
+ if (index >= shadow.blockerSampleCount) { break; }
160
+ let offset = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;
161
+ let sampleDepth = textureSampleLevel(
162
+ directionalShadowTexture,
163
+ directionalShadowTextureSampler,
164
+ clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),
165
+ cascadeIndex,
166
+ 0
167
+ );
168
+ if (sampleDepth < projected.z) {
169
+ blockerDepth += sampleDepth;
170
+ blockerCount += 1.0;
171
+ }
172
+ }
173
+ if (blockerCount == 0.0) { return 1.0; }
174
+ let averageBlocker = blockerDepth / blockerCount;
175
+ let penumbra = clamp((projected.z - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
176
+ let filterRadius = max(texel.x, searchRadius * (1.0 + penumbra * 18.0));
177
+ var visibility = 0.0;
178
+ for (var index: i32 = 0; index < 48; index++) {
179
+ if (index >= shadow.filterSampleCount) { break; }
180
+ let offset = shadow_diskSample(index, shadow.filterSampleCount, rotation) * filterRadius;
181
+ visibility += textureSampleCompareLevel(
182
+ directionalShadowTexture,
183
+ shadowComparisonSampler,
184
+ clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),
185
+ cascadeIndex,
186
+ projected.z
187
+ );
188
+ }
189
+ return visibility / max(f32(shadow.filterSampleCount), 1.0);
190
+ }
191
+
192
+ fn shadow_getDirectionalFactor(worldPosition: vec3f, worldNormal: vec3f, viewDepth: f32) -> f32 {
193
+ if (shadow.directionalLightCount == 0 || viewDepth <= 0.0) { return 1.0; }
194
+ let light = shadow.directionalLights[0];
195
+ if (viewDepth >= light.shadowDistance) { return 1.0; }
196
+ let biasedPosition = worldPosition + normalize(worldNormal) * light.normalBias;
197
+ let cascadeIndex = shadow_directionalCascadeIndex(viewDepth);
198
+ var visibility = shadow_directionalPCSS(cascadeIndex, biasedPosition, viewDepth);
199
+ if (cascadeIndex + 1 < shadow.cascadeCount) {
200
+ var cascadeStart = 0.0;
201
+ if (cascadeIndex > 0) { cascadeStart = shadow.cascadeSplits[cascadeIndex - 1]; }
202
+ let cascadeEnd = shadow.cascadeSplits[cascadeIndex];
203
+ let blendWidth = max((cascadeEnd - cascadeStart) * light.cascadeBlendFraction, 0.0001);
204
+ let blend = smoothstep(cascadeEnd - blendWidth, cascadeEnd, viewDepth);
205
+ if (blend > 0.0) {
206
+ visibility = mix(
207
+ visibility,
208
+ shadow_directionalPCSS(cascadeIndex + 1, biasedPosition, viewDepth),
209
+ blend
210
+ );
211
+ }
212
+ }
213
+ let fadeWidth = max(light.shadowDistance * light.farFadeFraction, 0.0001);
214
+ let farFade = smoothstep(light.shadowDistance - fadeWidth, light.shadowDistance, viewDepth);
215
+ visibility = mix(visibility, 1.0, farFade);
216
+ return mix(1.0, visibility, light.strength);
217
+ }
218
+
219
+ fn shadow_spotPCSS(lightIndex: i32, worldPosition: vec3f) -> f32 {
220
+ let projected = shadow_project(shadow.spotViewProjectionMatrices[lightIndex], worldPosition);
221
+ if (!shadow_validProjection(projected)) { return 1.0; }
222
+ let light = shadow.spotLights[lightIndex];
223
+ let dimensions = vec2f(textureDimensions(spotShadowTexture));
224
+ let texel = 1.0 / dimensions;
225
+ let distanceToLight = length(worldPosition - light.position);
226
+ let searchRadius = max(texel.x, light.sourceRadius / max(distanceToLight, 0.001) * 0.5);
227
+ let rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;
228
+ var blockerDepth = 0.0;
229
+ var blockerCount = 0.0;
230
+ for (var index: i32 = 0; index < 24; index++) {
231
+ if (index >= shadow.blockerSampleCount) { break; }
232
+ let offset = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;
233
+ let sampleDepth = textureSampleLevel(
234
+ spotShadowTexture,
235
+ directionalShadowTextureSampler,
236
+ clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),
237
+ lightIndex,
238
+ 0
239
+ );
240
+ if (sampleDepth < projected.z) { blockerDepth += sampleDepth; blockerCount += 1.0; }
241
+ }
242
+ if (blockerCount == 0.0) { return 1.0; }
243
+ let averageBlocker = blockerDepth / blockerCount;
244
+ let penumbra = clamp((projected.z - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
245
+ let filterRadius = max(texel.x, searchRadius * (1.0 + penumbra * 16.0));
246
+ var visibility = 0.0;
247
+ for (var index: i32 = 0; index < 48; index++) {
248
+ if (index >= shadow.filterSampleCount) { break; }
249
+ let offset = shadow_diskSample(index, shadow.filterSampleCount, rotation) * filterRadius;
250
+ visibility += textureSampleCompareLevel(
251
+ spotShadowTexture,
252
+ shadowComparisonSampler,
253
+ clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),
254
+ lightIndex,
255
+ projected.z
256
+ );
257
+ }
258
+ return visibility / max(f32(shadow.filterSampleCount), 1.0);
259
+ }
260
+
261
+ fn shadow_getSpotFactor(lightIndex: i32, worldPosition: vec3f, worldNormal: vec3f) -> f32 {
262
+ if (lightIndex < 0 || lightIndex >= shadow.spotLightCount) { return 1.0; }
263
+ let light = shadow.spotLights[lightIndex];
264
+ let fromLight = worldPosition - light.position;
265
+ let distanceToLight = length(fromLight);
266
+ let coneCos = dot(normalize(fromLight), normalize(light.direction));
267
+ if (distanceToLight >= light.range || coneCos < light.outerConeCos) { return 1.0; }
268
+ let biasedPosition = worldPosition + normalize(worldNormal) * light.normalBias;
269
+ return mix(1.0, shadow_spotPCSS(lightIndex, biasedPosition), light.strength);
270
+ }
271
+
272
+ fn shadow_pointReferenceDepth(majorDistance: f32, nearPlane: f32, farPlane: f32) -> f32 {
273
+ // Match the cube-face projection and stored WebGPU depth in [0, 1].
274
+ return farPlane / (farPlane - nearPlane) -
275
+ (farPlane * nearPlane) / ((farPlane - nearPlane) * majorDistance);
276
+ }
277
+
278
+ fn shadow_pointBasis(direction: vec3f) -> mat2x3f {
279
+ let reference = select(vec3f(0.0, 1.0, 0.0), vec3f(0.0, 0.0, 1.0), abs(direction.y) > 0.99);
280
+ let tangent = normalize(cross(direction, reference));
281
+ return mat2x3f(tangent, normalize(cross(direction, tangent)));
282
+ }
283
+
284
+ fn shadow_getPointFactor(lightIndex: i32, worldPosition: vec3f, worldNormal: vec3f) -> f32 {
285
+ if (lightIndex < 0 || lightIndex >= shadow.pointLightCount) { return 1.0; }
286
+ let light = shadow.pointLights[lightIndex];
287
+ let biasedPosition = worldPosition + normalize(worldNormal) * light.normalBias;
288
+ let fromLight = biasedPosition - light.position;
289
+ let distanceToLight = length(fromLight);
290
+ if (distanceToLight >= light.range || distanceToLight <= light.nearPlane) { return 1.0; }
291
+ let direction = normalize(fromLight);
292
+ let majorDistance = max(max(abs(fromLight.x), abs(fromLight.y)), abs(fromLight.z));
293
+ let referenceDepth = shadow_pointReferenceDepth(majorDistance, light.nearPlane, light.range);
294
+ let searchRadius = light.sourceRadius / max(distanceToLight, 0.001);
295
+ let rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;
296
+ let basis = shadow_pointBasis(direction);
297
+ var blockerDepth = 0.0;
298
+ var blockerCount = 0.0;
299
+ for (var index: i32 = 0; index < 24; index++) {
300
+ if (index >= shadow.blockerSampleCount) { break; }
301
+ let disk = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;
302
+ let sampleDirection = normalize(direction + basis[0] * disk.x + basis[1] * disk.y);
303
+ let sampleDepth = textureSampleLevel(
304
+ pointShadowTexture,
305
+ directionalShadowTextureSampler,
306
+ sampleDirection,
307
+ lightIndex,
308
+ 0
309
+ );
310
+ if (sampleDepth < referenceDepth) { blockerDepth += sampleDepth; blockerCount += 1.0; }
311
+ }
312
+ if (blockerCount == 0.0) { return 1.0; }
313
+ let averageBlocker = blockerDepth / blockerCount;
314
+ let penumbra = clamp((referenceDepth - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
315
+ let filterRadius = searchRadius * (1.0 + penumbra * 12.0);
316
+ var visibility = 0.0;
317
+ for (var index: i32 = 0; index < 48; index++) {
318
+ if (index >= shadow.filterSampleCount) { break; }
319
+ let disk = shadow_diskSample(index, shadow.filterSampleCount, rotation) * filterRadius;
320
+ let sampleDirection = normalize(direction + basis[0] * disk.x + basis[1] * disk.y);
321
+ visibility += textureSampleCompareLevel(
322
+ pointShadowTexture,
323
+ shadowComparisonSampler,
324
+ sampleDirection,
325
+ lightIndex,
326
+ referenceDepth
327
+ );
328
+ }
329
+ visibility /= max(f32(shadow.filterSampleCount), 1.0);
330
+ return mix(1.0, visibility, light.strength);
331
+ }
332
+ `
333
+ );
334
+ const shadow = {
335
+ name: "shadow",
336
+ source: SHADOW_WGSL,
337
+ fs: import_shadow_glsl.SHADOW_GLSL,
338
+ props: {},
339
+ uniforms: {},
340
+ bindings: {},
341
+ uniformTypes: {
342
+ directionalLightCount: "i32",
343
+ spotLightCount: "i32",
344
+ pointLightCount: "i32",
345
+ cascadeCount: "i32",
346
+ blockerSampleCount: "i32",
347
+ filterSampleCount: "i32",
348
+ cascadeSplits: "vec4<f32>",
349
+ directionalViewProjectionMatrices: ["mat4x4<f32>", MAX_CASCADES],
350
+ spotViewProjectionMatrices: ["mat4x4<f32>", MAX_SPOT_LIGHTS],
351
+ pointViewProjectionMatrices: ["mat4x4<f32>", MAX_POINT_MATRICES],
352
+ directionalLights: [DIRECTIONAL_LIGHT_UNIFORM_TYPE, 1],
353
+ spotLights: [SPOT_LIGHT_UNIFORM_TYPE, MAX_SPOT_LIGHTS],
354
+ pointLights: [POINT_LIGHT_UNIFORM_TYPE, MAX_POINT_LIGHTS]
355
+ },
356
+ bindingLayout: [
357
+ { name: "shadow", group: 2 },
358
+ { name: "directionalShadowTexture", group: 2 },
359
+ { name: "spotShadowTexture", group: 2 },
360
+ { name: "pointShadowTexture", group: 2 },
361
+ { name: "shadowComparisonSampler", group: 2 }
362
+ ],
363
+ getUniforms(props) {
364
+ if (!props.directionalLights || !props.spotLights || !props.pointLights || !props.cascadeSplits || !props.directionalViewProjectionMatrices || !props.spotViewProjectionMatrices || !props.pointViewProjectionMatrices || !props.directionalShadowTexture || !props.spotShadowTexture || !props.pointShadowTexture || !props.nonFilteringSampler || !props.comparisonSampler) {
365
+ return {};
366
+ }
367
+ return {
368
+ directionalLightCount: props.directionalLights.length,
369
+ spotLightCount: props.spotLights.length,
370
+ pointLightCount: props.pointLights.length,
371
+ cascadeCount: props.cascadeCount,
372
+ blockerSampleCount: props.blockerSampleCount,
373
+ filterSampleCount: props.filterSampleCount,
374
+ cascadeSplits: props.cascadeSplits,
375
+ directionalViewProjectionMatrices: props.directionalViewProjectionMatrices,
376
+ spotViewProjectionMatrices: props.spotViewProjectionMatrices,
377
+ pointViewProjectionMatrices: props.pointViewProjectionMatrices,
378
+ directionalLights: padDirectionalLights(props.directionalLights),
379
+ spotLights: padSpotLights(props.spotLights),
380
+ pointLights: padPointLights(props.pointLights),
381
+ directionalShadowTexture: props.directionalShadowTexture,
382
+ spotShadowTexture: props.spotShadowTexture,
383
+ pointShadowTexture: props.pointShadowTexture,
384
+ ...props.directionalShadowTexture.device.type === "webgpu" ? { shadowComparisonSampler: props.comparisonSampler } : {}
385
+ };
386
+ }
387
+ };
388
+ function padDirectionalLights(lights) {
389
+ const light = lights[0];
390
+ return [
391
+ light ? {
392
+ direction: light.direction,
393
+ strength: light.strength ?? 1,
394
+ normalBias: light.normalBias ?? 0.04,
395
+ sourceAngularRadius: light.sourceAngularRadius ?? 465e-5,
396
+ cascadeBlendFraction: light.cascadeBlendFraction ?? 0.1,
397
+ farFadeFraction: light.farFadeFraction ?? 0.1,
398
+ shadowDistance: light.shadowDistance ?? 1
399
+ } : {
400
+ direction: [0, 1, 0],
401
+ strength: 0,
402
+ normalBias: 0,
403
+ sourceAngularRadius: 0,
404
+ cascadeBlendFraction: 0,
405
+ farFadeFraction: 0,
406
+ shadowDistance: 1
407
+ }
408
+ ];
409
+ }
410
+ function padSpotLights(lights) {
411
+ return padArray(lights.map((light) => ({
412
+ position: light.position,
413
+ range: light.range,
414
+ direction: light.direction,
415
+ outerConeCos: Math.cos(light.outerConeAngle),
416
+ sourceRadius: light.sourceRadius ?? 0.2,
417
+ normalBias: light.normalBias ?? 0.025,
418
+ strength: light.strength ?? 1,
419
+ nearPlane: light.nearPlane ?? 0.1
420
+ })), MAX_SPOT_LIGHTS, {
421
+ position: [0, 0, 0],
422
+ range: 1,
423
+ direction: [0, -1, 0],
424
+ outerConeCos: 1,
425
+ sourceRadius: 0,
426
+ normalBias: 0,
427
+ strength: 0,
428
+ nearPlane: 0.1
429
+ });
430
+ }
431
+ function padPointLights(lights) {
432
+ return padArray(lights.map((light) => ({
433
+ position: light.position,
434
+ range: light.range,
435
+ sourceRadius: light.sourceRadius ?? 0.2,
436
+ normalBias: light.normalBias ?? 0.025,
437
+ strength: light.strength ?? 1,
438
+ nearPlane: light.nearPlane ?? 0.1
439
+ })), MAX_POINT_LIGHTS, {
440
+ position: [0, 0, 0],
441
+ range: 1,
442
+ sourceRadius: 0,
443
+ normalBias: 0,
444
+ strength: 0,
445
+ nearPlane: 0.1
446
+ });
447
+ }
448
+ function padArray(values, length, fallback) {
449
+ const result = values.slice(0, length);
450
+ while (result.length < length) {
451
+ result.push(fallback);
452
+ }
453
+ return result;
454
+ }
455
+ // Annotate the CommonJS export names for ESM import in node:
456
+ 0 && (module.exports = {
457
+ shadow
458
+ });
@@ -0,0 +1,117 @@
1
+ import type { NumberArray3, NumberArray4, NumberArray16 } from '@math.gl/core';
2
+ import type { ShadowProps } from "./shadow-types.js";
3
+ type DirectionalShadowUniform = {
4
+ direction: Readonly<NumberArray3>;
5
+ strength: number;
6
+ normalBias: number;
7
+ sourceAngularRadius: number;
8
+ cascadeBlendFraction: number;
9
+ farFadeFraction: number;
10
+ shadowDistance: number;
11
+ };
12
+ type SpotShadowUniform = {
13
+ position: Readonly<NumberArray3>;
14
+ range: number;
15
+ direction: Readonly<NumberArray3>;
16
+ outerConeCos: number;
17
+ sourceRadius: number;
18
+ normalBias: number;
19
+ strength: number;
20
+ nearPlane: number;
21
+ };
22
+ type PointShadowUniform = {
23
+ position: Readonly<NumberArray3>;
24
+ range: number;
25
+ sourceRadius: number;
26
+ normalBias: number;
27
+ strength: number;
28
+ nearPlane: number;
29
+ };
30
+ export type ShadowUniforms = {
31
+ directionalLightCount: number;
32
+ spotLightCount: number;
33
+ pointLightCount: number;
34
+ cascadeCount: number;
35
+ blockerSampleCount: number;
36
+ filterSampleCount: number;
37
+ cascadeSplits: Readonly<NumberArray4>;
38
+ directionalViewProjectionMatrices: readonly Readonly<NumberArray16>[];
39
+ spotViewProjectionMatrices: readonly Readonly<NumberArray16>[];
40
+ pointViewProjectionMatrices: readonly Readonly<NumberArray16>[];
41
+ directionalLights: readonly DirectionalShadowUniform[];
42
+ spotLights: readonly SpotShadowUniform[];
43
+ pointLights: readonly PointShadowUniform[];
44
+ };
45
+ export type ShadowBindings<TextureT = unknown, SamplerT = unknown> = {
46
+ directionalShadowTexture: TextureT;
47
+ spotShadowTexture: TextureT;
48
+ pointShadowTexture: TextureT;
49
+ shadowComparisonSampler?: SamplerT;
50
+ };
51
+ /** Backend-aware shadow sampling module. Applications explicitly apply factors to direct-light terms. */
52
+ export declare const shadow: {
53
+ readonly name: "shadow";
54
+ readonly source: "const SHADOW_PI: f32 = 3.141592653589793;\n\nstruct DirectionalShadowLightUniform {\n direction: vec3f,\n strength: f32,\n normalBias: f32,\n sourceAngularRadius: f32,\n cascadeBlendFraction: f32,\n farFadeFraction: f32,\n shadowDistance: f32,\n};\n\nstruct SpotShadowLightUniform {\n position: vec3f,\n range: f32,\n direction: vec3f,\n outerConeCos: f32,\n sourceRadius: f32,\n normalBias: f32,\n strength: f32,\n nearPlane: f32,\n};\n\nstruct PointShadowLightUniform {\n position: vec3f,\n range: f32,\n sourceRadius: f32,\n normalBias: f32,\n strength: f32,\n nearPlane: f32,\n};\n\nstruct ShadowUniforms {\n directionalLightCount: i32,\n spotLightCount: i32,\n pointLightCount: i32,\n cascadeCount: i32,\n blockerSampleCount: i32,\n filterSampleCount: i32,\n cascadeSplits: vec4f,\n directionalViewProjectionMatrices: array<mat4x4f, 4>,\n spotViewProjectionMatrices: array<mat4x4f, 4>,\n pointViewProjectionMatrices: array<mat4x4f, 24>,\n directionalLights: array<DirectionalShadowLightUniform, 1>,\n spotLights: array<SpotShadowLightUniform, 4>,\n pointLights: array<PointShadowLightUniform, 4>,\n};\n\n@group(2) @binding(auto) var<uniform> shadow: ShadowUniforms;\n@group(2) @binding(auto) var directionalShadowTexture: texture_depth_2d_array;\n@group(2) @binding(auto) var spotShadowTexture: texture_depth_2d_array;\n@group(2) @binding(auto) var pointShadowTexture: texture_depth_cube_array;\n@group(2) @binding(auto) var directionalShadowTextureSampler: sampler;\n@group(2) @binding(auto) var shadowComparisonSampler: sampler_comparison;\n\nfn shadow_hash(position: vec3f) -> f32 {\n return fract(sin(dot(position, vec3f(12.9898, 78.233, 37.719))) * 43758.5453);\n}\n\nfn shadow_diskSample(index: i32, count: i32, rotation: f32) -> vec2f {\n let fraction = (f32(index) + 0.5) / max(f32(count), 1.0);\n let angle = f32(index) * 2.39996323 + rotation;\n return sqrt(fraction) * vec2f(cos(angle), sin(angle));\n}\n\nfn shadow_project(matrix: mat4x4f, worldPosition: vec3f) -> vec3f {\n let clip = matrix * vec4f(worldPosition, 1.0);\n let ndc = clip.xyz / max(abs(clip.w), 0.00001);\n return vec3f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5, ndc.z);\n}\n\nfn shadow_validProjection(projected: vec3f) -> bool {\n return all(projected.xy >= vec2f(0.0)) && all(projected.xy <= vec2f(1.0)) &&\n projected.z >= 0.0 && projected.z <= 1.0;\n}\n\nfn shadow_directionalCascadeIndex(viewDepth: f32) -> i32 {\n var cascadeIndex = 0;\n for (var index: i32 = 0; index < 4; index++) {\n if (index >= shadow.cascadeCount) { break; }\n cascadeIndex = index;\n if (viewDepth <= shadow.cascadeSplits[index]) { break; }\n }\n return cascadeIndex;\n}\n\nfn shadow_getDirectionalCascadeIndex(viewDepth: f32) -> i32 {\n if (shadow.directionalLightCount == 0) { return -1; }\n return shadow_directionalCascadeIndex(viewDepth);\n}\n\nfn shadow_directionalPCSS(cascadeIndex: i32, worldPosition: vec3f, viewDepth: f32) -> f32 {\n let projected = shadow_project(shadow.directionalViewProjectionMatrices[cascadeIndex], worldPosition);\n if (!shadow_validProjection(projected)) { return 1.0; }\n let dimensions = vec2f(textureDimensions(directionalShadowTexture));\n let texel = 1.0 / dimensions;\n let light = shadow.directionalLights[0];\n let rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;\n let searchRadius = max(texel.x, light.sourceAngularRadius * viewDepth / max(light.shadowDistance, 0.001));\n var blockerDepth = 0.0;\n var blockerCount = 0.0;\n for (var index: i32 = 0; index < 24; index++) {\n if (index >= shadow.blockerSampleCount) { break; }\n let offset = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;\n let sampleDepth = textureSampleLevel(\n directionalShadowTexture,\n directionalShadowTextureSampler,\n clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),\n cascadeIndex,\n 0\n );\n if (sampleDepth < projected.z) {\n blockerDepth += sampleDepth;\n blockerCount += 1.0;\n }\n }\n if (blockerCount == 0.0) { return 1.0; }\n let averageBlocker = blockerDepth / blockerCount;\n let penumbra = clamp((projected.z - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);\n let filterRadius = max(texel.x, searchRadius * (1.0 + penumbra * 18.0));\n var visibility = 0.0;\n for (var index: i32 = 0; index < 48; index++) {\n if (index >= shadow.filterSampleCount) { break; }\n let offset = shadow_diskSample(index, shadow.filterSampleCount, rotation) * filterRadius;\n visibility += textureSampleCompareLevel(\n directionalShadowTexture,\n shadowComparisonSampler,\n clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),\n cascadeIndex,\n projected.z\n );\n }\n return visibility / max(f32(shadow.filterSampleCount), 1.0);\n}\n\nfn shadow_getDirectionalFactor(worldPosition: vec3f, worldNormal: vec3f, viewDepth: f32) -> f32 {\n if (shadow.directionalLightCount == 0 || viewDepth <= 0.0) { return 1.0; }\n let light = shadow.directionalLights[0];\n if (viewDepth >= light.shadowDistance) { return 1.0; }\n let biasedPosition = worldPosition + normalize(worldNormal) * light.normalBias;\n let cascadeIndex = shadow_directionalCascadeIndex(viewDepth);\n var visibility = shadow_directionalPCSS(cascadeIndex, biasedPosition, viewDepth);\n if (cascadeIndex + 1 < shadow.cascadeCount) {\n var cascadeStart = 0.0;\n if (cascadeIndex > 0) { cascadeStart = shadow.cascadeSplits[cascadeIndex - 1]; }\n let cascadeEnd = shadow.cascadeSplits[cascadeIndex];\n let blendWidth = max((cascadeEnd - cascadeStart) * light.cascadeBlendFraction, 0.0001);\n let blend = smoothstep(cascadeEnd - blendWidth, cascadeEnd, viewDepth);\n if (blend > 0.0) {\n visibility = mix(\n visibility,\n shadow_directionalPCSS(cascadeIndex + 1, biasedPosition, viewDepth),\n blend\n );\n }\n }\n let fadeWidth = max(light.shadowDistance * light.farFadeFraction, 0.0001);\n let farFade = smoothstep(light.shadowDistance - fadeWidth, light.shadowDistance, viewDepth);\n visibility = mix(visibility, 1.0, farFade);\n return mix(1.0, visibility, light.strength);\n}\n\nfn shadow_spotPCSS(lightIndex: i32, worldPosition: vec3f) -> f32 {\n let projected = shadow_project(shadow.spotViewProjectionMatrices[lightIndex], worldPosition);\n if (!shadow_validProjection(projected)) { return 1.0; }\n let light = shadow.spotLights[lightIndex];\n let dimensions = vec2f(textureDimensions(spotShadowTexture));\n let texel = 1.0 / dimensions;\n let distanceToLight = length(worldPosition - light.position);\n let searchRadius = max(texel.x, light.sourceRadius / max(distanceToLight, 0.001) * 0.5);\n let rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;\n var blockerDepth = 0.0;\n var blockerCount = 0.0;\n for (var index: i32 = 0; index < 24; index++) {\n if (index >= shadow.blockerSampleCount) { break; }\n let offset = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;\n let sampleDepth = textureSampleLevel(\n spotShadowTexture,\n directionalShadowTextureSampler,\n clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),\n lightIndex,\n 0\n );\n if (sampleDepth < projected.z) { blockerDepth += sampleDepth; blockerCount += 1.0; }\n }\n if (blockerCount == 0.0) { return 1.0; }\n let averageBlocker = blockerDepth / blockerCount;\n let penumbra = clamp((projected.z - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);\n let filterRadius = max(texel.x, searchRadius * (1.0 + penumbra * 16.0));\n var visibility = 0.0;\n for (var index: i32 = 0; index < 48; index++) {\n if (index >= shadow.filterSampleCount) { break; }\n let offset = shadow_diskSample(index, shadow.filterSampleCount, rotation) * filterRadius;\n visibility += textureSampleCompareLevel(\n spotShadowTexture,\n shadowComparisonSampler,\n clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),\n lightIndex,\n projected.z\n );\n }\n return visibility / max(f32(shadow.filterSampleCount), 1.0);\n}\n\nfn shadow_getSpotFactor(lightIndex: i32, worldPosition: vec3f, worldNormal: vec3f) -> f32 {\n if (lightIndex < 0 || lightIndex >= shadow.spotLightCount) { return 1.0; }\n let light = shadow.spotLights[lightIndex];\n let fromLight = worldPosition - light.position;\n let distanceToLight = length(fromLight);\n let coneCos = dot(normalize(fromLight), normalize(light.direction));\n if (distanceToLight >= light.range || coneCos < light.outerConeCos) { return 1.0; }\n let biasedPosition = worldPosition + normalize(worldNormal) * light.normalBias;\n return mix(1.0, shadow_spotPCSS(lightIndex, biasedPosition), light.strength);\n}\n\nfn shadow_pointReferenceDepth(majorDistance: f32, nearPlane: f32, farPlane: f32) -> f32 {\n // Match the cube-face projection and stored WebGPU depth in [0, 1].\n return farPlane / (farPlane - nearPlane) -\n (farPlane * nearPlane) / ((farPlane - nearPlane) * majorDistance);\n}\n\nfn shadow_pointBasis(direction: vec3f) -> mat2x3f {\n let reference = select(vec3f(0.0, 1.0, 0.0), vec3f(0.0, 0.0, 1.0), abs(direction.y) > 0.99);\n let tangent = normalize(cross(direction, reference));\n return mat2x3f(tangent, normalize(cross(direction, tangent)));\n}\n\nfn shadow_getPointFactor(lightIndex: i32, worldPosition: vec3f, worldNormal: vec3f) -> f32 {\n if (lightIndex < 0 || lightIndex >= shadow.pointLightCount) { return 1.0; }\n let light = shadow.pointLights[lightIndex];\n let biasedPosition = worldPosition + normalize(worldNormal) * light.normalBias;\n let fromLight = biasedPosition - light.position;\n let distanceToLight = length(fromLight);\n if (distanceToLight >= light.range || distanceToLight <= light.nearPlane) { return 1.0; }\n let direction = normalize(fromLight);\n let majorDistance = max(max(abs(fromLight.x), abs(fromLight.y)), abs(fromLight.z));\n let referenceDepth = shadow_pointReferenceDepth(majorDistance, light.nearPlane, light.range);\n let searchRadius = light.sourceRadius / max(distanceToLight, 0.001);\n let rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;\n let basis = shadow_pointBasis(direction);\n var blockerDepth = 0.0;\n var blockerCount = 0.0;\n for (var index: i32 = 0; index < 24; index++) {\n if (index >= shadow.blockerSampleCount) { break; }\n let disk = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;\n let sampleDirection = normalize(direction + basis[0] * disk.x + basis[1] * disk.y);\n let sampleDepth = textureSampleLevel(\n pointShadowTexture,\n directionalShadowTextureSampler,\n sampleDirection,\n lightIndex,\n 0\n );\n if (sampleDepth < referenceDepth) { blockerDepth += sampleDepth; blockerCount += 1.0; }\n }\n if (blockerCount == 0.0) { return 1.0; }\n let averageBlocker = blockerDepth / blockerCount;\n let penumbra = clamp((referenceDepth - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);\n let filterRadius = searchRadius * (1.0 + penumbra * 12.0);\n var visibility = 0.0;\n for (var index: i32 = 0; index < 48; index++) {\n if (index >= shadow.filterSampleCount) { break; }\n let disk = shadow_diskSample(index, shadow.filterSampleCount, rotation) * filterRadius;\n let sampleDirection = normalize(direction + basis[0] * disk.x + basis[1] * disk.y);\n visibility += textureSampleCompareLevel(\n pointShadowTexture,\n shadowComparisonSampler,\n sampleDirection,\n lightIndex,\n referenceDepth\n );\n }\n visibility /= max(f32(shadow.filterSampleCount), 1.0);\n return mix(1.0, visibility, light.strength);\n}\n";
55
+ readonly fs: "\nprecision highp sampler2DArray;\nstruct DirectionalShadowLightUniform {\n vec3 direction;\n float strength;\n float normalBias;\n float sourceAngularRadius;\n float cascadeBlendFraction;\n float farFadeFraction;\n float shadowDistance;\n};\nstruct SpotShadowLightUniform {\n vec3 position;\n float range;\n vec3 direction;\n float outerConeCos;\n float sourceRadius;\n float normalBias;\n float strength;\n float nearPlane;\n};\nstruct PointShadowLightUniform {\n vec3 position;\n float range;\n float sourceRadius;\n float normalBias;\n float strength;\n float nearPlane;\n};\nlayout(std140) uniform shadowUniforms {\n int directionalLightCount;\n int spotLightCount;\n int pointLightCount;\n int cascadeCount;\n int blockerSampleCount;\n int filterSampleCount;\n vec4 cascadeSplits;\n mat4 directionalViewProjectionMatrices[4];\n mat4 spotViewProjectionMatrices[4];\n mat4 pointViewProjectionMatrices[24];\n DirectionalShadowLightUniform directionalLights[1];\n SpotShadowLightUniform spotLights[4];\n PointShadowLightUniform pointLights[4];\n} shadow;\nuniform sampler2DArray directionalShadowTexture;\nuniform sampler2DArray spotShadowTexture;\nuniform sampler2DArray pointShadowTexture;\nconst float SHADOW_PI = 3.141592653589793;\nfloat shadow_hash(vec3 position) {\n return fract(sin(dot(position, vec3(12.9898, 78.233, 37.719))) * 43758.5453);\n}\nvec2 shadow_diskSample(int index, int count, float rotation) {\n float fraction = (float(index) + 0.5) / max(float(count), 1.0);\n float angle = float(index) * 2.39996323 + rotation;\n return sqrt(fraction) * vec2(cos(angle), sin(angle));\n}\nvec3 shadow_project(mat4 matrix, vec3 worldPosition) {\n vec4 clip = matrix * vec4(worldPosition, 1.0);\n vec3 projected = clip.xyz / max(abs(clip.w), 0.00001);\n // Shared matrices retain [0,1] depth; WebGL texture rows start at the bottom.\n return vec3(projected.xy * 0.5 + 0.5, projected.z);\n}\nbool shadow_validProjection(vec3 projected) {\n return all(greaterThanEqual(projected, vec3(0.0))) && all(lessThanEqual(projected, vec3(1.0)));\n}\n// Bilinear interpolation of four depth comparisons matches a hardware 2x2 PCF lookup.\nfloat shadow_compare(sampler2DArray shadowMap, vec2 coordinate, int layer, float reference) {\n ivec2 dimensions = textureSize(shadowMap, 0).xy;\n vec2 position = coordinate * vec2(dimensions) - 0.5;\n ivec2 base = ivec2(floor(position));\n vec2 fraction = fract(position);\n ivec2 maximum = dimensions - 1;\n float lowerLeft = step(reference, texelFetch(shadowMap, ivec3(clamp(base, ivec2(0), maximum), layer), 0).r);\n float lowerRight = step(reference, texelFetch(shadowMap, ivec3(clamp(base + ivec2(1,0), ivec2(0), maximum), layer), 0).r);\n float upperLeft = step(reference, texelFetch(shadowMap, ivec3(clamp(base + ivec2(0,1), ivec2(0), maximum), layer), 0).r);\n float upperRight = step(reference, texelFetch(shadowMap, ivec3(clamp(base + ivec2(1,1), ivec2(0), maximum), layer), 0).r);\n return mix(mix(lowerLeft, lowerRight, fraction.x), mix(upperLeft, upperRight, fraction.x), fraction.y);\n}\nfloat shadow_arrayPCSS(sampler2DArray shadowMap, int layer, vec3 projected, float searchRadius, float rotation, float penumbraScale) {\n if (!shadow_validProjection(projected)) return 1.0;\n float texel = 1.0 / float(textureSize(shadowMap, 0).x);\n searchRadius = max(texel, searchRadius);\n float blockerDepth = 0.0;\n float blockerCount = 0.0;\n for (int index = 0; index < 24; index++) {\n if (index >= shadow.blockerSampleCount) break;\n vec2 coordinate = clamp(projected.xy + shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius, vec2(0.0), vec2(1.0));\n float depth = textureLod(shadowMap, vec3(coordinate, float(layer)), 0.0).r;\n if (depth < projected.z) { blockerDepth += depth; blockerCount += 1.0; }\n }\n if (blockerCount == 0.0) return 1.0;\n float averageBlocker = blockerDepth / blockerCount;\n float penumbra = clamp((projected.z - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);\n float radius = max(texel, searchRadius * (1.0 + penumbra * penumbraScale));\n float visibility = 0.0;\n for (int index = 0; index < 48; index++) {\n if (index >= shadow.filterSampleCount) break;\n vec2 coordinate = clamp(projected.xy + shadow_diskSample(index, shadow.filterSampleCount, rotation) * radius, vec2(0.0), vec2(1.0));\n visibility += shadow_compare(shadowMap, coordinate, layer, projected.z);\n }\n return visibility / max(float(shadow.filterSampleCount), 1.0);\n}\nint shadow_directionalCascadeIndex(float viewDepth) {\n int cascadeIndex = 0;\n for (int index = 0; index < 4; index++) {\n if (index >= shadow.cascadeCount) break;\n cascadeIndex = index;\n if (viewDepth <= shadow.cascadeSplits[index]) break;\n }\n return cascadeIndex;\n}\nint shadow_getDirectionalCascadeIndex(float viewDepth) {\n return shadow.directionalLightCount == 0 ? -1 : shadow_directionalCascadeIndex(viewDepth);\n}\nfloat shadow_directionalPCSS(int cascadeIndex, vec3 worldPosition, float viewDepth) {\n DirectionalShadowLightUniform light = shadow.directionalLights[0];\n return shadow_arrayPCSS(directionalShadowTexture, cascadeIndex,\n shadow_project(shadow.directionalViewProjectionMatrices[cascadeIndex], worldPosition),\n light.sourceAngularRadius * viewDepth / max(light.shadowDistance, 0.001),\n shadow_hash(worldPosition) * 2.0 * SHADOW_PI, 18.0);\n}\nfloat shadow_getDirectionalFactor(vec3 worldPosition, vec3 worldNormal, float viewDepth) {\n if (shadow.directionalLightCount == 0 || viewDepth <= 0.0) return 1.0;\n DirectionalShadowLightUniform light = shadow.directionalLights[0];\n if (viewDepth >= light.shadowDistance) return 1.0;\n vec3 position = worldPosition + normalize(worldNormal) * light.normalBias;\n int cascadeIndex = shadow_directionalCascadeIndex(viewDepth);\n float visibility = shadow_directionalPCSS(cascadeIndex, position, viewDepth);\n if (cascadeIndex + 1 < shadow.cascadeCount) {\n float cascadeStart = cascadeIndex > 0 ? shadow.cascadeSplits[cascadeIndex - 1] : 0.0;\n float cascadeEnd = shadow.cascadeSplits[cascadeIndex];\n float blendWidth = max((cascadeEnd - cascadeStart) * light.cascadeBlendFraction, 0.0001);\n float blend = smoothstep(cascadeEnd - blendWidth, cascadeEnd, viewDepth);\n if (blend > 0.0) visibility = mix(visibility, shadow_directionalPCSS(cascadeIndex + 1, position, viewDepth), blend);\n }\n float fadeWidth = max(light.shadowDistance * light.farFadeFraction, 0.0001);\n visibility = mix(visibility, 1.0, smoothstep(light.shadowDistance - fadeWidth, light.shadowDistance, viewDepth));\n return mix(1.0, visibility, light.strength);\n}\nfloat shadow_getSpotFactor(int lightIndex, vec3 worldPosition, vec3 worldNormal) {\n if (lightIndex < 0 || lightIndex >= shadow.spotLightCount) return 1.0;\n SpotShadowLightUniform light = shadow.spotLights[lightIndex];\n vec3 fromLight = worldPosition - light.position;\n float distanceToLight = length(fromLight);\n if (distanceToLight >= light.range || dot(normalize(fromLight), normalize(light.direction)) < light.outerConeCos) return 1.0;\n vec3 position = worldPosition + normalize(worldNormal) * light.normalBias;\n float visibility = shadow_arrayPCSS(spotShadowTexture, lightIndex,\n shadow_project(shadow.spotViewProjectionMatrices[lightIndex], position),\n light.sourceRadius / max(distanceToLight, 0.001) * 0.5,\n shadow_hash(worldPosition) * 2.0 * SHADOW_PI, 16.0);\n return mix(1.0, visibility, light.strength);\n}\nfloat shadow_pointReferenceDepth(float majorDistance, float nearPlane, float farPlane) {\n return farPlane / (farPlane - nearPlane) - (farPlane * nearPlane) / ((farPlane - nearPlane) * majorDistance);\n}\nvec3 shadow_pointCoordinate(int lightIndex, vec3 direction) {\n vec3 magnitude = abs(direction);\n int face = magnitude.x >= magnitude.y && magnitude.x >= magnitude.z ? (direction.x >= 0.0 ? 0 : 1)\n : magnitude.y >= magnitude.z ? (direction.y >= 0.0 ? 2 : 3) : (direction.z >= 0.0 ? 4 : 5);\n PointShadowLightUniform light = shadow.pointLights[lightIndex];\n vec3 projected = shadow_project(shadow.pointViewProjectionMatrices[lightIndex * 6 + face], light.position + direction * light.range * 0.5);\n return vec3(projected.xy, float(lightIndex * 6 + face));\n}\nfloat shadow_getPointFactor(int lightIndex, vec3 worldPosition, vec3 worldNormal) {\n if (lightIndex < 0 || lightIndex >= shadow.pointLightCount) return 1.0;\n PointShadowLightUniform light = shadow.pointLights[lightIndex];\n vec3 fromLight = worldPosition + normalize(worldNormal) * light.normalBias - light.position;\n float distanceToLight = length(fromLight);\n if (distanceToLight >= light.range || distanceToLight <= light.nearPlane) return 1.0;\n vec3 direction = normalize(fromLight);\n float referenceDepth = shadow_pointReferenceDepth(max(max(abs(fromLight.x), abs(fromLight.y)), abs(fromLight.z)), light.nearPlane, light.range);\n vec3 reference = abs(direction.y) > 0.99 ? vec3(0,0,1) : vec3(0,1,0);\n vec3 tangent = normalize(cross(direction, reference));\n vec3 bitangent = normalize(cross(direction, tangent));\n float searchRadius = light.sourceRadius / max(distanceToLight, 0.001);\n float rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;\n float blockerDepth = 0.0;\n float blockerCount = 0.0;\n for (int index = 0; index < 24; index++) {\n if (index >= shadow.blockerSampleCount) break;\n vec2 disk = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;\n vec3 coordinate = shadow_pointCoordinate(lightIndex, normalize(direction + tangent * disk.x + bitangent * disk.y));\n float depth = textureLod(pointShadowTexture, coordinate, 0.0).r;\n if (depth < referenceDepth) { blockerDepth += depth; blockerCount += 1.0; }\n }\n if (blockerCount == 0.0) return 1.0;\n float averageBlocker = blockerDepth / blockerCount;\n float penumbra = clamp((referenceDepth - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);\n float radius = searchRadius * (1.0 + penumbra * 12.0);\n float visibility = 0.0;\n for (int index = 0; index < 48; index++) {\n if (index >= shadow.filterSampleCount) break;\n vec2 disk = shadow_diskSample(index, shadow.filterSampleCount, rotation) * radius;\n vec3 coordinate = shadow_pointCoordinate(lightIndex, normalize(direction + tangent * disk.x + bitangent * disk.y));\n visibility += shadow_compare(pointShadowTexture, coordinate.xy, int(coordinate.z), referenceDepth);\n }\n return mix(1.0, visibility / max(float(shadow.filterSampleCount), 1.0), light.strength);\n}\n";
56
+ readonly props: ShadowProps;
57
+ readonly uniforms: ShadowUniforms;
58
+ readonly bindings: ShadowBindings;
59
+ readonly uniformTypes: {
60
+ readonly directionalLightCount: "i32";
61
+ readonly spotLightCount: "i32";
62
+ readonly pointLightCount: "i32";
63
+ readonly cascadeCount: "i32";
64
+ readonly blockerSampleCount: "i32";
65
+ readonly filterSampleCount: "i32";
66
+ readonly cascadeSplits: "vec4<f32>";
67
+ readonly directionalViewProjectionMatrices: readonly ["mat4x4<f32>", 4];
68
+ readonly spotViewProjectionMatrices: readonly ["mat4x4<f32>", 4];
69
+ readonly pointViewProjectionMatrices: readonly ["mat4x4<f32>", 24];
70
+ readonly directionalLights: readonly [{
71
+ readonly direction: "vec3<f32>";
72
+ readonly strength: "f32";
73
+ readonly normalBias: "f32";
74
+ readonly sourceAngularRadius: "f32";
75
+ readonly cascadeBlendFraction: "f32";
76
+ readonly farFadeFraction: "f32";
77
+ readonly shadowDistance: "f32";
78
+ }, 1];
79
+ readonly spotLights: readonly [{
80
+ readonly position: "vec3<f32>";
81
+ readonly range: "f32";
82
+ readonly direction: "vec3<f32>";
83
+ readonly outerConeCos: "f32";
84
+ readonly sourceRadius: "f32";
85
+ readonly normalBias: "f32";
86
+ readonly strength: "f32";
87
+ readonly nearPlane: "f32";
88
+ }, 4];
89
+ readonly pointLights: readonly [{
90
+ readonly position: "vec3<f32>";
91
+ readonly range: "f32";
92
+ readonly sourceRadius: "f32";
93
+ readonly normalBias: "f32";
94
+ readonly strength: "f32";
95
+ readonly nearPlane: "f32";
96
+ }, 4];
97
+ };
98
+ readonly bindingLayout: readonly [{
99
+ readonly name: "shadow";
100
+ readonly group: 2;
101
+ }, {
102
+ readonly name: "directionalShadowTexture";
103
+ readonly group: 2;
104
+ }, {
105
+ readonly name: "spotShadowTexture";
106
+ readonly group: 2;
107
+ }, {
108
+ readonly name: "pointShadowTexture";
109
+ readonly group: 2;
110
+ }, {
111
+ readonly name: "shadowComparisonSampler";
112
+ readonly group: 2;
113
+ }];
114
+ readonly getUniforms: (props: Partial<ShadowProps>) => Partial<ShadowUniforms & ShadowBindings>;
115
+ };
116
+ export {};
117
+ //# sourceMappingURL=shadow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow.d.ts","sourceRoot":"","sources":["../../../../src/modules/lighting/shadow/shadow.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,YAAY,EAAE,YAAY,EAAE,aAAa,EAAC,MAAM,eAAe,CAAC;AAE7E,OAAO,KAAK,EAGV,WAAW,EAEZ,0BAAuB;AAOxB,KAAK,wBAAwB,GAAG;IAC9B,SAAS,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACvB,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;IACtC,iCAAiC,EAAE,SAAS,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;IACtE,0BAA0B,EAAE,SAAS,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;IAC/D,2BAA2B,EAAE,SAAS,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;IAChE,iBAAiB,EAAE,SAAS,wBAAwB,EAAE,CAAC;IACvD,UAAU,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACzC,WAAW,EAAE,SAAS,kBAAkB,EAAE,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,QAAQ,GAAG,OAAO,EAAE,QAAQ,GAAG,OAAO,IAAI;IACnE,wBAAwB,EAAE,QAAQ,CAAC;IACnC,iBAAiB,EAAE,QAAQ,CAAC;IAC5B,kBAAkB,EAAE,QAAQ,CAAC;IAC7B,uBAAuB,CAAC,EAAE,QAAQ,CAAC;CACpC,CAAC;AAsTF,yGAAyG;AACzG,eAAO,MAAM,MAAM;;;;oBAIJ,WAAW;uBACR,cAAc;uBACd,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAuBX,OAAO,CAAC,WAAW,CAAC,KAAG,OAAO,CAAC,cAAc,GAAG,cAAc,CAAC;CA0CN,CAAC"}