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

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 (110) hide show
  1. package/dist/dist.dev.js +1923 -60
  2. package/dist/dist.min.js +1499 -148
  3. package/dist/glsl.cjs.map +1 -1
  4. package/dist/index.cjs +41 -0
  5. package/dist/index.cjs.map +3 -3
  6. package/dist/index.d.ts +20 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +11 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.cjs +40 -0
  11. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.d.ts +6 -0
  12. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.d.ts.map +1 -0
  13. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.js +15 -0
  14. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.js.map +1 -0
  15. package/dist/modules/lighting/contact-shadow/contact-shadow.cjs +348 -0
  16. package/dist/modules/lighting/contact-shadow/contact-shadow.d.ts +188 -0
  17. package/dist/modules/lighting/contact-shadow/contact-shadow.d.ts.map +1 -0
  18. package/dist/modules/lighting/contact-shadow/contact-shadow.js +307 -0
  19. package/dist/modules/lighting/contact-shadow/contact-shadow.js.map +1 -0
  20. package/dist/modules/lighting/lightmap/lightmap-filter.cjs +93 -0
  21. package/dist/modules/lighting/lightmap/lightmap-filter.d.ts +35 -0
  22. package/dist/modules/lighting/lightmap/lightmap-filter.d.ts.map +1 -0
  23. package/dist/modules/lighting/lightmap/lightmap-filter.js +71 -0
  24. package/dist/modules/lighting/lightmap/lightmap-filter.js.map +1 -0
  25. package/dist/modules/lighting/lightmap/lightmap.cjs +89 -0
  26. package/dist/modules/lighting/lightmap/lightmap.d.ts +405 -0
  27. package/dist/modules/lighting/lightmap/lightmap.d.ts.map +1 -0
  28. package/dist/modules/lighting/lightmap/lightmap.js +67 -0
  29. package/dist/modules/lighting/lightmap/lightmap.js.map +1 -0
  30. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.cjs +87 -0
  31. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.d.ts +21 -0
  32. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.d.ts.map +1 -0
  33. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.js +61 -0
  34. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.js.map +1 -0
  35. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.cjs +87 -0
  36. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.d.ts +21 -0
  37. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.d.ts.map +1 -0
  38. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.js +61 -0
  39. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.js.map +1 -0
  40. package/dist/modules/lighting/pbr-material/pbr-material-glsl.cjs +33 -11
  41. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts +2 -2
  42. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts.map +1 -1
  43. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js +33 -11
  44. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js.map +1 -1
  45. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.cjs +33 -11
  46. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts +1 -1
  47. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts.map +1 -1
  48. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js +33 -11
  49. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js.map +1 -1
  50. package/dist/modules/lighting/pbr-material/pbr-material.d.ts +3 -3
  51. package/dist/modules/lighting/pbr-shadow/pbr-shadow.cjs +97 -0
  52. package/dist/modules/lighting/pbr-shadow/pbr-shadow.d.ts +15 -0
  53. package/dist/modules/lighting/pbr-shadow/pbr-shadow.d.ts.map +1 -0
  54. package/dist/modules/lighting/pbr-shadow/pbr-shadow.js +69 -0
  55. package/dist/modules/lighting/pbr-shadow/pbr-shadow.js.map +1 -0
  56. package/dist/modules/lighting/pbr-surface/pbr-surface.cjs +114 -0
  57. package/dist/modules/lighting/pbr-surface/pbr-surface.d.ts +15 -0
  58. package/dist/modules/lighting/pbr-surface/pbr-surface.d.ts.map +1 -0
  59. package/dist/modules/lighting/pbr-surface/pbr-surface.js +88 -0
  60. package/dist/modules/lighting/pbr-surface/pbr-surface.js.map +1 -0
  61. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.cjs +379 -0
  62. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.d.ts +41 -0
  63. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.d.ts.map +1 -0
  64. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.js +345 -0
  65. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.js.map +1 -0
  66. package/dist/modules/lighting/shadow/shadow-glsl.cjs +228 -0
  67. package/dist/modules/lighting/shadow/shadow-glsl.d.ts +3 -0
  68. package/dist/modules/lighting/shadow/shadow-glsl.d.ts.map +1 -0
  69. package/dist/modules/lighting/shadow/shadow-glsl.js +204 -0
  70. package/dist/modules/lighting/shadow/shadow-glsl.js.map +1 -0
  71. package/dist/modules/lighting/shadow/shadow-types.cjs +15 -0
  72. package/dist/modules/lighting/shadow/shadow-types.d.ts +63 -0
  73. package/dist/modules/lighting/shadow/shadow-types.d.ts.map +1 -0
  74. package/dist/modules/lighting/shadow/shadow-types.js +5 -0
  75. package/dist/modules/lighting/shadow/shadow-types.js.map +1 -0
  76. package/dist/modules/lighting/shadow/shadow.cjs +458 -0
  77. package/dist/modules/lighting/shadow/shadow.d.ts +117 -0
  78. package/dist/modules/lighting/shadow/shadow.d.ts.map +1 -0
  79. package/dist/modules/lighting/shadow/shadow.js +450 -0
  80. package/dist/modules/lighting/shadow/shadow.js.map +1 -0
  81. package/dist/modules/lighting/unlit-material/unlit-material.cjs +60 -0
  82. package/dist/modules/lighting/unlit-material/unlit-material.d.ts +9 -0
  83. package/dist/modules/lighting/unlit-material/unlit-material.d.ts.map +1 -0
  84. package/dist/modules/lighting/unlit-material/unlit-material.js +40 -0
  85. package/dist/modules/lighting/unlit-material/unlit-material.js.map +1 -0
  86. package/dist/types/shader-type-decoder.cjs +12 -2
  87. package/dist/types/shader-type-decoder.d.ts +7 -7
  88. package/dist/types/shader-type-decoder.d.ts.map +1 -1
  89. package/dist/types/shader-type-decoder.js +13 -2
  90. package/dist/types/shader-type-decoder.js.map +1 -1
  91. package/dist/types.cjs.map +1 -1
  92. package/dist/wgsl.cjs.map +1 -1
  93. package/package.json +3 -3
  94. package/src/index.ts +52 -0
  95. package/src/modules/lighting/clustered-light-grid/clustered-light-grid.ts +17 -0
  96. package/src/modules/lighting/contact-shadow/contact-shadow.ts +365 -0
  97. package/src/modules/lighting/lightmap/lightmap-filter.ts +73 -0
  98. package/src/modules/lighting/lightmap/lightmap.ts +76 -0
  99. package/src/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.ts +78 -0
  100. package/src/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.ts +78 -0
  101. package/src/modules/lighting/pbr-material/pbr-material-glsl.ts +33 -11
  102. package/src/modules/lighting/pbr-material/pbr-material-wgsl.ts +33 -11
  103. package/src/modules/lighting/pbr-shadow/pbr-shadow.ts +85 -0
  104. package/src/modules/lighting/pbr-surface/pbr-surface.ts +103 -0
  105. package/src/modules/lighting/prefiltered-environment/prefiltered-environment.ts +370 -0
  106. package/src/modules/lighting/shadow/shadow-glsl.ts +204 -0
  107. package/src/modules/lighting/shadow/shadow-types.ts +68 -0
  108. package/src/modules/lighting/shadow/shadow.ts +536 -0
  109. package/src/modules/lighting/unlit-material/unlit-material.ts +44 -0
  110. package/src/types/shader-type-decoder.ts +22 -9
package/dist/dist.dev.js CHANGED
@@ -28,6 +28,8 @@ var __exports__ = (() => {
28
28
  var bundle_exports = {};
29
29
  __export(bundle_exports, {
30
30
  GLSLShaderAssembler: () => GLSLShaderAssembler,
31
+ PBR_ENVIRONMENT_FRAGMENT_GLSL: () => PBR_ENVIRONMENT_FRAGMENT_GLSL,
32
+ PBR_ENVIRONMENT_FRAGMENT_WGSL: () => PBR_ENVIRONMENT_FRAGMENT_WGSL,
31
33
  PBR_TONE_MAP_MODE: () => PBR_TONE_MAP_MODE,
32
34
  SKIN_MAX_JOINTS: () => SKIN_MAX_JOINTS,
33
35
  STORAGE_COLOR_DEFAULT_BYTE_STRIDES: () => STORAGE_COLOR_DEFAULT_BYTE_STRIDES,
@@ -43,8 +45,12 @@ var __exports__ = (() => {
43
45
  checkShaderModuleDeprecations: () => checkShaderModuleDeprecations,
44
46
  clipShaderPlugin: () => clipShaderPlugin,
45
47
  clouds: () => clouds,
48
+ clusteredLightGrid: () => clusteredLightGrid,
46
49
  colors: () => colors,
47
50
  combineInjects: () => combineInjects,
51
+ contactShadowBilateralBlur: () => contactShadowBilateralBlur,
52
+ contactShadowComposite: () => contactShadowComposite,
53
+ contactShadowTrace: () => contactShadowTrace,
48
54
  convertToVec4: () => convertToVec4,
49
55
  dggs: () => dggs,
50
56
  dirlight: () => dirlight,
@@ -80,13 +86,20 @@ var __exports__ = (() => {
80
86
  integrateSurfaceWeather: () => integrateSurfaceWeather,
81
87
  lambertMaterial: () => lambertMaterial,
82
88
  lighting: () => lighting,
89
+ lightmapBake: () => lightmapBake,
90
+ lightmapFilter: () => lightmapFilter,
83
91
  mergeShaderPluginModules: () => mergeShaderPluginModules,
84
92
  normalizeByteColor3: () => normalizeByteColor3,
85
93
  normalizeByteColor4: () => normalizeByteColor4,
86
94
  pathDash: () => pathDash,
87
95
  patternFill: () => patternFill,
96
+ pbrAmbientOcclusion: () => pbrAmbientOcclusion,
97
+ pbrContactShadow: () => pbrContactShadow,
98
+ pbrEnvironmentFilter: () => pbrEnvironmentFilter,
88
99
  pbrMaterial: () => pbrMaterial,
89
100
  pbrScene: () => pbrScene,
101
+ pbrShadow: () => pbrShadow,
102
+ pbrSurface: () => pbrSurface,
90
103
  phongMaterial: () => phongMaterial,
91
104
  picking: () => picking,
92
105
  pointGlow: () => pointGlow,
@@ -97,6 +110,7 @@ var __exports__ = (() => {
97
110
  resolveUseByteColors: () => resolveUseByteColors,
98
111
  riverWaterMaterial: () => riverWaterMaterial,
99
112
  scanWGSLInterface: () => scanWGSLInterface,
113
+ shadow: () => shadow,
100
114
  sketchStroke: () => sketchStroke,
101
115
  skin: () => skin,
102
116
  storageColors: () => storageColors,
@@ -104,6 +118,7 @@ var __exports__ = (() => {
104
118
  toHalfFloat: () => toHalfFloat,
105
119
  typeToChannelCount: () => typeToChannelCount,
106
120
  typeToChannelSuffix: () => typeToChannelSuffix,
121
+ unlitMaterial: () => unlitMaterial,
107
122
  validateShaderModuleUniformLayout: () => validateShaderModuleUniformLayout,
108
123
  valueNoise: () => valueNoise,
109
124
  volumeRaymarch: () => volumeRaymarch,
@@ -1184,8 +1199,13 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1184
1199
  }
1185
1200
  return decoded;
1186
1201
  }
1202
+ var attributeShaderTypeInfoCache = /* @__PURE__ */ new Map();
1187
1203
  function getAttributeShaderTypeInfo(attributeType) {
1188
1204
  const resolvedAttributeType = resolveAttributeShaderTypeAlias(attributeType);
1205
+ const cached = attributeShaderTypeInfoCache.get(resolvedAttributeType);
1206
+ if (cached) {
1207
+ return cached;
1208
+ }
1189
1209
  const decoded = TYPE_INFO[resolvedAttributeType];
1190
1210
  if (!decoded) {
1191
1211
  throw new Error(`Unsupported attribute shader type: ${attributeType}`);
@@ -1194,13 +1214,15 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1194
1214
  const integer = primitiveType === "i32" || primitiveType === "u32";
1195
1215
  const signed = primitiveType !== "u32";
1196
1216
  const byteLength = PRIMITIVE_TYPE_SIZES[primitiveType] * components;
1197
- return {
1217
+ const result = Object.freeze({
1198
1218
  primitiveType,
1199
1219
  components,
1200
1220
  byteLength,
1201
1221
  integer,
1202
1222
  signed
1203
- };
1223
+ });
1224
+ attributeShaderTypeInfoCache.set(resolvedAttributeType, result);
1225
+ return result;
1204
1226
  }
1205
1227
  var ShaderTypeDecoder = class {
1206
1228
  getVariableShaderTypeInfo(format) {
@@ -1309,6 +1331,9 @@ ${fragmentString}${source7.slice(insertionIndex)}`;
1309
1331
  "mat4x3<u32>": { type: "u32", components: 12 },
1310
1332
  "mat4x4<u32>": { type: "u32", components: 16 }
1311
1333
  };
1334
+ for (const info of Object.values(UNIFORM_FORMATS)) {
1335
+ Object.freeze(info);
1336
+ }
1312
1337
  var WGSL_ATTRIBUTE_TYPE_ALIAS_MAP = {
1313
1338
  vec2i: "vec2<i32>",
1314
1339
  vec3i: "vec3<i32>",
@@ -5137,6 +5162,35 @@ void main() {
5137
5162
  out[15] = 1;
5138
5163
  return out;
5139
5164
  }
5165
+ function affineComponents(matrix) {
5166
+ if (matrix.length !== 16 || Array.from(matrix).some((x) => !Number.isFinite(x)) || matrix[3] !== 0 || matrix[7] !== 0 || matrix[11] !== 0 || matrix[15] !== 1) {
5167
+ throw new RangeError("Expected a finite affine matrix");
5168
+ }
5169
+ const scale2 = [
5170
+ Math.hypot(matrix[0], matrix[1], matrix[2]),
5171
+ Math.hypot(matrix[4], matrix[5], matrix[6]),
5172
+ Math.hypot(matrix[8], matrix[9], matrix[10])
5173
+ ];
5174
+ if (scale2.some((x) => x === 0 || !Number.isFinite(x)))
5175
+ throw new RangeError("Singular transform");
5176
+ const rotation = Array.from({ length: 9 }, (_, i) => matrix[Math.floor(i / 3) * 4 + i % 3] / scale2[Math.floor(i / 3)]);
5177
+ for (const [a, b] of [
5178
+ [0, 3],
5179
+ [0, 6],
5180
+ [3, 6]
5181
+ ]) {
5182
+ if (Math.abs(rotation[a] * rotation[b] + rotation[a + 1] * rotation[b + 1] + rotation[a + 2] * rotation[b + 2]) > 1e-7) {
5183
+ throw new RangeError("Sheared transforms do not have a TRS decomposition");
5184
+ }
5185
+ }
5186
+ const determinant2 = rotation[0] * (rotation[4] * rotation[8] - rotation[5] * rotation[7]) - rotation[3] * (rotation[1] * rotation[8] - rotation[2] * rotation[7]) + rotation[6] * (rotation[1] * rotation[5] - rotation[2] * rotation[4]);
5187
+ if (determinant2 < 0) {
5188
+ scale2[0] = -scale2[0];
5189
+ for (let i = 0; i < 3; i++)
5190
+ rotation[i] = -rotation[i];
5191
+ }
5192
+ return { rotation, scale: scale2, translation: [matrix[12], matrix[13], matrix[14]] };
5193
+ }
5140
5194
 
5141
5195
  // ../../node_modules/@math.gl/core/dist/gl-matrix/vec4.js
5142
5196
  function transformMat43(out, a, m) {
@@ -5431,27 +5485,15 @@ void main() {
5431
5485
  * @returns self
5432
5486
  */
5433
5487
  getRotation(result, scaleResult) {
5434
- result = result || [-0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0];
5435
- scaleResult = scaleResult || [-0, -0, -0];
5436
- const scale2 = this.getScale(scaleResult);
5437
- const inverseScale0 = 1 / scale2[0];
5438
- const inverseScale1 = 1 / scale2[1];
5439
- const inverseScale2 = 1 / scale2[2];
5440
- result[0] = this[0] * inverseScale0;
5441
- result[1] = this[1] * inverseScale1;
5442
- result[2] = this[2] * inverseScale2;
5443
- result[3] = 0;
5444
- result[4] = this[4] * inverseScale0;
5445
- result[5] = this[5] * inverseScale1;
5446
- result[6] = this[6] * inverseScale2;
5447
- result[7] = 0;
5448
- result[8] = this[8] * inverseScale0;
5449
- result[9] = this[9] * inverseScale1;
5450
- result[10] = this[10] * inverseScale2;
5451
- result[11] = 0;
5452
- result[12] = 0;
5453
- result[13] = 0;
5454
- result[14] = 0;
5488
+ const { rotation, scale: scale2 } = affineComponents(this);
5489
+ result = result || new Array(16);
5490
+ if (scaleResult)
5491
+ for (let i = 0; i < 3; i++)
5492
+ scaleResult[i] = scale2[i];
5493
+ for (let i = 0; i < 16; i++)
5494
+ result[i] = 0;
5495
+ for (let i = 0; i < 9; i++)
5496
+ result[Math.floor(i / 3) * 4 + i % 3] = rotation[i];
5455
5497
  result[15] = 1;
5456
5498
  return result;
5457
5499
  }
@@ -5462,21 +5504,13 @@ void main() {
5462
5504
  * @returns self
5463
5505
  */
5464
5506
  getRotationMatrix3(result, scaleResult) {
5465
- result = result || [-0, -0, -0, -0, -0, -0, -0, -0, -0];
5466
- scaleResult = scaleResult || [-0, -0, -0];
5467
- const scale2 = this.getScale(scaleResult);
5468
- const inverseScale0 = 1 / scale2[0];
5469
- const inverseScale1 = 1 / scale2[1];
5470
- const inverseScale2 = 1 / scale2[2];
5471
- result[0] = this[0] * inverseScale0;
5472
- result[1] = this[1] * inverseScale1;
5473
- result[2] = this[2] * inverseScale2;
5474
- result[3] = this[4] * inverseScale0;
5475
- result[4] = this[5] * inverseScale1;
5476
- result[5] = this[6] * inverseScale2;
5477
- result[6] = this[8] * inverseScale0;
5478
- result[7] = this[9] * inverseScale1;
5479
- result[8] = this[10] * inverseScale2;
5507
+ const { rotation, scale: scale2 } = affineComponents(this);
5508
+ result = result || new Array(9);
5509
+ if (scaleResult)
5510
+ for (let i = 0; i < 3; i++)
5511
+ scaleResult[i] = scale2[i];
5512
+ for (let i = 0; i < 9; i++)
5513
+ result[i] = rotation[i];
5480
5514
  return result;
5481
5515
  }
5482
5516
  // Modifiers
@@ -12913,6 +12947,42 @@ vec4 dirlight_filterColor(vec4 color) {
12913
12947
  return uniforms2;
12914
12948
  }
12915
12949
 
12950
+ // src/modules/lighting/unlit-material/unlit-material.ts
12951
+ var SOURCE_WGSL2 = (
12952
+ /* wgsl */
12953
+ `fn unlitMaterial_getColor(surfaceColor: vec4<f32>) -> vec4<f32> {
12954
+ return surfaceColor;
12955
+ }
12956
+
12957
+ // Compatibility with the aggregate Phong/Gouraud material evaluation function.
12958
+ fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, positionWorldspace: vec3<f32>, normalWorldspace: vec3<f32>) -> vec3<f32> {
12959
+ return surfaceColor;
12960
+ }
12961
+ `
12962
+ );
12963
+ var SOURCE_GLSL = (
12964
+ /* glsl */
12965
+ `vec4 unlitMaterial_getColor(vec4 surfaceColor) {
12966
+ return surfaceColor;
12967
+ }
12968
+
12969
+ // Compatibility with the aggregate Phong/Gouraud material evaluation function.
12970
+ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 positionWorldspace, vec3 normalWorldspace) {
12971
+ return surfaceColor;
12972
+ }
12973
+ `
12974
+ );
12975
+ var unlitMaterial = {
12976
+ name: "unlitMaterial",
12977
+ source: SOURCE_WGSL2,
12978
+ vs: SOURCE_GLSL,
12979
+ fs: SOURCE_GLSL,
12980
+ dependencies: [],
12981
+ bindingLayout: [],
12982
+ uniformTypes: {},
12983
+ defaultUniforms: {}
12984
+ };
12985
+
12916
12986
  // src/modules/lighting/lambert-material/lambert-shaders-wgsl.ts
12917
12987
  var LAMBERT_WGSL = (
12918
12988
  /* wgsl */
@@ -14542,7 +14612,7 @@ void pbr_setPositionNormalTangentUV(
14542
14612
  vec3 bitangentW = cross(normalW, tangentW) * tangent.w;
14543
14613
  pbr_vTBN = mat3(tangentW, bitangentW, normalW);
14544
14614
  #else // HAS_TANGENTS != 1
14545
- pbr_vNormal = normalize(vec3(pbrProjection.modelMatrix * vec4(normal.xyz, 0.0)));
14615
+ pbr_vNormal = normalize(vec3(pbrProjection.normalMatrix * vec4(normal.xyz, 0.0)));
14546
14616
  #endif
14547
14617
  #endif
14548
14618
 
@@ -15549,7 +15619,9 @@ vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor) {
15549
15619
  }
15550
15620
 
15551
15621
  // Visibility for the first four spotlights; environment and other lights are unaffected.
15552
- vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15622
+ float pbr_ambientVisibility = 1.0;
15623
+
15624
+ vec4 pbr_filterColorWithShadows(vec4 vertexColor, float directionalVisibility, vec4 pointVisibility, vec4 spotVisibility)
15553
15625
  {
15554
15626
  vec2 baseColorUV = getMaterialUV(pbrMaterial.baseColorUVSet, pbrMaterial.baseColorUVTransform);
15555
15627
  vec2 metallicRoughnessUV = getMaterialUV(
@@ -15645,7 +15717,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15645
15717
  color.rgb = baseColor.rgb;
15646
15718
  #ifdef HAS_LIGHTMAP
15647
15719
  if (pbrMaterial.lightmapMapEnabled) {
15648
- color *= lightmapIrradiance / M_PI;
15720
+ color *= lightmapIrradiance / M_PI * pbr_ambientVisibility;
15649
15721
  #ifdef HAS_OCCLUSIONMAP
15650
15722
  if (pbrMaterial.occlusionMapEnabled) {
15651
15723
  float ao = texture(pbr_occlusionSampler, occlusionUV).r;
@@ -15747,12 +15819,12 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15747
15819
 
15748
15820
  #ifdef USE_LIGHTS
15749
15821
  PBRInfo_setAmbientLight(pbrInfo);
15750
- color += calculateFinalColor(pbrInfo, lighting.ambientColor);
15822
+ color += calculateFinalColor(pbrInfo, lighting.ambientColor * pbr_ambientVisibility);
15751
15823
 
15752
15824
  for(int i = 0; i < lighting.directionalLightCount; i++) {
15753
15825
  if (i < lighting.directionalLightCount) {
15754
15826
  PBRInfo_setDirectionalLight(pbrInfo, -lighting_getDirectionalLight(i).direction);
15755
- color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
15827
+ color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color * (i == 0 ? clamp(directionalVisibility, 0.0, 1.0) : 1.0));
15756
15828
  }
15757
15829
  }
15758
15830
 
@@ -15760,7 +15832,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15760
15832
  if (i < lighting.pointLightCount) {
15761
15833
  PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
15762
15834
  float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
15763
- color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
15835
+ color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color * (i < 4 ? clamp(pointVisibility[min(i, 3)], 0.0, 1.0) : 1.0) / attenuation);
15764
15836
  }
15765
15837
  }
15766
15838
 
@@ -15786,6 +15858,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15786
15858
  }
15787
15859
  #endif
15788
15860
 
15861
+ vec3 indirectBaseColor = color;
15789
15862
  #ifdef USE_IBL
15790
15863
  if (pbrMaterial.IBLenabled) {
15791
15864
  color += getIBLContribution(pbrInfo, n, reflection);
@@ -15794,6 +15867,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15794
15867
 
15795
15868
  // The pinned KHR_lightmap draft explicitly defines c_diff = (1 - metallic) * baseColor.
15796
15869
  color += baseColor.rgb * (1.0 - metallic) * lightmapIrradiance / M_PI;
15870
+ color = indirectBaseColor + (color - indirectBaseColor) * pbr_ambientVisibility;
15797
15871
 
15798
15872
  #ifdef HAS_OCCLUSIONMAP
15799
15873
  if (pbrMaterial.occlusionMapEnabled) {
@@ -16026,7 +16100,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16026
16100
  PBRInfo_setAmbientLight(pbrInfo);
16027
16101
  color += calculateMaterialLightColor(
16028
16102
  pbrInfo,
16029
- lighting.ambientColor,
16103
+ lighting.ambientColor * pbr_ambientVisibility,
16030
16104
  clearcoatNormal,
16031
16105
  clearcoatFactor,
16032
16106
  clearcoatRoughness,
@@ -16042,7 +16116,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16042
16116
  PBRInfo_setDirectionalLight(pbrInfo, -lighting_getDirectionalLight(i).direction);
16043
16117
  color += calculateMaterialLightColor(
16044
16118
  pbrInfo,
16045
- lighting_getDirectionalLight(i).color,
16119
+ lighting_getDirectionalLight(i).color * (i == 0 ? clamp(directionalVisibility, 0.0, 1.0) : 1.0),
16046
16120
  clearcoatNormal,
16047
16121
  clearcoatFactor,
16048
16122
  clearcoatRoughness,
@@ -16053,7 +16127,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16053
16127
  );
16054
16128
  color += calculateDiffuseTransmissionLight(
16055
16129
  pbrInfo,
16056
- lighting_getDirectionalLight(i).color,
16130
+ lighting_getDirectionalLight(i).color * (i == 0 ? clamp(directionalVisibility, 0.0, 1.0) : 1.0),
16057
16131
  diffuseTransmissionColor,
16058
16132
  diffuseTransmission,
16059
16133
  multiscatterColor,
@@ -16069,7 +16143,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16069
16143
  float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
16070
16144
  color += calculateMaterialLightColor(
16071
16145
  pbrInfo,
16072
- lighting_getPointLight(i).color / attenuation,
16146
+ lighting_getPointLight(i).color * (i < 4 ? clamp(pointVisibility[min(i, 3)], 0.0, 1.0) : 1.0) / attenuation,
16073
16147
  clearcoatNormal,
16074
16148
  clearcoatFactor,
16075
16149
  clearcoatRoughness,
@@ -16080,7 +16154,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16080
16154
  );
16081
16155
  color += calculateDiffuseTransmissionLight(
16082
16156
  pbrInfo,
16083
- lighting_getPointLight(i).color / attenuation,
16157
+ lighting_getPointLight(i).color * (i < 4 ? clamp(pointVisibility[min(i, 3)], 0.0, 1.0) : 1.0) / attenuation,
16084
16158
  diffuseTransmissionColor,
16085
16159
  diffuseTransmission,
16086
16160
  multiscatterColor,
@@ -16149,6 +16223,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16149
16223
  }
16150
16224
  #endif
16151
16225
 
16226
+ vec3 indirectBaseColor = color;
16152
16227
  // Calculate lighting contribution from image based lighting source (IBL)
16153
16228
  #ifdef USE_IBL
16154
16229
  if (pbrMaterial.IBLenabled) {
@@ -16183,6 +16258,8 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16183
16258
  (1.0 - diffuseTransmission) * baseLayerEnergy * lightmapIrradiance / M_PI;
16184
16259
  #endif
16185
16260
 
16261
+ color = indirectBaseColor + (color - indirectBaseColor) * pbr_ambientVisibility;
16262
+
16186
16263
  // Apply optional PBR terms for additional (optional) shading
16187
16264
  #ifdef HAS_OCCLUSIONMAP
16188
16265
  if (pbrMaterial.occlusionMapEnabled) {
@@ -16244,6 +16321,21 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16244
16321
  return vec4(applySceneColorManagement(color), alpha);
16245
16322
  }
16246
16323
 
16324
+ // Depth-only passes use the same base-color alpha, vertex color and UV transform.
16325
+ void pbr_discardMaskedFragment(vec4 vertexColor) {
16326
+ #ifdef ALPHA_CUTOFF
16327
+ float alpha = pbrMaterial.baseColorFactor.a * vertexColor.a;
16328
+ #ifdef HAS_BASECOLORMAP
16329
+ alpha *= texture(pbr_baseColorSampler, getMaterialUV(pbrMaterial.baseColorUVSet, pbrMaterial.baseColorUVTransform)).a;
16330
+ #endif
16331
+ if (alpha < pbrMaterial.alphaCutoff) {discard;}
16332
+ #endif
16333
+ }
16334
+
16335
+ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility) {
16336
+ return pbr_filterColorWithShadows(vertexColor, 1.0, vec4(1.0), spotVisibility);
16337
+ }
16338
+
16247
16339
  vec4 pbr_filterColor(vec4 vertexColor) {
16248
16340
  return pbr_filterColorWithSpotShadows(vertexColor, vec4(1.0));
16249
16341
  }
@@ -17368,7 +17460,9 @@ fn calculateFinalColor(pbrInfo: PBRInfo, lightColor: vec3<f32>) -> vec3<f32> {
17368
17460
  }
17369
17461
 
17370
17462
  // Visibility for the first four spotlights; environment and other lights are unaffected.
17371
- fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f) -> vec4<f32> {
17463
+ var<private> pbr_ambientVisibility: f32 = 1.0;
17464
+
17465
+ fn pbr_filterColorWithShadows(vertexColor: vec4<f32>, directionalVisibility: f32, pointVisibility: vec4f, spotVisibility: vec4f) -> vec4<f32> {
17372
17466
  let baseColorUV = getMaterialUV(pbrMaterial.baseColorUVSet, pbrMaterial.baseColorUVTransform);
17373
17467
  let metallicRoughnessUV = getMaterialUV(
17374
17468
  pbrMaterial.metallicRoughnessUVSet,
@@ -17461,7 +17555,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17461
17555
  color = baseColor.rgb;
17462
17556
  #ifdef HAS_LIGHTMAP
17463
17557
  if (pbrMaterial.lightmapMapEnabled != 0u) {
17464
- color *= lightmapIrradiance / M_PI;
17558
+ color *= lightmapIrradiance / M_PI * pbr_ambientVisibility;
17465
17559
  #ifdef HAS_OCCLUSIONMAP
17466
17560
  if (pbrMaterial.occlusionMapEnabled != 0) {
17467
17561
  let ao = textureSample(pbr_occlusionSampler, pbr_occlusionSamplerSampler, occlusionUV).r;
@@ -17564,12 +17658,12 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17564
17658
 
17565
17659
  #ifdef USE_LIGHTS
17566
17660
  PBRInfo_setAmbientLight(&pbrInfo);
17567
- color += calculateFinalColor(pbrInfo, lighting.ambientColor);
17661
+ color += calculateFinalColor(pbrInfo, lighting.ambientColor * pbr_ambientVisibility);
17568
17662
 
17569
17663
  for (var i = 0; i < lighting.directionalLightCount; i++) {
17570
17664
  if (i < lighting.directionalLightCount) {
17571
17665
  PBRInfo_setDirectionalLight(&pbrInfo, -lighting_getDirectionalLight(i).direction);
17572
- color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
17666
+ color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color * select(1.0, clamp(directionalVisibility, 0.0, 1.0), i == 0));
17573
17667
  }
17574
17668
  }
17575
17669
 
@@ -17580,7 +17674,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17580
17674
  lighting_getPointLight(i),
17581
17675
  distance(lighting_getPointLight(i).position, fragmentInputs.pbr_vPosition)
17582
17676
  );
17583
- color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
17677
+ color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color * select(1.0, clamp(pointVisibility[min(i, 3)], 0.0, 1.0), i < 4) / attenuation);
17584
17678
  }
17585
17679
  }
17586
17680
 
@@ -17609,7 +17703,8 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17609
17703
  }
17610
17704
  #endif
17611
17705
 
17612
- #ifdef USE_IBL
17706
+ let indirectBaseColor = color;
17707
+ #ifdef USE_IBL
17613
17708
  if (pbrMaterial.IBLenabled != 0) {
17614
17709
  color += getIBLContribution(pbrInfo, n, reflection);
17615
17710
  }
@@ -17617,6 +17712,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17617
17712
 
17618
17713
  // The pinned KHR_lightmap draft explicitly defines c_diff = (1 - metallic) * baseColor.
17619
17714
  color += baseColor.rgb * (1.0 - metallic) * lightmapIrradiance / M_PI;
17715
+ color = indirectBaseColor + (color - indirectBaseColor) * pbr_ambientVisibility;
17620
17716
 
17621
17717
  #ifdef HAS_OCCLUSIONMAP
17622
17718
  if (pbrMaterial.occlusionMapEnabled != 0) {
@@ -17915,7 +18011,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17915
18011
  PBRInfo_setAmbientLight(&pbrInfo);
17916
18012
  color += calculateMaterialLightColor(
17917
18013
  pbrInfo,
17918
- lighting.ambientColor,
18014
+ lighting.ambientColor * pbr_ambientVisibility,
17919
18015
  clearcoatNormal,
17920
18016
  clearcoatFactor,
17921
18017
  clearcoatRoughness,
@@ -17931,7 +18027,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17931
18027
  PBRInfo_setDirectionalLight(&pbrInfo, -lighting_getDirectionalLight(i).direction);
17932
18028
  color += calculateMaterialLightColor(
17933
18029
  pbrInfo,
17934
- lighting_getDirectionalLight(i).color,
18030
+ lighting_getDirectionalLight(i).color * select(1.0, clamp(directionalVisibility, 0.0, 1.0), i == 0),
17935
18031
  clearcoatNormal,
17936
18032
  clearcoatFactor,
17937
18033
  clearcoatRoughness,
@@ -17942,7 +18038,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17942
18038
  );
17943
18039
  color += calculateDiffuseTransmissionLight(
17944
18040
  pbrInfo,
17945
- lighting_getDirectionalLight(i).color,
18041
+ lighting_getDirectionalLight(i).color * select(1.0, clamp(directionalVisibility, 0.0, 1.0), i == 0),
17946
18042
  diffuseTransmissionColor,
17947
18043
  diffuseTransmission,
17948
18044
  multiscatterColor,
@@ -17961,7 +18057,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17961
18057
  );
17962
18058
  color += calculateMaterialLightColor(
17963
18059
  pbrInfo,
17964
- lighting_getPointLight(i).color / attenuation,
18060
+ lighting_getPointLight(i).color * select(1.0, clamp(pointVisibility[min(i, 3)], 0.0, 1.0), i < 4) / attenuation,
17965
18061
  clearcoatNormal,
17966
18062
  clearcoatFactor,
17967
18063
  clearcoatRoughness,
@@ -17972,7 +18068,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17972
18068
  );
17973
18069
  color += calculateDiffuseTransmissionLight(
17974
18070
  pbrInfo,
17975
- lighting_getPointLight(i).color / attenuation,
18071
+ lighting_getPointLight(i).color * select(1.0, clamp(pointVisibility[min(i, 3)], 0.0, 1.0), i < 4) / attenuation,
17976
18072
  diffuseTransmissionColor,
17977
18073
  diffuseTransmission,
17978
18074
  multiscatterColor,
@@ -18041,6 +18137,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
18041
18137
  }
18042
18138
  #endif
18043
18139
 
18140
+ let indirectBaseColor = color;
18044
18141
  // Calculate lighting contribution from image based lighting source (IBL)
18045
18142
  #ifdef USE_IBL
18046
18143
  if (pbrMaterial.IBLenabled != 0) {
@@ -18075,6 +18172,8 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
18075
18172
  (1.0 - diffuseTransmission) * baseLayerEnergy * lightmapIrradiance / M_PI;
18076
18173
  #endif
18077
18174
 
18175
+ color = indirectBaseColor + (color - indirectBaseColor) * pbr_ambientVisibility;
18176
+
18078
18177
  // Apply optional PBR terms for additional (optional) shading
18079
18178
  #ifdef HAS_OCCLUSIONMAP
18080
18179
  if (pbrMaterial.occlusionMapEnabled != 0) {
@@ -18137,6 +18236,21 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
18137
18236
  return vec4<f32>(applySceneColorManagement(color), alpha);
18138
18237
  }
18139
18238
 
18239
+ // Depth-only passes use the same base-color alpha, vertex color and UV transform.
18240
+ fn pbr_discardMaskedFragment(vertexColor: vec4f) {
18241
+ #ifdef ALPHA_CUTOFF
18242
+ var alpha = pbrMaterial.baseColorFactor.a * vertexColor.a;
18243
+ #ifdef HAS_BASECOLORMAP
18244
+ alpha *= textureSample(pbr_baseColorSampler, pbr_baseColorSamplerSampler, getMaterialUV(pbrMaterial.baseColorUVSet, pbrMaterial.baseColorUVTransform)).a;
18245
+ #endif
18246
+ if (alpha < pbrMaterial.alphaCutoff) {discard;}
18247
+ #endif
18248
+ }
18249
+
18250
+ fn pbr_filterColorWithSpotShadows(vertexColor: vec4f, spotVisibility: vec4f) -> vec4f {
18251
+ return pbr_filterColorWithShadows(vertexColor, 1.0, vec4f(1.0), spotVisibility);
18252
+ }
18253
+
18140
18254
  fn pbr_filterColor(vertexColor: vec4<f32>) -> vec4<f32> {
18141
18255
  return pbr_filterColorWithSpotShadows(vertexColor, vec4f(1.0));
18142
18256
  }
@@ -19874,6 +19988,1755 @@ fn globeClouds_getColor(camera: vec3f, direction: vec3f) -> vec4f {
19874
19988
  `
19875
19989
  )
19876
19990
  };
19991
+
19992
+ // src/modules/lighting/prefiltered-environment/prefiltered-environment.ts
19993
+ var GLSL_UNIFORMS = (
19994
+ /* glsl */
19995
+ `layout(std140) uniform pbrEnvironmentFilterUniforms {
19996
+ int face;
19997
+ float roughness;
19998
+ int mode;
19999
+ int sampleCount;
20000
+ int sourceEncoding;
20001
+ } pbrEnvironmentFilter;
20002
+
20003
+ uniform sampler2D pbrEnvironmentSource;
20004
+ `
20005
+ );
20006
+ var WGSL_UNIFORMS = (
20007
+ /* wgsl */
20008
+ `struct PBREnvironmentFilterUniforms {
20009
+ face: i32,
20010
+ roughness: f32,
20011
+ mode: i32,
20012
+ sampleCount: i32,
20013
+ sourceEncoding: i32,
20014
+ };
20015
+
20016
+ @group(0) @binding(auto) var<uniform> pbrEnvironmentFilter: PBREnvironmentFilterUniforms;
20017
+ @group(0) @binding(auto) var pbrEnvironmentSource: texture_2d<f32>;
20018
+ @group(0) @binding(auto) var pbrEnvironmentSourceSampler: sampler;
20019
+ `
20020
+ );
20021
+ var pbrEnvironmentFilter = {
20022
+ name: "pbrEnvironmentFilter",
20023
+ bindingLayout: [
20024
+ { name: "pbrEnvironmentFilter", group: 0 },
20025
+ { name: "pbrEnvironmentSource", group: 0 }
20026
+ ],
20027
+ fs: GLSL_UNIFORMS,
20028
+ source: WGSL_UNIFORMS,
20029
+ getUniforms: (properties) => properties,
20030
+ uniformTypes: {
20031
+ face: "i32",
20032
+ roughness: "f32",
20033
+ mode: "i32",
20034
+ sampleCount: "i32",
20035
+ sourceEncoding: "i32"
20036
+ },
20037
+ defaultUniforms: {
20038
+ face: 0,
20039
+ roughness: 0,
20040
+ mode: 0,
20041
+ sampleCount: 64,
20042
+ sourceEncoding: 0
20043
+ }
20044
+ };
20045
+ var PBR_ENVIRONMENT_FRAGMENT_GLSL = (
20046
+ /* glsl */
20047
+ `#version 300 es
20048
+ precision highp float;
20049
+ precision highp int;
20050
+
20051
+ in vec2 uv;
20052
+ out vec4 fragmentColor;
20053
+
20054
+ const float ENVIRONMENT_PI = 3.141592653589793;
20055
+
20056
+ vec2 getEnvironmentHammersley(uint index, uint sampleCount)
20057
+ {
20058
+ uint reversedBits = (index << 16u) | (index >> 16u);
20059
+ reversedBits = ((reversedBits & 0x55555555u) << 1u) |
20060
+ ((reversedBits & 0xAAAAAAAAu) >> 1u);
20061
+ reversedBits = ((reversedBits & 0x33333333u) << 2u) |
20062
+ ((reversedBits & 0xCCCCCCCCu) >> 2u);
20063
+ reversedBits = ((reversedBits & 0x0F0F0F0Fu) << 4u) |
20064
+ ((reversedBits & 0xF0F0F0F0u) >> 4u);
20065
+ reversedBits = ((reversedBits & 0x00FF00FFu) << 8u) |
20066
+ ((reversedBits & 0xFF00FF00u) >> 8u);
20067
+ return vec2(
20068
+ float(index) / float(sampleCount),
20069
+ float(reversedBits) * 2.3283064365386963e-10
20070
+ );
20071
+ }
20072
+
20073
+ vec3 getEnvironmentFaceDirection(int face, vec2 textureCoordinate)
20074
+ {
20075
+ vec2 coordinate = textureCoordinate * 2.0 - 1.0;
20076
+ if (face == 0) return normalize(vec3(1.0, -coordinate.y, -coordinate.x));
20077
+ if (face == 1) return normalize(vec3(-1.0, -coordinate.y, coordinate.x));
20078
+ if (face == 2) return normalize(vec3(coordinate.x, 1.0, coordinate.y));
20079
+ if (face == 3) return normalize(vec3(coordinate.x, -1.0, -coordinate.y));
20080
+ if (face == 4) return normalize(vec3(coordinate.x, -coordinate.y, 1.0));
20081
+ return normalize(vec3(-coordinate.x, -coordinate.y, -1.0));
20082
+ }
20083
+
20084
+ mat3 getEnvironmentTangentBasis(vec3 normal)
20085
+ {
20086
+ vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
20087
+ vec3 tangent = normalize(cross(up, normal));
20088
+ return mat3(tangent, cross(normal, tangent), normal);
20089
+ }
20090
+
20091
+ vec3 sampleEnvironmentDirection(vec3 direction)
20092
+ {
20093
+ vec3 normalizedDirection = normalize(direction);
20094
+ vec2 textureCoordinate = vec2(
20095
+ atan(normalizedDirection.z, normalizedDirection.x) / (2.0 * ENVIRONMENT_PI) + 0.5,
20096
+ acos(clamp(normalizedDirection.y, -1.0, 1.0)) / ENVIRONMENT_PI
20097
+ );
20098
+ vec3 color = texture(pbrEnvironmentSource, textureCoordinate).rgb;
20099
+ if (pbrEnvironmentFilter.sourceEncoding == 1) {
20100
+ vec3 lower = color / 12.92;
20101
+ vec3 upper = pow((color + 0.055) / 1.055, vec3(2.4));
20102
+ color = mix(lower, upper, step(vec3(0.04045), color));
20103
+ }
20104
+ return color;
20105
+ }
20106
+
20107
+ vec3 importanceSampleEnvironment(vec2 sequence, vec3 normal, float roughness)
20108
+ {
20109
+ float alpha = max(roughness * roughness, 0.0001);
20110
+ float azimuth = 2.0 * ENVIRONMENT_PI * sequence.x;
20111
+ float cosine = sqrt(
20112
+ (1.0 - sequence.y) / max(1.0 + (alpha * alpha - 1.0) * sequence.y, 0.0001)
20113
+ );
20114
+ float sine = sqrt(max(1.0 - cosine * cosine, 0.0));
20115
+ vec3 halfVector = vec3(cos(azimuth) * sine, sin(azimuth) * sine, cosine);
20116
+ return normalize(getEnvironmentTangentBasis(normal) * halfVector);
20117
+ }
20118
+
20119
+ float getEnvironmentGeometry(float normalDotDirection, float roughness)
20120
+ {
20121
+ float alpha = roughness * roughness;
20122
+ float factor = alpha * 0.5;
20123
+ return normalDotDirection / max(normalDotDirection * (1.0 - factor) + factor, 0.0001);
20124
+ }
20125
+
20126
+ vec2 integrateEnvironmentBRDF(vec2 textureCoordinate, int sampleCount)
20127
+ {
20128
+ float normalDotView = max(textureCoordinate.x, 0.001);
20129
+ float roughness = 1.0 - textureCoordinate.y;
20130
+ vec3 viewDirection = vec3(sqrt(max(1.0 - normalDotView * normalDotView, 0.0)), 0.0, normalDotView);
20131
+ vec2 integration = vec2(0.0);
20132
+
20133
+ for (int sampleIndex = 0; sampleIndex < 1024; sampleIndex++) {
20134
+ if (sampleIndex >= sampleCount) break;
20135
+ vec3 halfVector = importanceSampleEnvironment(
20136
+ getEnvironmentHammersley(uint(sampleIndex), uint(sampleCount)),
20137
+ vec3(0.0, 0.0, 1.0),
20138
+ roughness
20139
+ );
20140
+ vec3 lightDirection = normalize(2.0 * dot(viewDirection, halfVector) * halfVector - viewDirection);
20141
+ float normalDotLight = max(lightDirection.z, 0.0);
20142
+ float normalDotHalf = max(halfVector.z, 0.0);
20143
+ float viewDotHalf = max(dot(viewDirection, halfVector), 0.0);
20144
+ if (normalDotLight > 0.0) {
20145
+ float visibility = getEnvironmentGeometry(normalDotView, roughness) *
20146
+ getEnvironmentGeometry(normalDotLight, roughness) * viewDotHalf /
20147
+ max(normalDotHalf * normalDotView, 0.0001);
20148
+ float fresnel = pow(1.0 - viewDotHalf, 5.0);
20149
+ integration += vec2(1.0 - fresnel, fresnel) * visibility;
20150
+ }
20151
+ }
20152
+ return integration / float(sampleCount);
20153
+ }
20154
+
20155
+ vec3 integrateEnvironmentDirection(vec3 normal, int sampleCount)
20156
+ {
20157
+ vec3 accumulatedColor = vec3(0.0);
20158
+ float accumulatedWeight = 0.0;
20159
+
20160
+ for (int sampleIndex = 0; sampleIndex < 1024; sampleIndex++) {
20161
+ if (sampleIndex >= sampleCount) break;
20162
+ vec2 sequence = getEnvironmentHammersley(uint(sampleIndex), uint(sampleCount));
20163
+ vec3 direction;
20164
+ float sampleWeight;
20165
+
20166
+ if (pbrEnvironmentFilter.mode == 1) {
20167
+ float azimuth = 2.0 * ENVIRONMENT_PI * sequence.x;
20168
+ float cosine = sqrt(1.0 - sequence.y);
20169
+ float sine = sqrt(sequence.y);
20170
+ direction = normalize(
20171
+ getEnvironmentTangentBasis(normal) *
20172
+ vec3(cos(azimuth) * sine, sin(azimuth) * sine, cosine)
20173
+ );
20174
+ sampleWeight = 1.0;
20175
+ } else {
20176
+ vec3 halfVector = importanceSampleEnvironment(sequence, normal, pbrEnvironmentFilter.roughness);
20177
+ direction = normalize(2.0 * dot(normal, halfVector) * halfVector - normal);
20178
+ sampleWeight = max(dot(normal, direction), 0.0);
20179
+ }
20180
+
20181
+ accumulatedColor += sampleEnvironmentDirection(direction) * sampleWeight;
20182
+ accumulatedWeight += sampleWeight;
20183
+ }
20184
+
20185
+ return accumulatedColor / max(accumulatedWeight, 0.0001);
20186
+ }
20187
+
20188
+ void main(void)
20189
+ {
20190
+ int sampleCount = max(pbrEnvironmentFilter.sampleCount, 1);
20191
+ if (pbrEnvironmentFilter.mode == 2) {
20192
+ fragmentColor = vec4(integrateEnvironmentBRDF(uv, sampleCount), 0.0, 1.0);
20193
+ return;
20194
+ }
20195
+
20196
+ vec3 direction = getEnvironmentFaceDirection(pbrEnvironmentFilter.face, uv);
20197
+ fragmentColor = vec4(integrateEnvironmentDirection(direction, sampleCount), 1.0);
20198
+ }
20199
+ `
20200
+ );
20201
+ var PBR_ENVIRONMENT_FRAGMENT_WGSL = (
20202
+ /* wgsl */
20203
+ `const ENVIRONMENT_PI: f32 = 3.141592653589793;
20204
+
20205
+ fn getEnvironmentHammersley(index: u32, sampleCount: u32) -> vec2f {
20206
+ return vec2f(f32(index) / f32(sampleCount), f32(reverseBits(index)) * 2.3283064365386963e-10);
20207
+ }
20208
+
20209
+ fn getEnvironmentFaceDirection(face: i32, textureCoordinate: vec2f) -> vec3f {
20210
+ let coordinate = textureCoordinate * 2.0 - 1.0;
20211
+ if (face == 0) { return normalize(vec3f(1.0, -coordinate.y, -coordinate.x)); }
20212
+ if (face == 1) { return normalize(vec3f(-1.0, -coordinate.y, coordinate.x)); }
20213
+ if (face == 2) { return normalize(vec3f(coordinate.x, 1.0, coordinate.y)); }
20214
+ if (face == 3) { return normalize(vec3f(coordinate.x, -1.0, -coordinate.y)); }
20215
+ if (face == 4) { return normalize(vec3f(coordinate.x, -coordinate.y, 1.0)); }
20216
+ return normalize(vec3f(-coordinate.x, -coordinate.y, -1.0));
20217
+ }
20218
+
20219
+ fn getEnvironmentTangentBasis(normal: vec3f) -> mat3x3f {
20220
+ var up = vec3f(1.0, 0.0, 0.0);
20221
+ if (abs(normal.z) < 0.999) {
20222
+ up = vec3f(0.0, 0.0, 1.0);
20223
+ }
20224
+ let tangent = normalize(cross(up, normal));
20225
+ return mat3x3f(tangent, cross(normal, tangent), normal);
20226
+ }
20227
+
20228
+ fn sampleEnvironmentDirection(direction: vec3f) -> vec3f {
20229
+ let normalizedDirection = normalize(direction);
20230
+ let textureCoordinate = vec2f(
20231
+ atan2(normalizedDirection.z, normalizedDirection.x) / (2.0 * ENVIRONMENT_PI) + 0.5,
20232
+ acos(clamp(normalizedDirection.y, -1.0, 1.0)) / ENVIRONMENT_PI
20233
+ );
20234
+ var color = textureSampleLevel(
20235
+ pbrEnvironmentSource,
20236
+ pbrEnvironmentSourceSampler,
20237
+ textureCoordinate,
20238
+ 0.0
20239
+ ).rgb;
20240
+ if (pbrEnvironmentFilter.sourceEncoding == 1) {
20241
+ let lower = color / 12.92;
20242
+ let upper = pow((color + 0.055) / 1.055, vec3f(2.4));
20243
+ color = mix(lower, upper, step(vec3f(0.04045), color));
20244
+ }
20245
+ return color;
20246
+ }
20247
+
20248
+ fn importanceSampleEnvironment(sequence: vec2f, normal: vec3f, roughness: f32) -> vec3f {
20249
+ let alpha = max(roughness * roughness, 0.0001);
20250
+ let azimuth = 2.0 * ENVIRONMENT_PI * sequence.x;
20251
+ let cosine = sqrt(
20252
+ (1.0 - sequence.y) / max(1.0 + (alpha * alpha - 1.0) * sequence.y, 0.0001)
20253
+ );
20254
+ let sine = sqrt(max(1.0 - cosine * cosine, 0.0));
20255
+ let halfVector = vec3f(cos(azimuth) * sine, sin(azimuth) * sine, cosine);
20256
+ return normalize(getEnvironmentTangentBasis(normal) * halfVector);
20257
+ }
20258
+
20259
+ fn getEnvironmentGeometry(normalDotDirection: f32, roughness: f32) -> f32 {
20260
+ let alpha = roughness * roughness;
20261
+ let factor = alpha * 0.5;
20262
+ return normalDotDirection / max(normalDotDirection * (1.0 - factor) + factor, 0.0001);
20263
+ }
20264
+
20265
+ fn integrateEnvironmentBRDF(textureCoordinate: vec2f, sampleCount: u32) -> vec2f {
20266
+ let normalDotView = max(textureCoordinate.x, 0.001);
20267
+ let roughness = 1.0 - textureCoordinate.y;
20268
+ let viewDirection = vec3f(
20269
+ sqrt(max(1.0 - normalDotView * normalDotView, 0.0)),
20270
+ 0.0,
20271
+ normalDotView
20272
+ );
20273
+ var integration = vec2f(0.0);
20274
+
20275
+ for (var sampleIndex = 0u; sampleIndex < sampleCount; sampleIndex++) {
20276
+ let halfVector = importanceSampleEnvironment(
20277
+ getEnvironmentHammersley(sampleIndex, sampleCount),
20278
+ vec3f(0.0, 0.0, 1.0),
20279
+ roughness
20280
+ );
20281
+ let lightDirection = normalize(
20282
+ 2.0 * dot(viewDirection, halfVector) * halfVector - viewDirection
20283
+ );
20284
+ let normalDotLight = max(lightDirection.z, 0.0);
20285
+ let normalDotHalf = max(halfVector.z, 0.0);
20286
+ let viewDotHalf = max(dot(viewDirection, halfVector), 0.0);
20287
+ if (normalDotLight > 0.0) {
20288
+ let visibility = getEnvironmentGeometry(normalDotView, roughness) *
20289
+ getEnvironmentGeometry(normalDotLight, roughness) * viewDotHalf /
20290
+ max(normalDotHalf * normalDotView, 0.0001);
20291
+ let fresnel = pow(1.0 - viewDotHalf, 5.0);
20292
+ integration += vec2f(1.0 - fresnel, fresnel) * visibility;
20293
+ }
20294
+ }
20295
+ return integration / f32(sampleCount);
20296
+ }
20297
+
20298
+ fn integrateEnvironmentDirection(normal: vec3f, sampleCount: u32) -> vec3f {
20299
+ var accumulatedColor = vec3f(0.0);
20300
+ var accumulatedWeight = 0.0;
20301
+
20302
+ for (var sampleIndex = 0u; sampleIndex < sampleCount; sampleIndex++) {
20303
+ let sequence = getEnvironmentHammersley(sampleIndex, sampleCount);
20304
+ var direction: vec3f;
20305
+ var sampleWeight: f32;
20306
+
20307
+ if (pbrEnvironmentFilter.mode == 1) {
20308
+ let azimuth = 2.0 * ENVIRONMENT_PI * sequence.x;
20309
+ let cosine = sqrt(1.0 - sequence.y);
20310
+ let sine = sqrt(sequence.y);
20311
+ direction = normalize(
20312
+ getEnvironmentTangentBasis(normal) *
20313
+ vec3f(cos(azimuth) * sine, sin(azimuth) * sine, cosine)
20314
+ );
20315
+ sampleWeight = 1.0;
20316
+ } else {
20317
+ let halfVector = importanceSampleEnvironment(sequence, normal, pbrEnvironmentFilter.roughness);
20318
+ direction = normalize(2.0 * dot(normal, halfVector) * halfVector - normal);
20319
+ sampleWeight = max(dot(normal, direction), 0.0);
20320
+ }
20321
+
20322
+ accumulatedColor += sampleEnvironmentDirection(direction) * sampleWeight;
20323
+ accumulatedWeight += sampleWeight;
20324
+ }
20325
+ return accumulatedColor / max(accumulatedWeight, 0.0001);
20326
+ }
20327
+
20328
+ @fragment
20329
+ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
20330
+ // ClipSpace UVs increase upward; WebGPU texture rows increase downward.
20331
+ let textureCoordinate = vec2f(inputs.uv.x, 1.0 - inputs.uv.y);
20332
+ let sampleCount = u32(max(pbrEnvironmentFilter.sampleCount, 1));
20333
+ if (pbrEnvironmentFilter.mode == 2) {
20334
+ return vec4f(integrateEnvironmentBRDF(textureCoordinate, sampleCount), 0.0, 1.0);
20335
+ }
20336
+
20337
+ let direction = getEnvironmentFaceDirection(pbrEnvironmentFilter.face, textureCoordinate);
20338
+ return vec4f(integrateEnvironmentDirection(direction, sampleCount), 1.0);
20339
+ }
20340
+ `
20341
+ );
20342
+
20343
+ // src/modules/lighting/shadow/shadow-glsl.ts
20344
+ var SHADOW_GLSL = (
20345
+ /* glsl */
20346
+ `
20347
+ precision highp sampler2DArray;
20348
+ struct DirectionalShadowLightUniform {
20349
+ vec3 direction;
20350
+ float strength;
20351
+ float normalBias;
20352
+ float sourceAngularRadius;
20353
+ float cascadeBlendFraction;
20354
+ float farFadeFraction;
20355
+ float shadowDistance;
20356
+ };
20357
+ struct SpotShadowLightUniform {
20358
+ vec3 position;
20359
+ float range;
20360
+ vec3 direction;
20361
+ float outerConeCos;
20362
+ float sourceRadius;
20363
+ float normalBias;
20364
+ float strength;
20365
+ float nearPlane;
20366
+ };
20367
+ struct PointShadowLightUniform {
20368
+ vec3 position;
20369
+ float range;
20370
+ float sourceRadius;
20371
+ float normalBias;
20372
+ float strength;
20373
+ float nearPlane;
20374
+ };
20375
+ layout(std140) uniform shadowUniforms {
20376
+ int directionalLightCount;
20377
+ int spotLightCount;
20378
+ int pointLightCount;
20379
+ int cascadeCount;
20380
+ int blockerSampleCount;
20381
+ int filterSampleCount;
20382
+ vec4 cascadeSplits;
20383
+ mat4 directionalViewProjectionMatrices[4];
20384
+ mat4 spotViewProjectionMatrices[4];
20385
+ mat4 pointViewProjectionMatrices[24];
20386
+ DirectionalShadowLightUniform directionalLights[1];
20387
+ SpotShadowLightUniform spotLights[4];
20388
+ PointShadowLightUniform pointLights[4];
20389
+ } shadow;
20390
+ uniform sampler2DArray directionalShadowTexture;
20391
+ uniform sampler2DArray spotShadowTexture;
20392
+ uniform sampler2DArray pointShadowTexture;
20393
+ const float SHADOW_PI = 3.141592653589793;
20394
+ float shadow_hash(vec3 position) {
20395
+ return fract(sin(dot(position, vec3(12.9898, 78.233, 37.719))) * 43758.5453);
20396
+ }
20397
+ vec2 shadow_diskSample(int index, int count, float rotation) {
20398
+ float fraction = (float(index) + 0.5) / max(float(count), 1.0);
20399
+ float angle = float(index) * 2.39996323 + rotation;
20400
+ return sqrt(fraction) * vec2(cos(angle), sin(angle));
20401
+ }
20402
+ vec3 shadow_project(mat4 matrix, vec3 worldPosition) {
20403
+ vec4 clip = matrix * vec4(worldPosition, 1.0);
20404
+ vec3 projected = clip.xyz / max(abs(clip.w), 0.00001);
20405
+ // Shared matrices retain [0,1] depth; WebGL texture rows start at the bottom.
20406
+ return vec3(projected.xy * 0.5 + 0.5, projected.z);
20407
+ }
20408
+ bool shadow_validProjection(vec3 projected) {
20409
+ return all(greaterThanEqual(projected, vec3(0.0))) && all(lessThanEqual(projected, vec3(1.0)));
20410
+ }
20411
+ // Bilinear interpolation of four depth comparisons matches a hardware 2x2 PCF lookup.
20412
+ float shadow_compare(sampler2DArray shadowMap, vec2 coordinate, int layer, float reference) {
20413
+ ivec2 dimensions = textureSize(shadowMap, 0).xy;
20414
+ vec2 position = coordinate * vec2(dimensions) - 0.5;
20415
+ ivec2 base = ivec2(floor(position));
20416
+ vec2 fraction = fract(position);
20417
+ ivec2 maximum = dimensions - 1;
20418
+ float lowerLeft = step(reference, texelFetch(shadowMap, ivec3(clamp(base, ivec2(0), maximum), layer), 0).r);
20419
+ float lowerRight = step(reference, texelFetch(shadowMap, ivec3(clamp(base + ivec2(1,0), ivec2(0), maximum), layer), 0).r);
20420
+ float upperLeft = step(reference, texelFetch(shadowMap, ivec3(clamp(base + ivec2(0,1), ivec2(0), maximum), layer), 0).r);
20421
+ float upperRight = step(reference, texelFetch(shadowMap, ivec3(clamp(base + ivec2(1,1), ivec2(0), maximum), layer), 0).r);
20422
+ return mix(mix(lowerLeft, lowerRight, fraction.x), mix(upperLeft, upperRight, fraction.x), fraction.y);
20423
+ }
20424
+ float shadow_arrayPCSS(sampler2DArray shadowMap, int layer, vec3 projected, float searchRadius, float rotation, float penumbraScale) {
20425
+ if (!shadow_validProjection(projected)) return 1.0;
20426
+ float texel = 1.0 / float(textureSize(shadowMap, 0).x);
20427
+ searchRadius = max(texel, searchRadius);
20428
+ float blockerDepth = 0.0;
20429
+ float blockerCount = 0.0;
20430
+ for (int index = 0; index < 24; index++) {
20431
+ if (index >= shadow.blockerSampleCount) break;
20432
+ vec2 coordinate = clamp(projected.xy + shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius, vec2(0.0), vec2(1.0));
20433
+ float depth = textureLod(shadowMap, vec3(coordinate, float(layer)), 0.0).r;
20434
+ if (depth < projected.z) { blockerDepth += depth; blockerCount += 1.0; }
20435
+ }
20436
+ if (blockerCount == 0.0) return 1.0;
20437
+ float averageBlocker = blockerDepth / blockerCount;
20438
+ float penumbra = clamp((projected.z - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
20439
+ float radius = max(texel, searchRadius * (1.0 + penumbra * penumbraScale));
20440
+ float visibility = 0.0;
20441
+ for (int index = 0; index < 48; index++) {
20442
+ if (index >= shadow.filterSampleCount) break;
20443
+ vec2 coordinate = clamp(projected.xy + shadow_diskSample(index, shadow.filterSampleCount, rotation) * radius, vec2(0.0), vec2(1.0));
20444
+ visibility += shadow_compare(shadowMap, coordinate, layer, projected.z);
20445
+ }
20446
+ return visibility / max(float(shadow.filterSampleCount), 1.0);
20447
+ }
20448
+ int shadow_directionalCascadeIndex(float viewDepth) {
20449
+ int cascadeIndex = 0;
20450
+ for (int index = 0; index < 4; index++) {
20451
+ if (index >= shadow.cascadeCount) break;
20452
+ cascadeIndex = index;
20453
+ if (viewDepth <= shadow.cascadeSplits[index]) break;
20454
+ }
20455
+ return cascadeIndex;
20456
+ }
20457
+ int shadow_getDirectionalCascadeIndex(float viewDepth) {
20458
+ return shadow.directionalLightCount == 0 ? -1 : shadow_directionalCascadeIndex(viewDepth);
20459
+ }
20460
+ float shadow_directionalPCSS(int cascadeIndex, vec3 worldPosition, float viewDepth) {
20461
+ DirectionalShadowLightUniform light = shadow.directionalLights[0];
20462
+ return shadow_arrayPCSS(directionalShadowTexture, cascadeIndex,
20463
+ shadow_project(shadow.directionalViewProjectionMatrices[cascadeIndex], worldPosition),
20464
+ light.sourceAngularRadius * viewDepth / max(light.shadowDistance, 0.001),
20465
+ shadow_hash(worldPosition) * 2.0 * SHADOW_PI, 18.0);
20466
+ }
20467
+ float shadow_getDirectionalFactor(vec3 worldPosition, vec3 worldNormal, float viewDepth) {
20468
+ if (shadow.directionalLightCount == 0 || viewDepth <= 0.0) return 1.0;
20469
+ DirectionalShadowLightUniform light = shadow.directionalLights[0];
20470
+ if (viewDepth >= light.shadowDistance) return 1.0;
20471
+ vec3 position = worldPosition + normalize(worldNormal) * light.normalBias;
20472
+ int cascadeIndex = shadow_directionalCascadeIndex(viewDepth);
20473
+ float visibility = shadow_directionalPCSS(cascadeIndex, position, viewDepth);
20474
+ if (cascadeIndex + 1 < shadow.cascadeCount) {
20475
+ float cascadeStart = cascadeIndex > 0 ? shadow.cascadeSplits[cascadeIndex - 1] : 0.0;
20476
+ float cascadeEnd = shadow.cascadeSplits[cascadeIndex];
20477
+ float blendWidth = max((cascadeEnd - cascadeStart) * light.cascadeBlendFraction, 0.0001);
20478
+ float blend = smoothstep(cascadeEnd - blendWidth, cascadeEnd, viewDepth);
20479
+ if (blend > 0.0) visibility = mix(visibility, shadow_directionalPCSS(cascadeIndex + 1, position, viewDepth), blend);
20480
+ }
20481
+ float fadeWidth = max(light.shadowDistance * light.farFadeFraction, 0.0001);
20482
+ visibility = mix(visibility, 1.0, smoothstep(light.shadowDistance - fadeWidth, light.shadowDistance, viewDepth));
20483
+ return mix(1.0, visibility, light.strength);
20484
+ }
20485
+ float shadow_getSpotFactor(int lightIndex, vec3 worldPosition, vec3 worldNormal) {
20486
+ if (lightIndex < 0 || lightIndex >= shadow.spotLightCount) return 1.0;
20487
+ SpotShadowLightUniform light = shadow.spotLights[lightIndex];
20488
+ vec3 fromLight = worldPosition - light.position;
20489
+ float distanceToLight = length(fromLight);
20490
+ if (distanceToLight >= light.range || dot(normalize(fromLight), normalize(light.direction)) < light.outerConeCos) return 1.0;
20491
+ vec3 position = worldPosition + normalize(worldNormal) * light.normalBias;
20492
+ float visibility = shadow_arrayPCSS(spotShadowTexture, lightIndex,
20493
+ shadow_project(shadow.spotViewProjectionMatrices[lightIndex], position),
20494
+ light.sourceRadius / max(distanceToLight, 0.001) * 0.5,
20495
+ shadow_hash(worldPosition) * 2.0 * SHADOW_PI, 16.0);
20496
+ return mix(1.0, visibility, light.strength);
20497
+ }
20498
+ float shadow_pointReferenceDepth(float majorDistance, float nearPlane, float farPlane) {
20499
+ return farPlane / (farPlane - nearPlane) - (farPlane * nearPlane) / ((farPlane - nearPlane) * majorDistance);
20500
+ }
20501
+ vec3 shadow_pointCoordinate(int lightIndex, vec3 direction) {
20502
+ vec3 magnitude = abs(direction);
20503
+ int face = magnitude.x >= magnitude.y && magnitude.x >= magnitude.z ? (direction.x >= 0.0 ? 0 : 1)
20504
+ : magnitude.y >= magnitude.z ? (direction.y >= 0.0 ? 2 : 3) : (direction.z >= 0.0 ? 4 : 5);
20505
+ PointShadowLightUniform light = shadow.pointLights[lightIndex];
20506
+ vec3 projected = shadow_project(shadow.pointViewProjectionMatrices[lightIndex * 6 + face], light.position + direction * light.range * 0.5);
20507
+ return vec3(projected.xy, float(lightIndex * 6 + face));
20508
+ }
20509
+ float shadow_getPointFactor(int lightIndex, vec3 worldPosition, vec3 worldNormal) {
20510
+ if (lightIndex < 0 || lightIndex >= shadow.pointLightCount) return 1.0;
20511
+ PointShadowLightUniform light = shadow.pointLights[lightIndex];
20512
+ vec3 fromLight = worldPosition + normalize(worldNormal) * light.normalBias - light.position;
20513
+ float distanceToLight = length(fromLight);
20514
+ if (distanceToLight >= light.range || distanceToLight <= light.nearPlane) return 1.0;
20515
+ vec3 direction = normalize(fromLight);
20516
+ float referenceDepth = shadow_pointReferenceDepth(max(max(abs(fromLight.x), abs(fromLight.y)), abs(fromLight.z)), light.nearPlane, light.range);
20517
+ vec3 reference = abs(direction.y) > 0.99 ? vec3(0,0,1) : vec3(0,1,0);
20518
+ vec3 tangent = normalize(cross(direction, reference));
20519
+ vec3 bitangent = normalize(cross(direction, tangent));
20520
+ float searchRadius = light.sourceRadius / max(distanceToLight, 0.001);
20521
+ float rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;
20522
+ float blockerDepth = 0.0;
20523
+ float blockerCount = 0.0;
20524
+ for (int index = 0; index < 24; index++) {
20525
+ if (index >= shadow.blockerSampleCount) break;
20526
+ vec2 disk = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;
20527
+ vec3 coordinate = shadow_pointCoordinate(lightIndex, normalize(direction + tangent * disk.x + bitangent * disk.y));
20528
+ float depth = textureLod(pointShadowTexture, coordinate, 0.0).r;
20529
+ if (depth < referenceDepth) { blockerDepth += depth; blockerCount += 1.0; }
20530
+ }
20531
+ if (blockerCount == 0.0) return 1.0;
20532
+ float averageBlocker = blockerDepth / blockerCount;
20533
+ float penumbra = clamp((referenceDepth - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
20534
+ float radius = searchRadius * (1.0 + penumbra * 12.0);
20535
+ float visibility = 0.0;
20536
+ for (int index = 0; index < 48; index++) {
20537
+ if (index >= shadow.filterSampleCount) break;
20538
+ vec2 disk = shadow_diskSample(index, shadow.filterSampleCount, rotation) * radius;
20539
+ vec3 coordinate = shadow_pointCoordinate(lightIndex, normalize(direction + tangent * disk.x + bitangent * disk.y));
20540
+ visibility += shadow_compare(pointShadowTexture, coordinate.xy, int(coordinate.z), referenceDepth);
20541
+ }
20542
+ return mix(1.0, visibility / max(float(shadow.filterSampleCount), 1.0), light.strength);
20543
+ }
20544
+ `
20545
+ );
20546
+
20547
+ // src/modules/lighting/shadow/shadow.ts
20548
+ var MAX_CASCADES = 4;
20549
+ var MAX_SPOT_LIGHTS = 4;
20550
+ var MAX_POINT_LIGHTS = 4;
20551
+ var MAX_POINT_MATRICES = 24;
20552
+ var DIRECTIONAL_LIGHT_UNIFORM_TYPE = {
20553
+ direction: "vec3<f32>",
20554
+ strength: "f32",
20555
+ normalBias: "f32",
20556
+ sourceAngularRadius: "f32",
20557
+ cascadeBlendFraction: "f32",
20558
+ farFadeFraction: "f32",
20559
+ shadowDistance: "f32"
20560
+ };
20561
+ var SPOT_LIGHT_UNIFORM_TYPE = {
20562
+ position: "vec3<f32>",
20563
+ range: "f32",
20564
+ direction: "vec3<f32>",
20565
+ outerConeCos: "f32",
20566
+ sourceRadius: "f32",
20567
+ normalBias: "f32",
20568
+ strength: "f32",
20569
+ nearPlane: "f32"
20570
+ };
20571
+ var POINT_LIGHT_UNIFORM_TYPE = {
20572
+ position: "vec3<f32>",
20573
+ range: "f32",
20574
+ sourceRadius: "f32",
20575
+ normalBias: "f32",
20576
+ strength: "f32",
20577
+ nearPlane: "f32"
20578
+ };
20579
+ var SHADOW_WGSL = (
20580
+ /* wgsl */
20581
+ `const SHADOW_PI: f32 = 3.141592653589793;
20582
+
20583
+ struct DirectionalShadowLightUniform {
20584
+ direction: vec3f,
20585
+ strength: f32,
20586
+ normalBias: f32,
20587
+ sourceAngularRadius: f32,
20588
+ cascadeBlendFraction: f32,
20589
+ farFadeFraction: f32,
20590
+ shadowDistance: f32,
20591
+ };
20592
+
20593
+ struct SpotShadowLightUniform {
20594
+ position: vec3f,
20595
+ range: f32,
20596
+ direction: vec3f,
20597
+ outerConeCos: f32,
20598
+ sourceRadius: f32,
20599
+ normalBias: f32,
20600
+ strength: f32,
20601
+ nearPlane: f32,
20602
+ };
20603
+
20604
+ struct PointShadowLightUniform {
20605
+ position: vec3f,
20606
+ range: f32,
20607
+ sourceRadius: f32,
20608
+ normalBias: f32,
20609
+ strength: f32,
20610
+ nearPlane: f32,
20611
+ };
20612
+
20613
+ struct ShadowUniforms {
20614
+ directionalLightCount: i32,
20615
+ spotLightCount: i32,
20616
+ pointLightCount: i32,
20617
+ cascadeCount: i32,
20618
+ blockerSampleCount: i32,
20619
+ filterSampleCount: i32,
20620
+ cascadeSplits: vec4f,
20621
+ directionalViewProjectionMatrices: array<mat4x4f, 4>,
20622
+ spotViewProjectionMatrices: array<mat4x4f, 4>,
20623
+ pointViewProjectionMatrices: array<mat4x4f, 24>,
20624
+ directionalLights: array<DirectionalShadowLightUniform, 1>,
20625
+ spotLights: array<SpotShadowLightUniform, 4>,
20626
+ pointLights: array<PointShadowLightUniform, 4>,
20627
+ };
20628
+
20629
+ @group(2) @binding(auto) var<uniform> shadow: ShadowUniforms;
20630
+ @group(2) @binding(auto) var directionalShadowTexture: texture_depth_2d_array;
20631
+ @group(2) @binding(auto) var spotShadowTexture: texture_depth_2d_array;
20632
+ @group(2) @binding(auto) var pointShadowTexture: texture_depth_cube_array;
20633
+ @group(2) @binding(auto) var directionalShadowTextureSampler: sampler;
20634
+ @group(2) @binding(auto) var shadowComparisonSampler: sampler_comparison;
20635
+
20636
+ fn shadow_hash(position: vec3f) -> f32 {
20637
+ return fract(sin(dot(position, vec3f(12.9898, 78.233, 37.719))) * 43758.5453);
20638
+ }
20639
+
20640
+ fn shadow_diskSample(index: i32, count: i32, rotation: f32) -> vec2f {
20641
+ let fraction = (f32(index) + 0.5) / max(f32(count), 1.0);
20642
+ let angle = f32(index) * 2.39996323 + rotation;
20643
+ return sqrt(fraction) * vec2f(cos(angle), sin(angle));
20644
+ }
20645
+
20646
+ fn shadow_project(matrix: mat4x4f, worldPosition: vec3f) -> vec3f {
20647
+ let clip = matrix * vec4f(worldPosition, 1.0);
20648
+ let ndc = clip.xyz / max(abs(clip.w), 0.00001);
20649
+ return vec3f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5, ndc.z);
20650
+ }
20651
+
20652
+ fn shadow_validProjection(projected: vec3f) -> bool {
20653
+ return all(projected.xy >= vec2f(0.0)) && all(projected.xy <= vec2f(1.0)) &&
20654
+ projected.z >= 0.0 && projected.z <= 1.0;
20655
+ }
20656
+
20657
+ fn shadow_directionalCascadeIndex(viewDepth: f32) -> i32 {
20658
+ var cascadeIndex = 0;
20659
+ for (var index: i32 = 0; index < 4; index++) {
20660
+ if (index >= shadow.cascadeCount) { break; }
20661
+ cascadeIndex = index;
20662
+ if (viewDepth <= shadow.cascadeSplits[index]) { break; }
20663
+ }
20664
+ return cascadeIndex;
20665
+ }
20666
+
20667
+ fn shadow_getDirectionalCascadeIndex(viewDepth: f32) -> i32 {
20668
+ if (shadow.directionalLightCount == 0) { return -1; }
20669
+ return shadow_directionalCascadeIndex(viewDepth);
20670
+ }
20671
+
20672
+ fn shadow_directionalPCSS(cascadeIndex: i32, worldPosition: vec3f, viewDepth: f32) -> f32 {
20673
+ let projected = shadow_project(shadow.directionalViewProjectionMatrices[cascadeIndex], worldPosition);
20674
+ if (!shadow_validProjection(projected)) { return 1.0; }
20675
+ let dimensions = vec2f(textureDimensions(directionalShadowTexture));
20676
+ let texel = 1.0 / dimensions;
20677
+ let light = shadow.directionalLights[0];
20678
+ let rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;
20679
+ let searchRadius = max(texel.x, light.sourceAngularRadius * viewDepth / max(light.shadowDistance, 0.001));
20680
+ var blockerDepth = 0.0;
20681
+ var blockerCount = 0.0;
20682
+ for (var index: i32 = 0; index < 24; index++) {
20683
+ if (index >= shadow.blockerSampleCount) { break; }
20684
+ let offset = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;
20685
+ let sampleDepth = textureSampleLevel(
20686
+ directionalShadowTexture,
20687
+ directionalShadowTextureSampler,
20688
+ clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),
20689
+ cascadeIndex,
20690
+ 0
20691
+ );
20692
+ if (sampleDepth < projected.z) {
20693
+ blockerDepth += sampleDepth;
20694
+ blockerCount += 1.0;
20695
+ }
20696
+ }
20697
+ if (blockerCount == 0.0) { return 1.0; }
20698
+ let averageBlocker = blockerDepth / blockerCount;
20699
+ let penumbra = clamp((projected.z - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
20700
+ let filterRadius = max(texel.x, searchRadius * (1.0 + penumbra * 18.0));
20701
+ var visibility = 0.0;
20702
+ for (var index: i32 = 0; index < 48; index++) {
20703
+ if (index >= shadow.filterSampleCount) { break; }
20704
+ let offset = shadow_diskSample(index, shadow.filterSampleCount, rotation) * filterRadius;
20705
+ visibility += textureSampleCompareLevel(
20706
+ directionalShadowTexture,
20707
+ shadowComparisonSampler,
20708
+ clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),
20709
+ cascadeIndex,
20710
+ projected.z
20711
+ );
20712
+ }
20713
+ return visibility / max(f32(shadow.filterSampleCount), 1.0);
20714
+ }
20715
+
20716
+ fn shadow_getDirectionalFactor(worldPosition: vec3f, worldNormal: vec3f, viewDepth: f32) -> f32 {
20717
+ if (shadow.directionalLightCount == 0 || viewDepth <= 0.0) { return 1.0; }
20718
+ let light = shadow.directionalLights[0];
20719
+ if (viewDepth >= light.shadowDistance) { return 1.0; }
20720
+ let biasedPosition = worldPosition + normalize(worldNormal) * light.normalBias;
20721
+ let cascadeIndex = shadow_directionalCascadeIndex(viewDepth);
20722
+ var visibility = shadow_directionalPCSS(cascadeIndex, biasedPosition, viewDepth);
20723
+ if (cascadeIndex + 1 < shadow.cascadeCount) {
20724
+ var cascadeStart = 0.0;
20725
+ if (cascadeIndex > 0) { cascadeStart = shadow.cascadeSplits[cascadeIndex - 1]; }
20726
+ let cascadeEnd = shadow.cascadeSplits[cascadeIndex];
20727
+ let blendWidth = max((cascadeEnd - cascadeStart) * light.cascadeBlendFraction, 0.0001);
20728
+ let blend = smoothstep(cascadeEnd - blendWidth, cascadeEnd, viewDepth);
20729
+ if (blend > 0.0) {
20730
+ visibility = mix(
20731
+ visibility,
20732
+ shadow_directionalPCSS(cascadeIndex + 1, biasedPosition, viewDepth),
20733
+ blend
20734
+ );
20735
+ }
20736
+ }
20737
+ let fadeWidth = max(light.shadowDistance * light.farFadeFraction, 0.0001);
20738
+ let farFade = smoothstep(light.shadowDistance - fadeWidth, light.shadowDistance, viewDepth);
20739
+ visibility = mix(visibility, 1.0, farFade);
20740
+ return mix(1.0, visibility, light.strength);
20741
+ }
20742
+
20743
+ fn shadow_spotPCSS(lightIndex: i32, worldPosition: vec3f) -> f32 {
20744
+ let projected = shadow_project(shadow.spotViewProjectionMatrices[lightIndex], worldPosition);
20745
+ if (!shadow_validProjection(projected)) { return 1.0; }
20746
+ let light = shadow.spotLights[lightIndex];
20747
+ let dimensions = vec2f(textureDimensions(spotShadowTexture));
20748
+ let texel = 1.0 / dimensions;
20749
+ let distanceToLight = length(worldPosition - light.position);
20750
+ let searchRadius = max(texel.x, light.sourceRadius / max(distanceToLight, 0.001) * 0.5);
20751
+ let rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;
20752
+ var blockerDepth = 0.0;
20753
+ var blockerCount = 0.0;
20754
+ for (var index: i32 = 0; index < 24; index++) {
20755
+ if (index >= shadow.blockerSampleCount) { break; }
20756
+ let offset = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;
20757
+ let sampleDepth = textureSampleLevel(
20758
+ spotShadowTexture,
20759
+ directionalShadowTextureSampler,
20760
+ clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),
20761
+ lightIndex,
20762
+ 0
20763
+ );
20764
+ if (sampleDepth < projected.z) { blockerDepth += sampleDepth; blockerCount += 1.0; }
20765
+ }
20766
+ if (blockerCount == 0.0) { return 1.0; }
20767
+ let averageBlocker = blockerDepth / blockerCount;
20768
+ let penumbra = clamp((projected.z - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
20769
+ let filterRadius = max(texel.x, searchRadius * (1.0 + penumbra * 16.0));
20770
+ var visibility = 0.0;
20771
+ for (var index: i32 = 0; index < 48; index++) {
20772
+ if (index >= shadow.filterSampleCount) { break; }
20773
+ let offset = shadow_diskSample(index, shadow.filterSampleCount, rotation) * filterRadius;
20774
+ visibility += textureSampleCompareLevel(
20775
+ spotShadowTexture,
20776
+ shadowComparisonSampler,
20777
+ clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),
20778
+ lightIndex,
20779
+ projected.z
20780
+ );
20781
+ }
20782
+ return visibility / max(f32(shadow.filterSampleCount), 1.0);
20783
+ }
20784
+
20785
+ fn shadow_getSpotFactor(lightIndex: i32, worldPosition: vec3f, worldNormal: vec3f) -> f32 {
20786
+ if (lightIndex < 0 || lightIndex >= shadow.spotLightCount) { return 1.0; }
20787
+ let light = shadow.spotLights[lightIndex];
20788
+ let fromLight = worldPosition - light.position;
20789
+ let distanceToLight = length(fromLight);
20790
+ let coneCos = dot(normalize(fromLight), normalize(light.direction));
20791
+ if (distanceToLight >= light.range || coneCos < light.outerConeCos) { return 1.0; }
20792
+ let biasedPosition = worldPosition + normalize(worldNormal) * light.normalBias;
20793
+ return mix(1.0, shadow_spotPCSS(lightIndex, biasedPosition), light.strength);
20794
+ }
20795
+
20796
+ fn shadow_pointReferenceDepth(majorDistance: f32, nearPlane: f32, farPlane: f32) -> f32 {
20797
+ // Match the cube-face projection and stored WebGPU depth in [0, 1].
20798
+ return farPlane / (farPlane - nearPlane) -
20799
+ (farPlane * nearPlane) / ((farPlane - nearPlane) * majorDistance);
20800
+ }
20801
+
20802
+ fn shadow_pointBasis(direction: vec3f) -> mat2x3f {
20803
+ let reference = select(vec3f(0.0, 1.0, 0.0), vec3f(0.0, 0.0, 1.0), abs(direction.y) > 0.99);
20804
+ let tangent = normalize(cross(direction, reference));
20805
+ return mat2x3f(tangent, normalize(cross(direction, tangent)));
20806
+ }
20807
+
20808
+ fn shadow_getPointFactor(lightIndex: i32, worldPosition: vec3f, worldNormal: vec3f) -> f32 {
20809
+ if (lightIndex < 0 || lightIndex >= shadow.pointLightCount) { return 1.0; }
20810
+ let light = shadow.pointLights[lightIndex];
20811
+ let biasedPosition = worldPosition + normalize(worldNormal) * light.normalBias;
20812
+ let fromLight = biasedPosition - light.position;
20813
+ let distanceToLight = length(fromLight);
20814
+ if (distanceToLight >= light.range || distanceToLight <= light.nearPlane) { return 1.0; }
20815
+ let direction = normalize(fromLight);
20816
+ let majorDistance = max(max(abs(fromLight.x), abs(fromLight.y)), abs(fromLight.z));
20817
+ let referenceDepth = shadow_pointReferenceDepth(majorDistance, light.nearPlane, light.range);
20818
+ let searchRadius = light.sourceRadius / max(distanceToLight, 0.001);
20819
+ let rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;
20820
+ let basis = shadow_pointBasis(direction);
20821
+ var blockerDepth = 0.0;
20822
+ var blockerCount = 0.0;
20823
+ for (var index: i32 = 0; index < 24; index++) {
20824
+ if (index >= shadow.blockerSampleCount) { break; }
20825
+ let disk = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;
20826
+ let sampleDirection = normalize(direction + basis[0] * disk.x + basis[1] * disk.y);
20827
+ let sampleDepth = textureSampleLevel(
20828
+ pointShadowTexture,
20829
+ directionalShadowTextureSampler,
20830
+ sampleDirection,
20831
+ lightIndex,
20832
+ 0
20833
+ );
20834
+ if (sampleDepth < referenceDepth) { blockerDepth += sampleDepth; blockerCount += 1.0; }
20835
+ }
20836
+ if (blockerCount == 0.0) { return 1.0; }
20837
+ let averageBlocker = blockerDepth / blockerCount;
20838
+ let penumbra = clamp((referenceDepth - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
20839
+ let filterRadius = searchRadius * (1.0 + penumbra * 12.0);
20840
+ var visibility = 0.0;
20841
+ for (var index: i32 = 0; index < 48; index++) {
20842
+ if (index >= shadow.filterSampleCount) { break; }
20843
+ let disk = shadow_diskSample(index, shadow.filterSampleCount, rotation) * filterRadius;
20844
+ let sampleDirection = normalize(direction + basis[0] * disk.x + basis[1] * disk.y);
20845
+ visibility += textureSampleCompareLevel(
20846
+ pointShadowTexture,
20847
+ shadowComparisonSampler,
20848
+ sampleDirection,
20849
+ lightIndex,
20850
+ referenceDepth
20851
+ );
20852
+ }
20853
+ visibility /= max(f32(shadow.filterSampleCount), 1.0);
20854
+ return mix(1.0, visibility, light.strength);
20855
+ }
20856
+ `
20857
+ );
20858
+ var shadow = {
20859
+ name: "shadow",
20860
+ source: SHADOW_WGSL,
20861
+ fs: SHADOW_GLSL,
20862
+ props: {},
20863
+ uniforms: {},
20864
+ bindings: {},
20865
+ uniformTypes: {
20866
+ directionalLightCount: "i32",
20867
+ spotLightCount: "i32",
20868
+ pointLightCount: "i32",
20869
+ cascadeCount: "i32",
20870
+ blockerSampleCount: "i32",
20871
+ filterSampleCount: "i32",
20872
+ cascadeSplits: "vec4<f32>",
20873
+ directionalViewProjectionMatrices: ["mat4x4<f32>", MAX_CASCADES],
20874
+ spotViewProjectionMatrices: ["mat4x4<f32>", MAX_SPOT_LIGHTS],
20875
+ pointViewProjectionMatrices: ["mat4x4<f32>", MAX_POINT_MATRICES],
20876
+ directionalLights: [DIRECTIONAL_LIGHT_UNIFORM_TYPE, 1],
20877
+ spotLights: [SPOT_LIGHT_UNIFORM_TYPE, MAX_SPOT_LIGHTS],
20878
+ pointLights: [POINT_LIGHT_UNIFORM_TYPE, MAX_POINT_LIGHTS]
20879
+ },
20880
+ bindingLayout: [
20881
+ { name: "shadow", group: 2 },
20882
+ { name: "directionalShadowTexture", group: 2 },
20883
+ { name: "spotShadowTexture", group: 2 },
20884
+ { name: "pointShadowTexture", group: 2 },
20885
+ { name: "shadowComparisonSampler", group: 2 }
20886
+ ],
20887
+ getUniforms(props) {
20888
+ 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) {
20889
+ return {};
20890
+ }
20891
+ return {
20892
+ directionalLightCount: props.directionalLights.length,
20893
+ spotLightCount: props.spotLights.length,
20894
+ pointLightCount: props.pointLights.length,
20895
+ cascadeCount: props.cascadeCount,
20896
+ blockerSampleCount: props.blockerSampleCount,
20897
+ filterSampleCount: props.filterSampleCount,
20898
+ cascadeSplits: props.cascadeSplits,
20899
+ directionalViewProjectionMatrices: props.directionalViewProjectionMatrices,
20900
+ spotViewProjectionMatrices: props.spotViewProjectionMatrices,
20901
+ pointViewProjectionMatrices: props.pointViewProjectionMatrices,
20902
+ directionalLights: padDirectionalLights(props.directionalLights),
20903
+ spotLights: padSpotLights(props.spotLights),
20904
+ pointLights: padPointLights(props.pointLights),
20905
+ directionalShadowTexture: props.directionalShadowTexture,
20906
+ spotShadowTexture: props.spotShadowTexture,
20907
+ pointShadowTexture: props.pointShadowTexture,
20908
+ ...props.directionalShadowTexture.device.type === "webgpu" ? { shadowComparisonSampler: props.comparisonSampler } : {}
20909
+ };
20910
+ }
20911
+ };
20912
+ function padDirectionalLights(lights) {
20913
+ const light = lights[0];
20914
+ return [
20915
+ light ? {
20916
+ direction: light.direction,
20917
+ strength: light.strength ?? 1,
20918
+ normalBias: light.normalBias ?? 0.04,
20919
+ sourceAngularRadius: light.sourceAngularRadius ?? 465e-5,
20920
+ cascadeBlendFraction: light.cascadeBlendFraction ?? 0.1,
20921
+ farFadeFraction: light.farFadeFraction ?? 0.1,
20922
+ shadowDistance: light.shadowDistance ?? 1
20923
+ } : {
20924
+ direction: [0, 1, 0],
20925
+ strength: 0,
20926
+ normalBias: 0,
20927
+ sourceAngularRadius: 0,
20928
+ cascadeBlendFraction: 0,
20929
+ farFadeFraction: 0,
20930
+ shadowDistance: 1
20931
+ }
20932
+ ];
20933
+ }
20934
+ function padSpotLights(lights) {
20935
+ return padArray(
20936
+ lights.map((light) => ({
20937
+ position: light.position,
20938
+ range: light.range,
20939
+ direction: light.direction,
20940
+ outerConeCos: Math.cos(light.outerConeAngle),
20941
+ sourceRadius: light.sourceRadius ?? 0.2,
20942
+ normalBias: light.normalBias ?? 0.025,
20943
+ strength: light.strength ?? 1,
20944
+ nearPlane: light.nearPlane ?? 0.1
20945
+ })),
20946
+ MAX_SPOT_LIGHTS,
20947
+ {
20948
+ position: [0, 0, 0],
20949
+ range: 1,
20950
+ direction: [0, -1, 0],
20951
+ outerConeCos: 1,
20952
+ sourceRadius: 0,
20953
+ normalBias: 0,
20954
+ strength: 0,
20955
+ nearPlane: 0.1
20956
+ }
20957
+ );
20958
+ }
20959
+ function padPointLights(lights) {
20960
+ return padArray(
20961
+ lights.map((light) => ({
20962
+ position: light.position,
20963
+ range: light.range,
20964
+ sourceRadius: light.sourceRadius ?? 0.2,
20965
+ normalBias: light.normalBias ?? 0.025,
20966
+ strength: light.strength ?? 1,
20967
+ nearPlane: light.nearPlane ?? 0.1
20968
+ })),
20969
+ MAX_POINT_LIGHTS,
20970
+ {
20971
+ position: [0, 0, 0],
20972
+ range: 1,
20973
+ sourceRadius: 0,
20974
+ normalBias: 0,
20975
+ strength: 0,
20976
+ nearPlane: 0.1
20977
+ }
20978
+ );
20979
+ }
20980
+ function padArray(values, length, fallback) {
20981
+ const result = values.slice(0, length);
20982
+ while (result.length < length) {
20983
+ result.push(fallback);
20984
+ }
20985
+ return result;
20986
+ }
20987
+
20988
+ // src/modules/lighting/pbr-shadow/pbr-shadow.ts
20989
+ var pbrShadow = {
20990
+ name: "pbrShadow",
20991
+ dependencies: [pbrMaterial, shadow],
20992
+ bindingLayout: [{ name: "pbrShadow", group: 0 }],
20993
+ uniformTypes: { viewMatrix: "mat4x4<f32>", enabled: "i32" },
20994
+ defaultUniforms: {
20995
+ viewMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
20996
+ enabled: 1
20997
+ },
20998
+ getUniforms(properties = {}) {
20999
+ return {
21000
+ ...properties.viewMatrix ? { viewMatrix: properties.viewMatrix } : {},
21001
+ ...properties.enabled === void 0 ? {} : { enabled: Number(properties.enabled) }
21002
+ };
21003
+ },
21004
+ source: (
21005
+ /* wgsl */
21006
+ `
21007
+ struct PBRShadowUniforms {
21008
+ viewMatrix: mat4x4f,
21009
+ enabled: i32,
21010
+ };
21011
+ @group(0) @binding(auto) var<uniform> pbrShadow: PBRShadowUniforms;
21012
+ fn pbrShadow_filterColorWithDirectionalContact(vertexColor: vec4f, worldPosition: vec3f, worldNormal: vec3f, contactVisibility: f32) -> vec4f {
21013
+ var directionalVisibility = 1.0;
21014
+ var pointVisibility = vec4f(1.0);
21015
+ var spotVisibility = vec4f(1.0);
21016
+ if (pbrShadow.enabled != 0) {
21017
+ let normal = normalize(worldNormal);
21018
+ let viewDepth = -(pbrShadow.viewMatrix * vec4f(worldPosition, 1.0)).z;
21019
+ directionalVisibility = shadow_getDirectionalFactor(worldPosition, normal, viewDepth);
21020
+ for (var index = 0; index < 4; index++) {
21021
+ pointVisibility[index] = shadow_getPointFactor(index, worldPosition, normal);
21022
+ spotVisibility[index] = shadow_getSpotFactor(index, worldPosition, normal);
21023
+ }
21024
+ }
21025
+ return pbr_filterColorWithShadows(vertexColor, min(directionalVisibility, contactVisibility), pointVisibility, spotVisibility);
21026
+ }
21027
+ fn pbrShadow_filterColor(vertexColor: vec4f, worldPosition: vec3f, worldNormal: vec3f) -> vec4f {
21028
+ return pbrShadow_filterColorWithDirectionalContact(vertexColor, worldPosition, worldNormal, 1.0);
21029
+ }`
21030
+ ),
21031
+ fs: (
21032
+ /* glsl */
21033
+ `
21034
+ layout(std140) uniform pbrShadowUniforms {
21035
+ mat4 viewMatrix;
21036
+ int enabled;
21037
+ } pbrShadow;
21038
+ vec4 pbrShadow_filterColorWithDirectionalContact(vec4 vertexColor, vec3 worldPosition, vec3 worldNormal, float contactVisibility) {
21039
+ float directionalVisibility = 1.0;
21040
+ vec4 pointVisibility = vec4(1.0);
21041
+ vec4 spotVisibility = vec4(1.0);
21042
+ if (pbrShadow.enabled != 0) {
21043
+ vec3 normal = normalize(worldNormal);
21044
+ float viewDepth = -(pbrShadow.viewMatrix * vec4(worldPosition, 1.0)).z;
21045
+ directionalVisibility = shadow_getDirectionalFactor(worldPosition, normal, viewDepth);
21046
+ for (int index = 0; index < 4; index++) {
21047
+ pointVisibility[index] = shadow_getPointFactor(index, worldPosition, normal);
21048
+ spotVisibility[index] = shadow_getSpotFactor(index, worldPosition, normal);
21049
+ }
21050
+ }
21051
+ return pbr_filterColorWithShadows(vertexColor, min(directionalVisibility, contactVisibility), pointVisibility, spotVisibility);
21052
+ }
21053
+ vec4 pbrShadow_filterColor(vec4 vertexColor, vec3 worldPosition, vec3 worldNormal) {
21054
+ return pbrShadow_filterColorWithDirectionalContact(vertexColor, worldPosition, worldNormal, 1.0);
21055
+ }`
21056
+ )
21057
+ };
21058
+
21059
+ // src/modules/lighting/pbr-surface/pbr-surface.ts
21060
+ var pbrSurface = {
21061
+ name: "pbrSurface",
21062
+ dependencies: [pbrMaterial],
21063
+ bindingLayout: [{ name: "pbrSurface", group: 0 }],
21064
+ uniformTypes: { viewMatrix: "mat4x4<f32>", useMaterialNormals: "i32" },
21065
+ defaultUniforms: {
21066
+ viewMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
21067
+ useMaterialNormals: 0
21068
+ },
21069
+ getUniforms(properties = {}) {
21070
+ return {
21071
+ ...properties.viewMatrix ? { viewMatrix: properties.viewMatrix } : {},
21072
+ ...properties.useMaterialNormals === void 0 ? {} : { useMaterialNormals: Number(properties.useMaterialNormals) }
21073
+ };
21074
+ },
21075
+ source: (
21076
+ /* wgsl */
21077
+ `
21078
+ struct PBRSurfaceUniforms {
21079
+ viewMatrix: mat4x4f,
21080
+ useMaterialNormals: i32,
21081
+ };
21082
+ @group(0) @binding(auto) var<uniform> pbrSurface: PBRSurfaceUniforms;
21083
+ fn pbrSurface_getColor() -> vec4f {
21084
+ #ifdef HAS_NORMALS
21085
+ var normal = normalize(fragmentInputs.pbr_vNormal);
21086
+ #ifdef DOUBLE_SIDED
21087
+ if (!pbr_frontFacing) {normal = -normal;}
21088
+ #endif
21089
+ #else
21090
+ var normal = normalize(cross(dpdy(fragmentInputs.pbr_vPosition), dpdx(fragmentInputs.pbr_vPosition)));
21091
+ #endif
21092
+ #ifdef HAS_NORMALMAP
21093
+ if (pbrSurface.useMaterialNormals != 0) {
21094
+ let coordinates = getMaterialUV(pbrMaterial.normalUVSet, pbrMaterial.normalUVTransform);
21095
+ normal = getNormal(getTBN(coordinates), coordinates);
21096
+ }
21097
+ #else
21098
+ #ifdef HAS_BUMPMAP
21099
+ if (pbrSurface.useMaterialNormals != 0) {
21100
+ let coordinates = getMaterialUV(pbrMaterial.normalUVSet, pbrMaterial.normalUVTransform);
21101
+ normal = getNormal(getTBN(coordinates), coordinates);
21102
+ }
21103
+ #endif
21104
+ #endif
21105
+ let viewNormal = normalize((pbrSurface.viewMatrix * vec4f(normal, 0.0)).xyz);
21106
+ return vec4f(viewNormal * 0.5 + vec3f(0.5), 1.0);
21107
+ }`
21108
+ ),
21109
+ fs: (
21110
+ /* glsl */
21111
+ `
21112
+ layout(std140) uniform pbrSurfaceUniforms {
21113
+ mat4 viewMatrix;
21114
+ int useMaterialNormals;
21115
+ } pbrSurface;
21116
+ vec4 pbrSurface_getColor() {
21117
+ #ifdef HAS_NORMALS
21118
+ #ifdef HAS_TANGENTS
21119
+ vec3 normal = normalize(pbr_vTBN[2]);
21120
+ #else
21121
+ vec3 normal = normalize(pbr_vNormal);
21122
+ #endif
21123
+ #ifdef DOUBLE_SIDED
21124
+ if (!gl_FrontFacing) normal = -normal;
21125
+ #endif
21126
+ #else
21127
+ vec3 normal = normalize(cross(dFdx(pbr_vPosition), dFdy(pbr_vPosition)));
21128
+ #endif
21129
+ #ifdef HAS_NORMALMAP
21130
+ if (pbrSurface.useMaterialNormals != 0) {
21131
+ vec2 coordinates = getMaterialUV(pbrMaterial.normalUVSet, pbrMaterial.normalUVTransform);
21132
+ normal = getNormal(getTBN(coordinates), coordinates);
21133
+ }
21134
+ #else
21135
+ #ifdef HAS_BUMPMAP
21136
+ if (pbrSurface.useMaterialNormals != 0) {
21137
+ vec2 coordinates = getMaterialUV(pbrMaterial.normalUVSet, pbrMaterial.normalUVTransform);
21138
+ normal = getNormal(getTBN(coordinates), coordinates);
21139
+ }
21140
+ #endif
21141
+ #endif
21142
+ vec3 viewNormal = normalize((pbrSurface.viewMatrix * vec4(normal, 0.0)).xyz);
21143
+ return vec4(viewNormal * 0.5 + 0.5, 1.0);
21144
+ }`
21145
+ )
21146
+ };
21147
+
21148
+ // src/modules/lighting/contact-shadow/contact-shadow.ts
21149
+ var IDENTITY_MATRIX3 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
21150
+ var contactShadowTrace = {
21151
+ name: "contactShadowTrace",
21152
+ source: (
21153
+ /* wgsl */
21154
+ `struct contactShadowTraceUniforms {
21155
+ projectionMatrix: mat4x4f,
21156
+ inverseProjectionMatrix: mat4x4f,
21157
+ lightDirectionView: vec3f,
21158
+ maxDistance: f32,
21159
+ thickness: f32,
21160
+ stepCount: f32,
21161
+ frameIndex: f32,
21162
+ normalBias: f32,
21163
+ };
21164
+ @group(0) @binding(auto) var<uniform> contactShadowTrace: contactShadowTraceUniforms;
21165
+ @group(0) @binding(auto) var depthTexture: texture_depth_2d;
21166
+ @group(0) @binding(auto) var depthTextureSampler: sampler;
21167
+ @group(0) @binding(auto) var normalTexture: texture_2d<f32>;
21168
+ @group(0) @binding(auto) var normalTextureSampler: sampler;
21169
+
21170
+ fn contactShadow_reconstructViewPosition(uv: vec2f, depth: f32) -> vec3f {
21171
+ let clip = vec4f(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, depth, 1.0);
21172
+ let view = contactShadowTrace.inverseProjectionMatrix * clip;
21173
+ return view.xyz / max(abs(view.w), 0.00001);
21174
+ }
21175
+
21176
+ fn contactShadow_projectViewPosition(position: vec3f) -> vec2f {
21177
+ let clip = contactShadowTrace.projectionMatrix * vec4f(position, 1.0);
21178
+ let ndc = clip.xy / max(abs(clip.w), 0.00001);
21179
+ return vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
21180
+ }
21181
+
21182
+ fn contactShadowTrace_sampleColor(
21183
+ sourceTexture: texture_2d<f32>, sourceTextureSampler: sampler, texSize: vec2f, texCoord: vec2f
21184
+ ) -> vec4f {
21185
+ let centerDepth = textureSampleLevel(depthTexture, depthTextureSampler, texCoord, 0);
21186
+ if (centerDepth >= 0.99999) { return vec4f(1.0); }
21187
+ let centerNormal = normalize(textureSampleLevel(normalTexture, normalTextureSampler, texCoord, 0.0).xyz * 2.0 - 1.0);
21188
+ let centerPosition = contactShadow_reconstructViewPosition(texCoord, centerDepth) + centerNormal * contactShadowTrace.normalBias;
21189
+ let rayDirection = normalize(contactShadowTrace.lightDirectionView);
21190
+ let noise = fract(sin(dot(texCoord * texSize + contactShadowTrace.frameIndex, vec2f(12.9898, 78.233))) * 43758.5453);
21191
+ for (var index: i32 = 0; index < 40; index++) {
21192
+ if (f32(index) >= contactShadowTrace.stepCount) { break; }
21193
+ let fraction = (f32(index) + 0.35 + noise * 0.5) / max(contactShadowTrace.stepCount, 1.0);
21194
+ let travel = contactShadowTrace.maxDistance * fraction * fraction;
21195
+ let rayPosition = centerPosition + rayDirection * travel;
21196
+ let sampleUv = contactShadow_projectViewPosition(rayPosition);
21197
+ if (any(sampleUv <= vec2f(0.0)) || any(sampleUv >= vec2f(1.0))) { break; }
21198
+ let sampleDepth = textureSampleLevel(depthTexture, depthTextureSampler, sampleUv, 0);
21199
+ if (sampleDepth >= 0.99999) { continue; }
21200
+ let scenePosition = contactShadow_reconstructViewPosition(sampleUv, sampleDepth);
21201
+ let depthDelta = (-rayPosition.z) - (-scenePosition.z);
21202
+ let hitThickness = contactShadowTrace.thickness + travel * 0.02;
21203
+ if (depthDelta >= 0.0 && depthDelta <= hitThickness) {
21204
+ let distanceFade = 1.0 - clamp(travel / contactShadowTrace.maxDistance, 0.0, 1.0);
21205
+ return vec4f(vec3f(1.0 - distanceFade), 1.0);
21206
+ }
21207
+ if (depthDelta > hitThickness * 8.0) { break; }
21208
+ }
21209
+ return vec4f(1.0);
21210
+ }`
21211
+ ),
21212
+ fs: (
21213
+ /* glsl */
21214
+ `
21215
+ layout(std140) uniform contactShadowTraceUniforms {
21216
+ mat4 projectionMatrix;
21217
+ mat4 inverseProjectionMatrix;
21218
+ vec3 lightDirectionView;
21219
+ float maxDistance;
21220
+ float thickness;
21221
+ float stepCount;
21222
+ float frameIndex;
21223
+ float normalBias;
21224
+ } contactShadowTrace;
21225
+ uniform sampler2D depthTexture;
21226
+ uniform sampler2D normalTexture;
21227
+ vec3 contactShadow_reconstructViewPosition(vec2 uv, float depth) {
21228
+ vec4 view = contactShadowTrace.inverseProjectionMatrix * vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
21229
+ return view.xyz / max(abs(view.w), 0.00001);
21230
+ }
21231
+ vec2 contactShadow_projectViewPosition(vec3 position) {
21232
+ vec4 clip = contactShadowTrace.projectionMatrix * vec4(position,1.0);
21233
+ return clip.xy / max(abs(clip.w),0.00001) * 0.5 + 0.5;
21234
+ }
21235
+ vec4 contactShadowTrace_sampleColor(sampler2D sourceTexture,vec2 texSize,vec2 texCoord) {
21236
+ float centerDepth = texture(depthTexture,texCoord).r;
21237
+ if(centerDepth >= 0.99999) return vec4(1.0);
21238
+ vec3 centerNormal = normalize(texture(normalTexture,texCoord).xyz * 2.0 - 1.0);
21239
+ vec3 centerPosition = contactShadow_reconstructViewPosition(texCoord,centerDepth) + centerNormal * contactShadowTrace.normalBias;
21240
+ vec3 rayDirection = normalize(contactShadowTrace.lightDirectionView);
21241
+ float noise = fract(sin(dot(texCoord * texSize + contactShadowTrace.frameIndex,vec2(12.9898,78.233))) * 43758.5453);
21242
+ for(int index=0;index<40;index++) {
21243
+ if(float(index)>=contactShadowTrace.stepCount) break;
21244
+ float fraction=(float(index)+0.35+noise*0.5)/max(contactShadowTrace.stepCount,1.0);
21245
+ float travel=contactShadowTrace.maxDistance*fraction*fraction;
21246
+ vec3 rayPosition=centerPosition+rayDirection*travel;
21247
+ vec2 sampleUv=contactShadow_projectViewPosition(rayPosition);
21248
+ if(any(lessThanEqual(sampleUv,vec2(0.0)))||any(greaterThanEqual(sampleUv,vec2(1.0))))break;
21249
+ float sampleDepth=texture(depthTexture,sampleUv).r;
21250
+ if(sampleDepth>=0.99999)continue;
21251
+ vec3 scenePosition=contactShadow_reconstructViewPosition(sampleUv,sampleDepth);
21252
+ float depthDelta=(-rayPosition.z)-(-scenePosition.z);
21253
+ float hitThickness=contactShadowTrace.thickness+travel*0.02;
21254
+ if(depthDelta>=0.0&&depthDelta<=hitThickness) {
21255
+ float distanceFade=1.0-clamp(travel/contactShadowTrace.maxDistance,0.0,1.0);
21256
+ return vec4(vec3(1.0-distanceFade),1.0);
21257
+ }
21258
+ if(depthDelta>hitThickness*8.0)break;
21259
+ }
21260
+ return vec4(1.0);
21261
+ }
21262
+ `
21263
+ ),
21264
+ bindingLayout: [
21265
+ { name: "depthTexture", group: 0 },
21266
+ { name: "normalTexture", group: 0 }
21267
+ ],
21268
+ props: {},
21269
+ uniforms: {},
21270
+ bindings: {},
21271
+ uniformTypes: {
21272
+ projectionMatrix: "mat4x4<f32>",
21273
+ inverseProjectionMatrix: "mat4x4<f32>",
21274
+ lightDirectionView: "vec3<f32>",
21275
+ maxDistance: "f32",
21276
+ thickness: "f32",
21277
+ stepCount: "f32",
21278
+ frameIndex: "f32",
21279
+ normalBias: "f32"
21280
+ },
21281
+ propTypes: {
21282
+ projectionMatrix: { value: IDENTITY_MATRIX3, private: true },
21283
+ inverseProjectionMatrix: { value: IDENTITY_MATRIX3, private: true },
21284
+ lightDirectionView: { value: [0, 1, 0], private: true },
21285
+ maxDistance: { value: 4, min: 0.1, softMax: 16 },
21286
+ thickness: { value: 0.12, min: 5e-3, softMax: 1 },
21287
+ stepCount: { value: 24, min: 1, max: 40 },
21288
+ frameIndex: { value: 0, private: true },
21289
+ normalBias: { value: 0.035, min: 0, softMax: 0.2 }
21290
+ },
21291
+ passes: [{ sampler: true }]
21292
+ };
21293
+ var contactShadowBilateralBlur = {
21294
+ name: "contactShadowBilateralBlur",
21295
+ source: (
21296
+ /* wgsl */
21297
+ `struct contactShadowBilateralBlurUniforms {
21298
+ direction: vec2f,
21299
+ radius: f32,
21300
+ depthSigma: f32,
21301
+ normalPower: f32,
21302
+ };
21303
+ @group(0) @binding(auto) var<uniform> contactShadowBilateralBlur: contactShadowBilateralBlurUniforms;
21304
+ @group(0) @binding(auto) var depthTexture: texture_depth_2d;
21305
+ @group(0) @binding(auto) var depthTextureSampler: sampler;
21306
+ @group(0) @binding(auto) var normalTexture: texture_2d<f32>;
21307
+ @group(0) @binding(auto) var normalTextureSampler: sampler;
21308
+
21309
+ fn contactShadowBilateralBlur_sampleColor(
21310
+ sourceTexture: texture_2d<f32>, sourceTextureSampler: sampler, texSize: vec2f, texCoord: vec2f
21311
+ ) -> vec4f {
21312
+ let sourceDimensions = vec2f(textureDimensions(sourceTexture));
21313
+ let texel = contactShadowBilateralBlur.direction / sourceDimensions;
21314
+ let centerDepth = textureSampleLevel(depthTexture, depthTextureSampler, texCoord, 0);
21315
+ let centerNormal = normalize(textureSampleLevel(normalTexture, normalTextureSampler, texCoord, 0.0).xyz * 2.0 - 1.0);
21316
+ var result = textureSampleLevel(sourceTexture, sourceTextureSampler, texCoord, 0.0).r;
21317
+ var totalWeight = 1.0;
21318
+ for (var index: i32 = 1; index <= 6; index++) {
21319
+ if (f32(index) > contactShadowBilateralBlur.radius) { break; }
21320
+ for (var side: i32 = -1; side <= 1; side += 2) {
21321
+ let sampleUv = clamp(texCoord + texel * f32(index * side), vec2f(0.0), vec2f(1.0));
21322
+ let sampleDepth = textureSampleLevel(depthTexture, depthTextureSampler, sampleUv, 0);
21323
+ let sampleNormal = normalize(textureSampleLevel(normalTexture, normalTextureSampler, sampleUv, 0.0).xyz * 2.0 - 1.0);
21324
+ let depthDelta = abs(sampleDepth - centerDepth);
21325
+ let depthWeight = exp(-(depthDelta * depthDelta) / max(2.0 * contactShadowBilateralBlur.depthSigma * contactShadowBilateralBlur.depthSigma, 0.000001));
21326
+ let normalWeight = pow(max(dot(centerNormal, sampleNormal), 0.0), contactShadowBilateralBlur.normalPower);
21327
+ let spatialWeight = exp(-f32(index * index) / 8.0);
21328
+ let weight = depthWeight * normalWeight * spatialWeight;
21329
+ result += textureSampleLevel(sourceTexture, sourceTextureSampler, sampleUv, 0.0).r * weight;
21330
+ totalWeight += weight;
21331
+ }
21332
+ }
21333
+ let visibility = result / totalWeight;
21334
+ return vec4f(vec3f(visibility), 1.0);
21335
+ }`
21336
+ ),
21337
+ fs: (
21338
+ /* glsl */
21339
+ `
21340
+ layout(std140) uniform contactShadowBilateralBlurUniforms {
21341
+ vec2 direction;
21342
+ float radius;
21343
+ float depthSigma;
21344
+ float normalPower;
21345
+ } contactShadowBilateralBlur;
21346
+ uniform sampler2D depthTexture;
21347
+ uniform sampler2D normalTexture;
21348
+ vec4 contactShadowBilateralBlur_sampleColor(sampler2D sourceTexture,vec2 texSize,vec2 texCoord) {
21349
+ vec2 texel=contactShadowBilateralBlur.direction/vec2(textureSize(sourceTexture,0));
21350
+ float centerDepth=texture(depthTexture,texCoord).r;
21351
+ vec3 centerNormal=normalize(texture(normalTexture,texCoord).xyz*2.0-1.0);
21352
+ float result=texture(sourceTexture,texCoord).r;
21353
+ float totalWeight=1.0;
21354
+ for(int index=1;index<=6;index++) {
21355
+ if(float(index)>contactShadowBilateralBlur.radius)break;
21356
+ for(int side=-1;side<=1;side+=2) {
21357
+ vec2 sampleUv=clamp(texCoord+texel*float(index*side),vec2(0.0),vec2(1.0));
21358
+ float sampleDepth=texture(depthTexture,sampleUv).r;
21359
+ vec3 sampleNormal=normalize(texture(normalTexture,sampleUv).xyz*2.0-1.0);
21360
+ float depthDelta=abs(sampleDepth-centerDepth);
21361
+ float depthWeight=exp(-(depthDelta*depthDelta)/max(2.0*contactShadowBilateralBlur.depthSigma*contactShadowBilateralBlur.depthSigma,0.000001));
21362
+ float normalWeight=pow(max(dot(centerNormal,sampleNormal),0.0),contactShadowBilateralBlur.normalPower);
21363
+ float spatialWeight=exp(-float(index*index)/8.0);
21364
+ float weight=depthWeight*normalWeight*spatialWeight;
21365
+ result+=texture(sourceTexture,sampleUv).r*weight;totalWeight+=weight;
21366
+ }
21367
+ }
21368
+ return vec4(vec3(result/totalWeight),1.0);
21369
+ }
21370
+ `
21371
+ ),
21372
+ bindingLayout: [
21373
+ { name: "depthTexture", group: 0 },
21374
+ { name: "normalTexture", group: 0 }
21375
+ ],
21376
+ props: {},
21377
+ uniforms: {},
21378
+ bindings: {},
21379
+ uniformTypes: {
21380
+ direction: "vec2<f32>",
21381
+ radius: "f32",
21382
+ depthSigma: "f32",
21383
+ normalPower: "f32"
21384
+ },
21385
+ propTypes: {
21386
+ direction: { value: [1, 0], private: true },
21387
+ radius: { value: 4, min: 1, max: 6 },
21388
+ depthSigma: { value: 6e-3, min: 1e-4, softMax: 0.05 },
21389
+ normalPower: { value: 24, min: 1, softMax: 64 }
21390
+ },
21391
+ passes: [{ sampler: true }]
21392
+ };
21393
+ var contactShadowComposite = {
21394
+ name: "contactShadowComposite",
21395
+ source: (
21396
+ /* wgsl */
21397
+ `struct contactShadowCompositeUniforms {
21398
+ strength: f32,
21399
+ debugMode: f32,
21400
+ };
21401
+ @group(0) @binding(auto) var<uniform> contactShadowComposite: contactShadowCompositeUniforms;
21402
+ @group(0) @binding(auto) var contactShadowTexture: texture_2d<f32>;
21403
+ @group(0) @binding(auto) var contactShadowTextureSampler: sampler;
21404
+ @group(0) @binding(auto) var directionalDirectTexture: texture_2d<f32>;
21405
+ @group(0) @binding(auto) var directionalDirectTextureSampler: sampler;
21406
+ @group(0) @binding(auto) var shadowDebugTexture: texture_2d<f32>;
21407
+ @group(0) @binding(auto) var shadowDebugTextureSampler: sampler;
21408
+
21409
+ fn contactShadowComposite_sampleColor(
21410
+ sourceTexture: texture_2d<f32>, sourceTextureSampler: sampler, texSize: vec2f, texCoord: vec2f
21411
+ ) -> vec4f {
21412
+ let color = textureSampleLevel(sourceTexture, sourceTextureSampler, texCoord, 0.0);
21413
+ let contact = textureSampleLevel(contactShadowTexture, contactShadowTextureSampler, texCoord, 0.0).r;
21414
+ let appliedContact = mix(1.0, contact, contactShadowComposite.strength);
21415
+ if (contactShadowComposite.debugMode > 0.5 && contactShadowComposite.debugMode < 1.5) {
21416
+ return vec4f(vec3f(contact), 1.0);
21417
+ }
21418
+ if (contactShadowComposite.debugMode >= 1.5) {
21419
+ let factors = textureSampleLevel(shadowDebugTexture, shadowDebugTextureSampler, texCoord, 0.0).rgb;
21420
+ return vec4f(vec3f(factors.x * factors.y * factors.z * contact), 1.0);
21421
+ }
21422
+ let directionalDirect = textureSampleLevel(
21423
+ directionalDirectTexture,
21424
+ directionalDirectTextureSampler,
21425
+ texCoord,
21426
+ 0.0
21427
+ ).rgb;
21428
+ return vec4f(max(color.rgb - directionalDirect * (1.0 - appliedContact), vec3f(0.0)), color.a);
21429
+ }`
21430
+ ),
21431
+ fs: (
21432
+ /* glsl */
21433
+ `
21434
+ layout(std140) uniform contactShadowCompositeUniforms {
21435
+ float strength;
21436
+ float debugMode;
21437
+ } contactShadowComposite;
21438
+ uniform sampler2D contactShadowTexture;
21439
+ uniform sampler2D directionalDirectTexture;
21440
+ uniform sampler2D shadowDebugTexture;
21441
+ vec4 contactShadowComposite_sampleColor(sampler2D sourceTexture,vec2 texSize,vec2 texCoord) {
21442
+ vec4 color=texture(sourceTexture,texCoord);
21443
+ float contact=texture(contactShadowTexture,texCoord).r;
21444
+ float appliedContact=mix(1.0,contact,contactShadowComposite.strength);
21445
+ if(contactShadowComposite.debugMode>0.5&&contactShadowComposite.debugMode<1.5)return vec4(vec3(contact),1.0);
21446
+ if(contactShadowComposite.debugMode>=1.5){vec3 factors=texture(shadowDebugTexture,texCoord).rgb;return vec4(vec3(factors.x*factors.y*factors.z*contact),1.0);}
21447
+ vec3 directionalDirect=texture(directionalDirectTexture,texCoord).rgb;
21448
+ return vec4(max(color.rgb-directionalDirect*(1.0-appliedContact),vec3(0.0)),color.a);
21449
+ }
21450
+ `
21451
+ ),
21452
+ bindingLayout: [
21453
+ { name: "contactShadowTexture", group: 0 },
21454
+ { name: "directionalDirectTexture", group: 0 },
21455
+ { name: "shadowDebugTexture", group: 0 }
21456
+ ],
21457
+ props: {},
21458
+ uniforms: {},
21459
+ bindings: {},
21460
+ uniformTypes: { strength: "f32", debugMode: "f32" },
21461
+ propTypes: {
21462
+ strength: { value: 1, min: 0, max: 1 },
21463
+ debugMode: { value: 0, min: 0, max: 2, private: true }
21464
+ },
21465
+ passes: [{ sampler: true }]
21466
+ };
21467
+
21468
+ // src/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.ts
21469
+ var pbrContactShadow = {
21470
+ name: "pbrContactShadow",
21471
+ dependencies: [pbrMaterial],
21472
+ bindingLayout: [
21473
+ { name: "pbrContactShadow", group: 0 },
21474
+ { name: "contactShadowTexture", group: 2 }
21475
+ ],
21476
+ uniformTypes: { viewport: "vec4<f32>", strength: "f32", enabled: "i32" },
21477
+ defaultUniforms: { viewport: [0, 0, 1, 1], strength: 1, enabled: 1 },
21478
+ getUniforms(properties = {}) {
21479
+ return {
21480
+ ...properties.viewport ? { viewport: properties.viewport } : {},
21481
+ ...properties.strength === void 0 ? {} : { strength: properties.strength },
21482
+ ...properties.enabled === void 0 ? {} : { enabled: Number(properties.enabled) },
21483
+ ...properties.contactShadowTexture ? { contactShadowTexture: properties.contactShadowTexture } : {}
21484
+ };
21485
+ },
21486
+ source: (
21487
+ /* wgsl */
21488
+ `
21489
+ struct PBRContactShadowUniforms {
21490
+ viewport: vec4f,
21491
+ strength: f32,
21492
+ enabled: i32,
21493
+ };
21494
+ @group(0) @binding(auto) var<uniform> pbrContactShadow: PBRContactShadowUniforms;
21495
+ @group(2) @binding(auto) var contactShadowTexture: texture_2d<f32>;
21496
+ @group(2) @binding(auto) var contactShadowTextureSampler: sampler;
21497
+ fn pbrContactShadow_getFactor(pixelPosition:vec2f)->f32 {
21498
+ if(pbrContactShadow.enabled==0){return 1.0;}
21499
+ let coordinates=(pixelPosition-pbrContactShadow.viewport.xy)/pbrContactShadow.viewport.zw;
21500
+ if(any(coordinates<vec2f(0.0))||any(coordinates>vec2f(1.0))){return 1.0;}
21501
+ let visibility=textureSampleLevel(contactShadowTexture,contactShadowTextureSampler,coordinates,0.0).r;
21502
+ return mix(1.0,clamp(visibility,0.0,1.0),clamp(pbrContactShadow.strength,0.0,1.0));
21503
+ }
21504
+ fn pbrContactShadow_filterColor(vertexColor:vec4f,pixelPosition:vec2f)->vec4f {
21505
+ return pbr_filterColorWithShadows(vertexColor,pbrContactShadow_getFactor(pixelPosition),vec4f(1.0),vec4f(1.0));
21506
+ }`
21507
+ ),
21508
+ fs: (
21509
+ /* glsl */
21510
+ `
21511
+ layout(std140) uniform pbrContactShadowUniforms {
21512
+ vec4 viewport;
21513
+ float strength;
21514
+ int enabled;
21515
+ } pbrContactShadow;
21516
+ uniform sampler2D contactShadowTexture;
21517
+ float pbrContactShadow_getFactor(vec2 pixelPosition) {
21518
+ if(pbrContactShadow.enabled==0)return 1.0;
21519
+ vec2 coordinates=(pixelPosition-pbrContactShadow.viewport.xy)/pbrContactShadow.viewport.zw;
21520
+ if(any(lessThan(coordinates,vec2(0.0)))||any(greaterThan(coordinates,vec2(1.0))))return 1.0;
21521
+ float visibility=texture(contactShadowTexture,coordinates).r;
21522
+ return mix(1.0,clamp(visibility,0.0,1.0),clamp(pbrContactShadow.strength,0.0,1.0));
21523
+ }
21524
+ vec4 pbrContactShadow_filterColor(vec4 vertexColor,vec2 pixelPosition) {
21525
+ return pbr_filterColorWithShadows(vertexColor,pbrContactShadow_getFactor(pixelPosition),vec4(1.0),vec4(1.0));
21526
+ }`
21527
+ )
21528
+ };
21529
+
21530
+ // src/modules/lighting/clustered-light-grid/clustered-light-grid.ts
21531
+ var clusteredLightGrid = {
21532
+ name: "clusteredLightGrid",
21533
+ source: (
21534
+ /* wgsl */
21535
+ `
21536
+ fn clusteredLightGrid_getIndex(coordinates: vec2f, viewDepth: f32, dimensions: vec3u, nearPlane: f32, farPlane: f32) -> u32 {
21537
+ let tile = min(vec2u(clamp(coordinates, vec2f(0.0), vec2f(0.999999)) * vec2f(dimensions.xy)), dimensions.xy - vec2u(1));
21538
+ let normalizedDepth = clamp(log(clamp(viewDepth, nearPlane, farPlane) / nearPlane) / log(farPlane / nearPlane), 0.0, 0.999999);
21539
+ let depthSlice = min(u32(normalizedDepth * f32(dimensions.z)), dimensions.z - 1u);
21540
+ return (depthSlice * dimensions.y + tile.y) * dimensions.x + tile.x;
21541
+ }
21542
+ `
21543
+ )
21544
+ };
21545
+
21546
+ // src/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.ts
21547
+ var pbrAmbientOcclusion = {
21548
+ name: "pbrAmbientOcclusion",
21549
+ dependencies: [pbrMaterial],
21550
+ bindingLayout: [
21551
+ { name: "pbrAmbientOcclusion", group: 0 },
21552
+ { name: "ambientOcclusionTexture", group: 2 }
21553
+ ],
21554
+ uniformTypes: { viewport: "vec4<f32>", strength: "f32", enabled: "i32" },
21555
+ defaultUniforms: { viewport: [0, 0, 1, 1], strength: 1, enabled: 1 },
21556
+ getUniforms(properties = {}) {
21557
+ return {
21558
+ ...properties.viewport ? { viewport: properties.viewport } : {},
21559
+ ...properties.strength === void 0 ? {} : { strength: properties.strength },
21560
+ ...properties.enabled === void 0 ? {} : { enabled: Number(properties.enabled) },
21561
+ ...properties.ambientOcclusionTexture ? { ambientOcclusionTexture: properties.ambientOcclusionTexture } : {}
21562
+ };
21563
+ },
21564
+ source: (
21565
+ /* wgsl */
21566
+ `
21567
+ struct PBRAmbientOcclusionUniforms {
21568
+ viewport: vec4f,
21569
+ strength: f32,
21570
+ enabled: i32,
21571
+ };
21572
+ @group(0) @binding(auto) var<uniform> pbrAmbientOcclusion: PBRAmbientOcclusionUniforms;
21573
+ @group(2) @binding(auto) var ambientOcclusionTexture: texture_2d<f32>;
21574
+ @group(2) @binding(auto) var ambientOcclusionTextureSampler: sampler;
21575
+ fn pbrAmbientOcclusion_getFactor(pixelPosition:vec2f)->f32 {
21576
+ if(pbrAmbientOcclusion.enabled==0){return 1.0;}
21577
+ let coordinates=(pixelPosition-pbrAmbientOcclusion.viewport.xy)/pbrAmbientOcclusion.viewport.zw;
21578
+ if(any(coordinates<vec2f(0.0))||any(coordinates>vec2f(1.0))){return 1.0;}
21579
+ let visibility=textureSampleLevel(ambientOcclusionTexture,ambientOcclusionTextureSampler,coordinates,0.0).r;
21580
+ return mix(1.0,clamp(visibility,0.0,1.0),clamp(pbrAmbientOcclusion.strength,0.0,1.0));
21581
+ }
21582
+ fn pbrAmbientOcclusion_setFactor(pixelPosition:vec2f) {
21583
+ pbr_ambientVisibility = pbrAmbientOcclusion_getFactor(pixelPosition);
21584
+ }`
21585
+ ),
21586
+ fs: (
21587
+ /* glsl */
21588
+ `
21589
+ layout(std140) uniform pbrAmbientOcclusionUniforms {
21590
+ vec4 viewport;
21591
+ float strength;
21592
+ int enabled;
21593
+ } pbrAmbientOcclusion;
21594
+ uniform sampler2D ambientOcclusionTexture;
21595
+ float pbrAmbientOcclusion_getFactor(vec2 pixelPosition) {
21596
+ if(pbrAmbientOcclusion.enabled==0)return 1.0;
21597
+ vec2 coordinates=(pixelPosition-pbrAmbientOcclusion.viewport.xy)/pbrAmbientOcclusion.viewport.zw;
21598
+ if(any(lessThan(coordinates,vec2(0.0)))||any(greaterThan(coordinates,vec2(1.0))))return 1.0;
21599
+ float visibility=texture(ambientOcclusionTexture,coordinates).r;
21600
+ return mix(1.0,clamp(visibility,0.0,1.0),clamp(pbrAmbientOcclusion.strength,0.0,1.0));
21601
+ }
21602
+ void pbrAmbientOcclusion_setFactor(vec2 pixelPosition) {
21603
+ pbr_ambientVisibility = pbrAmbientOcclusion_getFactor(pixelPosition);
21604
+ }`
21605
+ )
21606
+ };
21607
+
21608
+ // src/modules/lighting/lightmap/lightmap.ts
21609
+ var lightmapBake = {
21610
+ name: "lightmapBake",
21611
+ dependencies: [pbrMaterial],
21612
+ bindingLayout: [{ name: "lightmapBake", group: 0 }],
21613
+ uniformTypes: { uvSet: "i32", uvTransform: "mat3x3<f32>", chartId: "f32" },
21614
+ defaultUniforms: { uvSet: 1, uvTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1], chartId: 1 },
21615
+ getUniforms: (props) => props,
21616
+ source: `
21617
+ struct LightmapBakeUniforms {uvSet: i32, uvTransform: mat3x3f, chartId: f32};
21618
+ @group(0) @binding(auto) var<uniform> lightmapBake: LightmapBakeUniforms;
21619
+
21620
+ fn lightmap_getIrradiance(position: vec3f, normal: vec3f, directionalVisibility: f32, pointVisibility: vec4f, spotVisibility: vec4f) -> vec3f {
21621
+ if (lighting.enabled == 0) {return vec3f(0.0);}
21622
+ var irradiance = lighting.ambientColor;
21623
+ for (var index = 0; index < lighting.directionalLightCount; index++) {
21624
+ let light = lighting_getDirectionalLight(index);
21625
+ irradiance += light.color * max(dot(normal, -normalize(light.direction)), 0.0) * select(1.0, directionalVisibility, index == 0);
21626
+ }
21627
+ for (var index = 0; index < lighting.pointLightCount; index++) {
21628
+ let light = lighting_getPointLight(index);
21629
+ let offset = light.position - position;
21630
+ let visibility = select(1.0, pointVisibility[min(index, 3)], index < 4);
21631
+ irradiance += light.color * max(dot(normal, normalize(offset)), 0.0) * visibility / max(getPointLightAttenuation(light, length(offset)), 0.0001);
21632
+ }
21633
+ for (var index = 0; index < lighting.spotLightCount; index++) {
21634
+ let light = lighting_getSpotLight(index);
21635
+ irradiance += light.color * max(dot(normal, normalize(light.position - position)), 0.0) * select(1.0, spotVisibility[min(index, 3)], index < 4) / max(getSpotLightAttenuation(light, position), 0.0001);
21636
+ }
21637
+ return irradiance;
21638
+ }
21639
+ `,
21640
+ vs: `layout(std140) uniform lightmapBakeUniforms {
21641
+ int uvSet;
21642
+ mat3 uvTransform;
21643
+ float chartId;
21644
+ } lightmapBake;`,
21645
+ fs: `layout(std140) uniform lightmapBakeUniforms {
21646
+ int uvSet;
21647
+ mat3 uvTransform;
21648
+ float chartId;
21649
+ } lightmapBake;
21650
+
21651
+ vec3 lightmap_getIrradiance(vec3 position, vec3 normal, float directionalVisibility, vec4 pointVisibility, vec4 spotVisibility) {
21652
+ if (lighting.enabled == 0) {return vec3(0.0);}
21653
+ vec3 irradiance = lighting.ambientColor;
21654
+ for (int index = 0; index < lighting.directionalLightCount; index++) {
21655
+ DirectionalLight light = lighting_getDirectionalLight(index);
21656
+ irradiance += light.color * max(dot(normal, -normalize(light.direction)), 0.0) * (index == 0 ? directionalVisibility : 1.0);
21657
+ }
21658
+ for (int index = 0; index < lighting.pointLightCount; index++) {
21659
+ PointLight light = lighting_getPointLight(index);
21660
+ vec3 offset = light.position - position;
21661
+ irradiance += light.color * max(dot(normal, normalize(offset)), 0.0) * (index < 4 ? pointVisibility[index] : 1.0) / max(getPointLightAttenuation(light, length(offset)), 0.0001);
21662
+ }
21663
+ for (int index = 0; index < lighting.spotLightCount; index++) {
21664
+ SpotLight light = lighting_getSpotLight(index);
21665
+ irradiance += light.color * max(dot(normal, normalize(light.position - position)), 0.0) * (index < 4 ? spotVisibility[index] : 1.0) / max(getSpotLightAttenuation(light, position), 0.0001);
21666
+ }
21667
+ return irradiance;
21668
+ }
21669
+ `
21670
+ };
21671
+
21672
+ // src/modules/lighting/lightmap/lightmap-filter.ts
21673
+ var lightmapFilter = {
21674
+ name: "lightmapFilter",
21675
+ bindingLayout: [
21676
+ { name: "lightmapFilter", group: 0 },
21677
+ { name: "currentTexture", group: 0 },
21678
+ { name: "previousTexture", group: 0 },
21679
+ { name: "chartTexture", group: 0 }
21680
+ ],
21681
+ uniformTypes: { weight: "f32", mode: "i32" },
21682
+ defaultUniforms: { weight: 1, mode: 0 },
21683
+ getUniforms: (props) => props,
21684
+ source: `
21685
+ struct LightmapFilterUniforms {weight: f32, mode: i32};
21686
+ @group(0) @binding(auto) var<uniform> lightmapFilter: LightmapFilterUniforms;
21687
+ @group(0) @binding(auto) var currentTexture: texture_2d<f32>;
21688
+ @group(0) @binding(auto) var previousTexture: texture_2d<f32>;
21689
+ @group(0) @binding(auto) var chartTexture: texture_2d<f32>;
21690
+ fn lightmapFilter_getColor(coordinates: vec2i) -> vec4f {
21691
+ let current = textureLoad(currentTexture, coordinates, 0);
21692
+ if (lightmapFilter.mode == 0) {
21693
+ let previous = textureLoad(previousTexture, coordinates, 0);
21694
+ return vec4f(mix(previous.rgb, current.rgb, lightmapFilter.weight), current.a);
21695
+ }
21696
+ if (lightmapFilter.mode == 2 || current.a > 0.0) {return current;}
21697
+ let chart = textureLoad(chartTexture, coordinates, 0).r;
21698
+ if (chart <= 0.0) {return current;}
21699
+ let size = vec2i(textureDimensions(currentTexture));
21700
+ for (var y = -1; y <= 1; y++) {
21701
+ for (var x = -1; x <= 1; x++) {
21702
+ let neighbor = coordinates + vec2i(x, y);
21703
+ if (all(neighbor >= vec2i(0)) && all(neighbor < size)) {
21704
+ let candidate = textureLoad(currentTexture, neighbor, 0);
21705
+ if (candidate.a == chart) {return candidate;}
21706
+ }
21707
+ }
21708
+ }
21709
+ return current;
21710
+ }`,
21711
+ fs: `
21712
+ layout(std140) uniform lightmapFilterUniforms {
21713
+ float weight;
21714
+ int mode;
21715
+ } lightmapFilter;
21716
+ uniform sampler2D currentTexture;
21717
+ uniform sampler2D previousTexture;
21718
+ uniform sampler2D chartTexture;
21719
+ vec4 lightmapFilter_getColor(ivec2 coordinates) {
21720
+ vec4 current = texelFetch(currentTexture, coordinates, 0);
21721
+ if (lightmapFilter.mode == 0) {
21722
+ return vec4(mix(texelFetch(previousTexture, coordinates, 0).rgb, current.rgb, lightmapFilter.weight), current.a);
21723
+ }
21724
+ if (lightmapFilter.mode == 2 || current.a > 0.0) {return current;}
21725
+ float chart = texelFetch(chartTexture, coordinates, 0).r;
21726
+ ivec2 size = textureSize(currentTexture, 0);
21727
+ if (chart <= 0.0) return current;
21728
+ for (int y = -1; y <= 1; y++) {
21729
+ for (int x = -1; x <= 1; x++) {
21730
+ ivec2 neighbor = coordinates + ivec2(x,y);
21731
+ if (all(greaterThanEqual(neighbor, ivec2(0))) && all(lessThan(neighbor,size))) {
21732
+ vec4 candidate = texelFetch(currentTexture, neighbor, 0);
21733
+ if (candidate.a == chart) {return candidate;}
21734
+ }
21735
+ }
21736
+ }
21737
+ return current;
21738
+ }`
21739
+ };
19877
21740
  return __toCommonJS(bundle_exports);
19878
21741
  })();
19879
21742
  return __exports__;