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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/dist.dev.js +1776 -58
  2. package/dist/dist.min.js +1394 -147
  3. package/dist/index.cjs +35 -0
  4. package/dist/index.cjs.map +3 -3
  5. package/dist/index.d.ts +17 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +9 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.cjs +40 -0
  10. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.d.ts +6 -0
  11. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.d.ts.map +1 -0
  12. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.js +15 -0
  13. package/dist/modules/lighting/clustered-light-grid/clustered-light-grid.js.map +1 -0
  14. package/dist/modules/lighting/contact-shadow/contact-shadow.cjs +348 -0
  15. package/dist/modules/lighting/contact-shadow/contact-shadow.d.ts +188 -0
  16. package/dist/modules/lighting/contact-shadow/contact-shadow.d.ts.map +1 -0
  17. package/dist/modules/lighting/contact-shadow/contact-shadow.js +307 -0
  18. package/dist/modules/lighting/contact-shadow/contact-shadow.js.map +1 -0
  19. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.cjs +87 -0
  20. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.d.ts +21 -0
  21. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.d.ts.map +1 -0
  22. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.js +61 -0
  23. package/dist/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.js.map +1 -0
  24. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.cjs +87 -0
  25. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.d.ts +21 -0
  26. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.d.ts.map +1 -0
  27. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.js +61 -0
  28. package/dist/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.js.map +1 -0
  29. package/dist/modules/lighting/pbr-material/pbr-material-glsl.cjs +33 -11
  30. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts +2 -2
  31. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts.map +1 -1
  32. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js +33 -11
  33. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js.map +1 -1
  34. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.cjs +33 -11
  35. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts +1 -1
  36. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts.map +1 -1
  37. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js +33 -11
  38. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js.map +1 -1
  39. package/dist/modules/lighting/pbr-material/pbr-material.d.ts +3 -3
  40. package/dist/modules/lighting/pbr-shadow/pbr-shadow.cjs +97 -0
  41. package/dist/modules/lighting/pbr-shadow/pbr-shadow.d.ts +15 -0
  42. package/dist/modules/lighting/pbr-shadow/pbr-shadow.d.ts.map +1 -0
  43. package/dist/modules/lighting/pbr-shadow/pbr-shadow.js +69 -0
  44. package/dist/modules/lighting/pbr-shadow/pbr-shadow.js.map +1 -0
  45. package/dist/modules/lighting/pbr-surface/pbr-surface.cjs +114 -0
  46. package/dist/modules/lighting/pbr-surface/pbr-surface.d.ts +15 -0
  47. package/dist/modules/lighting/pbr-surface/pbr-surface.d.ts.map +1 -0
  48. package/dist/modules/lighting/pbr-surface/pbr-surface.js +88 -0
  49. package/dist/modules/lighting/pbr-surface/pbr-surface.js.map +1 -0
  50. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.cjs +379 -0
  51. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.d.ts +41 -0
  52. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.d.ts.map +1 -0
  53. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.js +345 -0
  54. package/dist/modules/lighting/prefiltered-environment/prefiltered-environment.js.map +1 -0
  55. package/dist/modules/lighting/shadow/shadow-glsl.cjs +228 -0
  56. package/dist/modules/lighting/shadow/shadow-glsl.d.ts +3 -0
  57. package/dist/modules/lighting/shadow/shadow-glsl.d.ts.map +1 -0
  58. package/dist/modules/lighting/shadow/shadow-glsl.js +204 -0
  59. package/dist/modules/lighting/shadow/shadow-glsl.js.map +1 -0
  60. package/dist/modules/lighting/shadow/shadow-types.cjs +15 -0
  61. package/dist/modules/lighting/shadow/shadow-types.d.ts +63 -0
  62. package/dist/modules/lighting/shadow/shadow-types.d.ts.map +1 -0
  63. package/dist/modules/lighting/shadow/shadow-types.js +5 -0
  64. package/dist/modules/lighting/shadow/shadow-types.js.map +1 -0
  65. package/dist/modules/lighting/shadow/shadow.cjs +458 -0
  66. package/dist/modules/lighting/shadow/shadow.d.ts +117 -0
  67. package/dist/modules/lighting/shadow/shadow.d.ts.map +1 -0
  68. package/dist/modules/lighting/shadow/shadow.js +450 -0
  69. package/dist/modules/lighting/shadow/shadow.js.map +1 -0
  70. package/dist/modules/lighting/unlit-material/unlit-material.cjs +60 -0
  71. package/dist/modules/lighting/unlit-material/unlit-material.d.ts +9 -0
  72. package/dist/modules/lighting/unlit-material/unlit-material.d.ts.map +1 -0
  73. package/dist/modules/lighting/unlit-material/unlit-material.js +40 -0
  74. package/dist/modules/lighting/unlit-material/unlit-material.js.map +1 -0
  75. package/package.json +3 -3
  76. package/src/index.ts +48 -0
  77. package/src/modules/lighting/clustered-light-grid/clustered-light-grid.ts +17 -0
  78. package/src/modules/lighting/contact-shadow/contact-shadow.ts +365 -0
  79. package/src/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.ts +78 -0
  80. package/src/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.ts +78 -0
  81. package/src/modules/lighting/pbr-material/pbr-material-glsl.ts +33 -11
  82. package/src/modules/lighting/pbr-material/pbr-material-wgsl.ts +33 -11
  83. package/src/modules/lighting/pbr-shadow/pbr-shadow.ts +85 -0
  84. package/src/modules/lighting/pbr-surface/pbr-surface.ts +103 -0
  85. package/src/modules/lighting/prefiltered-environment/prefiltered-environment.ts +370 -0
  86. package/src/modules/lighting/shadow/shadow-glsl.ts +204 -0
  87. package/src/modules/lighting/shadow/shadow-types.ts +68 -0
  88. package/src/modules/lighting/shadow/shadow.ts +536 -0
  89. package/src/modules/lighting/unlit-material/unlit-material.ts +44 -0
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,
@@ -85,8 +91,13 @@ var __exports__ = (() => {
85
91
  normalizeByteColor4: () => normalizeByteColor4,
86
92
  pathDash: () => pathDash,
87
93
  patternFill: () => patternFill,
94
+ pbrAmbientOcclusion: () => pbrAmbientOcclusion,
95
+ pbrContactShadow: () => pbrContactShadow,
96
+ pbrEnvironmentFilter: () => pbrEnvironmentFilter,
88
97
  pbrMaterial: () => pbrMaterial,
89
98
  pbrScene: () => pbrScene,
99
+ pbrShadow: () => pbrShadow,
100
+ pbrSurface: () => pbrSurface,
90
101
  phongMaterial: () => phongMaterial,
91
102
  picking: () => picking,
92
103
  pointGlow: () => pointGlow,
@@ -97,6 +108,7 @@ var __exports__ = (() => {
97
108
  resolveUseByteColors: () => resolveUseByteColors,
98
109
  riverWaterMaterial: () => riverWaterMaterial,
99
110
  scanWGSLInterface: () => scanWGSLInterface,
111
+ shadow: () => shadow,
100
112
  sketchStroke: () => sketchStroke,
101
113
  skin: () => skin,
102
114
  storageColors: () => storageColors,
@@ -104,6 +116,7 @@ var __exports__ = (() => {
104
116
  toHalfFloat: () => toHalfFloat,
105
117
  typeToChannelCount: () => typeToChannelCount,
106
118
  typeToChannelSuffix: () => typeToChannelSuffix,
119
+ unlitMaterial: () => unlitMaterial,
107
120
  validateShaderModuleUniformLayout: () => validateShaderModuleUniformLayout,
108
121
  valueNoise: () => valueNoise,
109
122
  volumeRaymarch: () => volumeRaymarch,
@@ -5137,6 +5150,35 @@ void main() {
5137
5150
  out[15] = 1;
5138
5151
  return out;
5139
5152
  }
5153
+ function affineComponents(matrix) {
5154
+ if (matrix.length !== 16 || Array.from(matrix).some((x) => !Number.isFinite(x)) || matrix[3] !== 0 || matrix[7] !== 0 || matrix[11] !== 0 || matrix[15] !== 1) {
5155
+ throw new RangeError("Expected a finite affine matrix");
5156
+ }
5157
+ const scale2 = [
5158
+ Math.hypot(matrix[0], matrix[1], matrix[2]),
5159
+ Math.hypot(matrix[4], matrix[5], matrix[6]),
5160
+ Math.hypot(matrix[8], matrix[9], matrix[10])
5161
+ ];
5162
+ if (scale2.some((x) => x === 0 || !Number.isFinite(x)))
5163
+ throw new RangeError("Singular transform");
5164
+ const rotation = Array.from({ length: 9 }, (_, i) => matrix[Math.floor(i / 3) * 4 + i % 3] / scale2[Math.floor(i / 3)]);
5165
+ for (const [a, b] of [
5166
+ [0, 3],
5167
+ [0, 6],
5168
+ [3, 6]
5169
+ ]) {
5170
+ if (Math.abs(rotation[a] * rotation[b] + rotation[a + 1] * rotation[b + 1] + rotation[a + 2] * rotation[b + 2]) > 1e-7) {
5171
+ throw new RangeError("Sheared transforms do not have a TRS decomposition");
5172
+ }
5173
+ }
5174
+ 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]);
5175
+ if (determinant2 < 0) {
5176
+ scale2[0] = -scale2[0];
5177
+ for (let i = 0; i < 3; i++)
5178
+ rotation[i] = -rotation[i];
5179
+ }
5180
+ return { rotation, scale: scale2, translation: [matrix[12], matrix[13], matrix[14]] };
5181
+ }
5140
5182
 
5141
5183
  // ../../node_modules/@math.gl/core/dist/gl-matrix/vec4.js
5142
5184
  function transformMat43(out, a, m) {
@@ -5431,27 +5473,15 @@ void main() {
5431
5473
  * @returns self
5432
5474
  */
5433
5475
  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;
5476
+ const { rotation, scale: scale2 } = affineComponents(this);
5477
+ result = result || new Array(16);
5478
+ if (scaleResult)
5479
+ for (let i = 0; i < 3; i++)
5480
+ scaleResult[i] = scale2[i];
5481
+ for (let i = 0; i < 16; i++)
5482
+ result[i] = 0;
5483
+ for (let i = 0; i < 9; i++)
5484
+ result[Math.floor(i / 3) * 4 + i % 3] = rotation[i];
5455
5485
  result[15] = 1;
5456
5486
  return result;
5457
5487
  }
@@ -5462,21 +5492,13 @@ void main() {
5462
5492
  * @returns self
5463
5493
  */
5464
5494
  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;
5495
+ const { rotation, scale: scale2 } = affineComponents(this);
5496
+ result = result || new Array(9);
5497
+ if (scaleResult)
5498
+ for (let i = 0; i < 3; i++)
5499
+ scaleResult[i] = scale2[i];
5500
+ for (let i = 0; i < 9; i++)
5501
+ result[i] = rotation[i];
5480
5502
  return result;
5481
5503
  }
5482
5504
  // Modifiers
@@ -12913,6 +12935,42 @@ vec4 dirlight_filterColor(vec4 color) {
12913
12935
  return uniforms2;
12914
12936
  }
12915
12937
 
12938
+ // src/modules/lighting/unlit-material/unlit-material.ts
12939
+ var SOURCE_WGSL2 = (
12940
+ /* wgsl */
12941
+ `fn unlitMaterial_getColor(surfaceColor: vec4<f32>) -> vec4<f32> {
12942
+ return surfaceColor;
12943
+ }
12944
+
12945
+ // Compatibility with the aggregate Phong/Gouraud material evaluation function.
12946
+ fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, positionWorldspace: vec3<f32>, normalWorldspace: vec3<f32>) -> vec3<f32> {
12947
+ return surfaceColor;
12948
+ }
12949
+ `
12950
+ );
12951
+ var SOURCE_GLSL = (
12952
+ /* glsl */
12953
+ `vec4 unlitMaterial_getColor(vec4 surfaceColor) {
12954
+ return surfaceColor;
12955
+ }
12956
+
12957
+ // Compatibility with the aggregate Phong/Gouraud material evaluation function.
12958
+ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 positionWorldspace, vec3 normalWorldspace) {
12959
+ return surfaceColor;
12960
+ }
12961
+ `
12962
+ );
12963
+ var unlitMaterial = {
12964
+ name: "unlitMaterial",
12965
+ source: SOURCE_WGSL2,
12966
+ vs: SOURCE_GLSL,
12967
+ fs: SOURCE_GLSL,
12968
+ dependencies: [],
12969
+ bindingLayout: [],
12970
+ uniformTypes: {},
12971
+ defaultUniforms: {}
12972
+ };
12973
+
12916
12974
  // src/modules/lighting/lambert-material/lambert-shaders-wgsl.ts
12917
12975
  var LAMBERT_WGSL = (
12918
12976
  /* wgsl */
@@ -14542,7 +14600,7 @@ void pbr_setPositionNormalTangentUV(
14542
14600
  vec3 bitangentW = cross(normalW, tangentW) * tangent.w;
14543
14601
  pbr_vTBN = mat3(tangentW, bitangentW, normalW);
14544
14602
  #else // HAS_TANGENTS != 1
14545
- pbr_vNormal = normalize(vec3(pbrProjection.modelMatrix * vec4(normal.xyz, 0.0)));
14603
+ pbr_vNormal = normalize(vec3(pbrProjection.normalMatrix * vec4(normal.xyz, 0.0)));
14546
14604
  #endif
14547
14605
  #endif
14548
14606
 
@@ -15549,7 +15607,9 @@ vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor) {
15549
15607
  }
15550
15608
 
15551
15609
  // Visibility for the first four spotlights; environment and other lights are unaffected.
15552
- vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15610
+ float pbr_ambientVisibility = 1.0;
15611
+
15612
+ vec4 pbr_filterColorWithShadows(vec4 vertexColor, float directionalVisibility, vec4 pointVisibility, vec4 spotVisibility)
15553
15613
  {
15554
15614
  vec2 baseColorUV = getMaterialUV(pbrMaterial.baseColorUVSet, pbrMaterial.baseColorUVTransform);
15555
15615
  vec2 metallicRoughnessUV = getMaterialUV(
@@ -15645,7 +15705,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15645
15705
  color.rgb = baseColor.rgb;
15646
15706
  #ifdef HAS_LIGHTMAP
15647
15707
  if (pbrMaterial.lightmapMapEnabled) {
15648
- color *= lightmapIrradiance / M_PI;
15708
+ color *= lightmapIrradiance / M_PI * pbr_ambientVisibility;
15649
15709
  #ifdef HAS_OCCLUSIONMAP
15650
15710
  if (pbrMaterial.occlusionMapEnabled) {
15651
15711
  float ao = texture(pbr_occlusionSampler, occlusionUV).r;
@@ -15747,12 +15807,12 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15747
15807
 
15748
15808
  #ifdef USE_LIGHTS
15749
15809
  PBRInfo_setAmbientLight(pbrInfo);
15750
- color += calculateFinalColor(pbrInfo, lighting.ambientColor);
15810
+ color += calculateFinalColor(pbrInfo, lighting.ambientColor * pbr_ambientVisibility);
15751
15811
 
15752
15812
  for(int i = 0; i < lighting.directionalLightCount; i++) {
15753
15813
  if (i < lighting.directionalLightCount) {
15754
15814
  PBRInfo_setDirectionalLight(pbrInfo, -lighting_getDirectionalLight(i).direction);
15755
- color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
15815
+ color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color * (i == 0 ? clamp(directionalVisibility, 0.0, 1.0) : 1.0));
15756
15816
  }
15757
15817
  }
15758
15818
 
@@ -15760,7 +15820,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15760
15820
  if (i < lighting.pointLightCount) {
15761
15821
  PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
15762
15822
  float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
15763
- color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
15823
+ color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color * (i < 4 ? clamp(pointVisibility[min(i, 3)], 0.0, 1.0) : 1.0) / attenuation);
15764
15824
  }
15765
15825
  }
15766
15826
 
@@ -15786,6 +15846,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15786
15846
  }
15787
15847
  #endif
15788
15848
 
15849
+ vec3 indirectBaseColor = color;
15789
15850
  #ifdef USE_IBL
15790
15851
  if (pbrMaterial.IBLenabled) {
15791
15852
  color += getIBLContribution(pbrInfo, n, reflection);
@@ -15794,6 +15855,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
15794
15855
 
15795
15856
  // The pinned KHR_lightmap draft explicitly defines c_diff = (1 - metallic) * baseColor.
15796
15857
  color += baseColor.rgb * (1.0 - metallic) * lightmapIrradiance / M_PI;
15858
+ color = indirectBaseColor + (color - indirectBaseColor) * pbr_ambientVisibility;
15797
15859
 
15798
15860
  #ifdef HAS_OCCLUSIONMAP
15799
15861
  if (pbrMaterial.occlusionMapEnabled) {
@@ -16026,7 +16088,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16026
16088
  PBRInfo_setAmbientLight(pbrInfo);
16027
16089
  color += calculateMaterialLightColor(
16028
16090
  pbrInfo,
16029
- lighting.ambientColor,
16091
+ lighting.ambientColor * pbr_ambientVisibility,
16030
16092
  clearcoatNormal,
16031
16093
  clearcoatFactor,
16032
16094
  clearcoatRoughness,
@@ -16042,7 +16104,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16042
16104
  PBRInfo_setDirectionalLight(pbrInfo, -lighting_getDirectionalLight(i).direction);
16043
16105
  color += calculateMaterialLightColor(
16044
16106
  pbrInfo,
16045
- lighting_getDirectionalLight(i).color,
16107
+ lighting_getDirectionalLight(i).color * (i == 0 ? clamp(directionalVisibility, 0.0, 1.0) : 1.0),
16046
16108
  clearcoatNormal,
16047
16109
  clearcoatFactor,
16048
16110
  clearcoatRoughness,
@@ -16053,7 +16115,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16053
16115
  );
16054
16116
  color += calculateDiffuseTransmissionLight(
16055
16117
  pbrInfo,
16056
- lighting_getDirectionalLight(i).color,
16118
+ lighting_getDirectionalLight(i).color * (i == 0 ? clamp(directionalVisibility, 0.0, 1.0) : 1.0),
16057
16119
  diffuseTransmissionColor,
16058
16120
  diffuseTransmission,
16059
16121
  multiscatterColor,
@@ -16069,7 +16131,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16069
16131
  float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
16070
16132
  color += calculateMaterialLightColor(
16071
16133
  pbrInfo,
16072
- lighting_getPointLight(i).color / attenuation,
16134
+ lighting_getPointLight(i).color * (i < 4 ? clamp(pointVisibility[min(i, 3)], 0.0, 1.0) : 1.0) / attenuation,
16073
16135
  clearcoatNormal,
16074
16136
  clearcoatFactor,
16075
16137
  clearcoatRoughness,
@@ -16080,7 +16142,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16080
16142
  );
16081
16143
  color += calculateDiffuseTransmissionLight(
16082
16144
  pbrInfo,
16083
- lighting_getPointLight(i).color / attenuation,
16145
+ lighting_getPointLight(i).color * (i < 4 ? clamp(pointVisibility[min(i, 3)], 0.0, 1.0) : 1.0) / attenuation,
16084
16146
  diffuseTransmissionColor,
16085
16147
  diffuseTransmission,
16086
16148
  multiscatterColor,
@@ -16149,6 +16211,7 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16149
16211
  }
16150
16212
  #endif
16151
16213
 
16214
+ vec3 indirectBaseColor = color;
16152
16215
  // Calculate lighting contribution from image based lighting source (IBL)
16153
16216
  #ifdef USE_IBL
16154
16217
  if (pbrMaterial.IBLenabled) {
@@ -16183,6 +16246,8 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16183
16246
  (1.0 - diffuseTransmission) * baseLayerEnergy * lightmapIrradiance / M_PI;
16184
16247
  #endif
16185
16248
 
16249
+ color = indirectBaseColor + (color - indirectBaseColor) * pbr_ambientVisibility;
16250
+
16186
16251
  // Apply optional PBR terms for additional (optional) shading
16187
16252
  #ifdef HAS_OCCLUSIONMAP
16188
16253
  if (pbrMaterial.occlusionMapEnabled) {
@@ -16244,6 +16309,21 @@ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility)
16244
16309
  return vec4(applySceneColorManagement(color), alpha);
16245
16310
  }
16246
16311
 
16312
+ // Depth-only passes use the same base-color alpha, vertex color and UV transform.
16313
+ void pbr_discardMaskedFragment(vec4 vertexColor) {
16314
+ #ifdef ALPHA_CUTOFF
16315
+ float alpha = pbrMaterial.baseColorFactor.a * vertexColor.a;
16316
+ #ifdef HAS_BASECOLORMAP
16317
+ alpha *= texture(pbr_baseColorSampler, getMaterialUV(pbrMaterial.baseColorUVSet, pbrMaterial.baseColorUVTransform)).a;
16318
+ #endif
16319
+ if (alpha < pbrMaterial.alphaCutoff) {discard;}
16320
+ #endif
16321
+ }
16322
+
16323
+ vec4 pbr_filterColorWithSpotShadows(vec4 vertexColor, vec4 spotVisibility) {
16324
+ return pbr_filterColorWithShadows(vertexColor, 1.0, vec4(1.0), spotVisibility);
16325
+ }
16326
+
16247
16327
  vec4 pbr_filterColor(vec4 vertexColor) {
16248
16328
  return pbr_filterColorWithSpotShadows(vertexColor, vec4(1.0));
16249
16329
  }
@@ -17368,7 +17448,9 @@ fn calculateFinalColor(pbrInfo: PBRInfo, lightColor: vec3<f32>) -> vec3<f32> {
17368
17448
  }
17369
17449
 
17370
17450
  // Visibility for the first four spotlights; environment and other lights are unaffected.
17371
- fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f) -> vec4<f32> {
17451
+ var<private> pbr_ambientVisibility: f32 = 1.0;
17452
+
17453
+ fn pbr_filterColorWithShadows(vertexColor: vec4<f32>, directionalVisibility: f32, pointVisibility: vec4f, spotVisibility: vec4f) -> vec4<f32> {
17372
17454
  let baseColorUV = getMaterialUV(pbrMaterial.baseColorUVSet, pbrMaterial.baseColorUVTransform);
17373
17455
  let metallicRoughnessUV = getMaterialUV(
17374
17456
  pbrMaterial.metallicRoughnessUVSet,
@@ -17461,7 +17543,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17461
17543
  color = baseColor.rgb;
17462
17544
  #ifdef HAS_LIGHTMAP
17463
17545
  if (pbrMaterial.lightmapMapEnabled != 0u) {
17464
- color *= lightmapIrradiance / M_PI;
17546
+ color *= lightmapIrradiance / M_PI * pbr_ambientVisibility;
17465
17547
  #ifdef HAS_OCCLUSIONMAP
17466
17548
  if (pbrMaterial.occlusionMapEnabled != 0) {
17467
17549
  let ao = textureSample(pbr_occlusionSampler, pbr_occlusionSamplerSampler, occlusionUV).r;
@@ -17564,12 +17646,12 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17564
17646
 
17565
17647
  #ifdef USE_LIGHTS
17566
17648
  PBRInfo_setAmbientLight(&pbrInfo);
17567
- color += calculateFinalColor(pbrInfo, lighting.ambientColor);
17649
+ color += calculateFinalColor(pbrInfo, lighting.ambientColor * pbr_ambientVisibility);
17568
17650
 
17569
17651
  for (var i = 0; i < lighting.directionalLightCount; i++) {
17570
17652
  if (i < lighting.directionalLightCount) {
17571
17653
  PBRInfo_setDirectionalLight(&pbrInfo, -lighting_getDirectionalLight(i).direction);
17572
- color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
17654
+ color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color * select(1.0, clamp(directionalVisibility, 0.0, 1.0), i == 0));
17573
17655
  }
17574
17656
  }
17575
17657
 
@@ -17580,7 +17662,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17580
17662
  lighting_getPointLight(i),
17581
17663
  distance(lighting_getPointLight(i).position, fragmentInputs.pbr_vPosition)
17582
17664
  );
17583
- color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
17665
+ color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color * select(1.0, clamp(pointVisibility[min(i, 3)], 0.0, 1.0), i < 4) / attenuation);
17584
17666
  }
17585
17667
  }
17586
17668
 
@@ -17609,7 +17691,8 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17609
17691
  }
17610
17692
  #endif
17611
17693
 
17612
- #ifdef USE_IBL
17694
+ let indirectBaseColor = color;
17695
+ #ifdef USE_IBL
17613
17696
  if (pbrMaterial.IBLenabled != 0) {
17614
17697
  color += getIBLContribution(pbrInfo, n, reflection);
17615
17698
  }
@@ -17617,6 +17700,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17617
17700
 
17618
17701
  // The pinned KHR_lightmap draft explicitly defines c_diff = (1 - metallic) * baseColor.
17619
17702
  color += baseColor.rgb * (1.0 - metallic) * lightmapIrradiance / M_PI;
17703
+ color = indirectBaseColor + (color - indirectBaseColor) * pbr_ambientVisibility;
17620
17704
 
17621
17705
  #ifdef HAS_OCCLUSIONMAP
17622
17706
  if (pbrMaterial.occlusionMapEnabled != 0) {
@@ -17915,7 +17999,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17915
17999
  PBRInfo_setAmbientLight(&pbrInfo);
17916
18000
  color += calculateMaterialLightColor(
17917
18001
  pbrInfo,
17918
- lighting.ambientColor,
18002
+ lighting.ambientColor * pbr_ambientVisibility,
17919
18003
  clearcoatNormal,
17920
18004
  clearcoatFactor,
17921
18005
  clearcoatRoughness,
@@ -17931,7 +18015,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17931
18015
  PBRInfo_setDirectionalLight(&pbrInfo, -lighting_getDirectionalLight(i).direction);
17932
18016
  color += calculateMaterialLightColor(
17933
18017
  pbrInfo,
17934
- lighting_getDirectionalLight(i).color,
18018
+ lighting_getDirectionalLight(i).color * select(1.0, clamp(directionalVisibility, 0.0, 1.0), i == 0),
17935
18019
  clearcoatNormal,
17936
18020
  clearcoatFactor,
17937
18021
  clearcoatRoughness,
@@ -17942,7 +18026,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17942
18026
  );
17943
18027
  color += calculateDiffuseTransmissionLight(
17944
18028
  pbrInfo,
17945
- lighting_getDirectionalLight(i).color,
18029
+ lighting_getDirectionalLight(i).color * select(1.0, clamp(directionalVisibility, 0.0, 1.0), i == 0),
17946
18030
  diffuseTransmissionColor,
17947
18031
  diffuseTransmission,
17948
18032
  multiscatterColor,
@@ -17961,7 +18045,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17961
18045
  );
17962
18046
  color += calculateMaterialLightColor(
17963
18047
  pbrInfo,
17964
- lighting_getPointLight(i).color / attenuation,
18048
+ lighting_getPointLight(i).color * select(1.0, clamp(pointVisibility[min(i, 3)], 0.0, 1.0), i < 4) / attenuation,
17965
18049
  clearcoatNormal,
17966
18050
  clearcoatFactor,
17967
18051
  clearcoatRoughness,
@@ -17972,7 +18056,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
17972
18056
  );
17973
18057
  color += calculateDiffuseTransmissionLight(
17974
18058
  pbrInfo,
17975
- lighting_getPointLight(i).color / attenuation,
18059
+ lighting_getPointLight(i).color * select(1.0, clamp(pointVisibility[min(i, 3)], 0.0, 1.0), i < 4) / attenuation,
17976
18060
  diffuseTransmissionColor,
17977
18061
  diffuseTransmission,
17978
18062
  multiscatterColor,
@@ -18041,6 +18125,7 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
18041
18125
  }
18042
18126
  #endif
18043
18127
 
18128
+ let indirectBaseColor = color;
18044
18129
  // Calculate lighting contribution from image based lighting source (IBL)
18045
18130
  #ifdef USE_IBL
18046
18131
  if (pbrMaterial.IBLenabled != 0) {
@@ -18075,6 +18160,8 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
18075
18160
  (1.0 - diffuseTransmission) * baseLayerEnergy * lightmapIrradiance / M_PI;
18076
18161
  #endif
18077
18162
 
18163
+ color = indirectBaseColor + (color - indirectBaseColor) * pbr_ambientVisibility;
18164
+
18078
18165
  // Apply optional PBR terms for additional (optional) shading
18079
18166
  #ifdef HAS_OCCLUSIONMAP
18080
18167
  if (pbrMaterial.occlusionMapEnabled != 0) {
@@ -18137,6 +18224,21 @@ fn pbr_filterColorWithSpotShadows(vertexColor: vec4<f32>, spotVisibility: vec4f)
18137
18224
  return vec4<f32>(applySceneColorManagement(color), alpha);
18138
18225
  }
18139
18226
 
18227
+ // Depth-only passes use the same base-color alpha, vertex color and UV transform.
18228
+ fn pbr_discardMaskedFragment(vertexColor: vec4f) {
18229
+ #ifdef ALPHA_CUTOFF
18230
+ var alpha = pbrMaterial.baseColorFactor.a * vertexColor.a;
18231
+ #ifdef HAS_BASECOLORMAP
18232
+ alpha *= textureSample(pbr_baseColorSampler, pbr_baseColorSamplerSampler, getMaterialUV(pbrMaterial.baseColorUVSet, pbrMaterial.baseColorUVTransform)).a;
18233
+ #endif
18234
+ if (alpha < pbrMaterial.alphaCutoff) {discard;}
18235
+ #endif
18236
+ }
18237
+
18238
+ fn pbr_filterColorWithSpotShadows(vertexColor: vec4f, spotVisibility: vec4f) -> vec4f {
18239
+ return pbr_filterColorWithShadows(vertexColor, 1.0, vec4f(1.0), spotVisibility);
18240
+ }
18241
+
18140
18242
  fn pbr_filterColor(vertexColor: vec4<f32>) -> vec4<f32> {
18141
18243
  return pbr_filterColorWithSpotShadows(vertexColor, vec4f(1.0));
18142
18244
  }
@@ -19874,6 +19976,1622 @@ fn globeClouds_getColor(camera: vec3f, direction: vec3f) -> vec4f {
19874
19976
  `
19875
19977
  )
19876
19978
  };
19979
+
19980
+ // src/modules/lighting/prefiltered-environment/prefiltered-environment.ts
19981
+ var GLSL_UNIFORMS = (
19982
+ /* glsl */
19983
+ `layout(std140) uniform pbrEnvironmentFilterUniforms {
19984
+ int face;
19985
+ float roughness;
19986
+ int mode;
19987
+ int sampleCount;
19988
+ int sourceEncoding;
19989
+ } pbrEnvironmentFilter;
19990
+
19991
+ uniform sampler2D pbrEnvironmentSource;
19992
+ `
19993
+ );
19994
+ var WGSL_UNIFORMS = (
19995
+ /* wgsl */
19996
+ `struct PBREnvironmentFilterUniforms {
19997
+ face: i32,
19998
+ roughness: f32,
19999
+ mode: i32,
20000
+ sampleCount: i32,
20001
+ sourceEncoding: i32,
20002
+ };
20003
+
20004
+ @group(0) @binding(auto) var<uniform> pbrEnvironmentFilter: PBREnvironmentFilterUniforms;
20005
+ @group(0) @binding(auto) var pbrEnvironmentSource: texture_2d<f32>;
20006
+ @group(0) @binding(auto) var pbrEnvironmentSourceSampler: sampler;
20007
+ `
20008
+ );
20009
+ var pbrEnvironmentFilter = {
20010
+ name: "pbrEnvironmentFilter",
20011
+ bindingLayout: [
20012
+ { name: "pbrEnvironmentFilter", group: 0 },
20013
+ { name: "pbrEnvironmentSource", group: 0 }
20014
+ ],
20015
+ fs: GLSL_UNIFORMS,
20016
+ source: WGSL_UNIFORMS,
20017
+ getUniforms: (properties) => properties,
20018
+ uniformTypes: {
20019
+ face: "i32",
20020
+ roughness: "f32",
20021
+ mode: "i32",
20022
+ sampleCount: "i32",
20023
+ sourceEncoding: "i32"
20024
+ },
20025
+ defaultUniforms: {
20026
+ face: 0,
20027
+ roughness: 0,
20028
+ mode: 0,
20029
+ sampleCount: 64,
20030
+ sourceEncoding: 0
20031
+ }
20032
+ };
20033
+ var PBR_ENVIRONMENT_FRAGMENT_GLSL = (
20034
+ /* glsl */
20035
+ `#version 300 es
20036
+ precision highp float;
20037
+ precision highp int;
20038
+
20039
+ in vec2 uv;
20040
+ out vec4 fragmentColor;
20041
+
20042
+ const float ENVIRONMENT_PI = 3.141592653589793;
20043
+
20044
+ vec2 getEnvironmentHammersley(uint index, uint sampleCount)
20045
+ {
20046
+ uint reversedBits = (index << 16u) | (index >> 16u);
20047
+ reversedBits = ((reversedBits & 0x55555555u) << 1u) |
20048
+ ((reversedBits & 0xAAAAAAAAu) >> 1u);
20049
+ reversedBits = ((reversedBits & 0x33333333u) << 2u) |
20050
+ ((reversedBits & 0xCCCCCCCCu) >> 2u);
20051
+ reversedBits = ((reversedBits & 0x0F0F0F0Fu) << 4u) |
20052
+ ((reversedBits & 0xF0F0F0F0u) >> 4u);
20053
+ reversedBits = ((reversedBits & 0x00FF00FFu) << 8u) |
20054
+ ((reversedBits & 0xFF00FF00u) >> 8u);
20055
+ return vec2(
20056
+ float(index) / float(sampleCount),
20057
+ float(reversedBits) * 2.3283064365386963e-10
20058
+ );
20059
+ }
20060
+
20061
+ vec3 getEnvironmentFaceDirection(int face, vec2 textureCoordinate)
20062
+ {
20063
+ vec2 coordinate = textureCoordinate * 2.0 - 1.0;
20064
+ if (face == 0) return normalize(vec3(1.0, -coordinate.y, -coordinate.x));
20065
+ if (face == 1) return normalize(vec3(-1.0, -coordinate.y, coordinate.x));
20066
+ if (face == 2) return normalize(vec3(coordinate.x, 1.0, coordinate.y));
20067
+ if (face == 3) return normalize(vec3(coordinate.x, -1.0, -coordinate.y));
20068
+ if (face == 4) return normalize(vec3(coordinate.x, -coordinate.y, 1.0));
20069
+ return normalize(vec3(-coordinate.x, -coordinate.y, -1.0));
20070
+ }
20071
+
20072
+ mat3 getEnvironmentTangentBasis(vec3 normal)
20073
+ {
20074
+ vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
20075
+ vec3 tangent = normalize(cross(up, normal));
20076
+ return mat3(tangent, cross(normal, tangent), normal);
20077
+ }
20078
+
20079
+ vec3 sampleEnvironmentDirection(vec3 direction)
20080
+ {
20081
+ vec3 normalizedDirection = normalize(direction);
20082
+ vec2 textureCoordinate = vec2(
20083
+ atan(normalizedDirection.z, normalizedDirection.x) / (2.0 * ENVIRONMENT_PI) + 0.5,
20084
+ acos(clamp(normalizedDirection.y, -1.0, 1.0)) / ENVIRONMENT_PI
20085
+ );
20086
+ vec3 color = texture(pbrEnvironmentSource, textureCoordinate).rgb;
20087
+ if (pbrEnvironmentFilter.sourceEncoding == 1) {
20088
+ vec3 lower = color / 12.92;
20089
+ vec3 upper = pow((color + 0.055) / 1.055, vec3(2.4));
20090
+ color = mix(lower, upper, step(vec3(0.04045), color));
20091
+ }
20092
+ return color;
20093
+ }
20094
+
20095
+ vec3 importanceSampleEnvironment(vec2 sequence, vec3 normal, float roughness)
20096
+ {
20097
+ float alpha = max(roughness * roughness, 0.0001);
20098
+ float azimuth = 2.0 * ENVIRONMENT_PI * sequence.x;
20099
+ float cosine = sqrt(
20100
+ (1.0 - sequence.y) / max(1.0 + (alpha * alpha - 1.0) * sequence.y, 0.0001)
20101
+ );
20102
+ float sine = sqrt(max(1.0 - cosine * cosine, 0.0));
20103
+ vec3 halfVector = vec3(cos(azimuth) * sine, sin(azimuth) * sine, cosine);
20104
+ return normalize(getEnvironmentTangentBasis(normal) * halfVector);
20105
+ }
20106
+
20107
+ float getEnvironmentGeometry(float normalDotDirection, float roughness)
20108
+ {
20109
+ float alpha = roughness * roughness;
20110
+ float factor = alpha * 0.5;
20111
+ return normalDotDirection / max(normalDotDirection * (1.0 - factor) + factor, 0.0001);
20112
+ }
20113
+
20114
+ vec2 integrateEnvironmentBRDF(vec2 textureCoordinate, int sampleCount)
20115
+ {
20116
+ float normalDotView = max(textureCoordinate.x, 0.001);
20117
+ float roughness = 1.0 - textureCoordinate.y;
20118
+ vec3 viewDirection = vec3(sqrt(max(1.0 - normalDotView * normalDotView, 0.0)), 0.0, normalDotView);
20119
+ vec2 integration = vec2(0.0);
20120
+
20121
+ for (int sampleIndex = 0; sampleIndex < 1024; sampleIndex++) {
20122
+ if (sampleIndex >= sampleCount) break;
20123
+ vec3 halfVector = importanceSampleEnvironment(
20124
+ getEnvironmentHammersley(uint(sampleIndex), uint(sampleCount)),
20125
+ vec3(0.0, 0.0, 1.0),
20126
+ roughness
20127
+ );
20128
+ vec3 lightDirection = normalize(2.0 * dot(viewDirection, halfVector) * halfVector - viewDirection);
20129
+ float normalDotLight = max(lightDirection.z, 0.0);
20130
+ float normalDotHalf = max(halfVector.z, 0.0);
20131
+ float viewDotHalf = max(dot(viewDirection, halfVector), 0.0);
20132
+ if (normalDotLight > 0.0) {
20133
+ float visibility = getEnvironmentGeometry(normalDotView, roughness) *
20134
+ getEnvironmentGeometry(normalDotLight, roughness) * viewDotHalf /
20135
+ max(normalDotHalf * normalDotView, 0.0001);
20136
+ float fresnel = pow(1.0 - viewDotHalf, 5.0);
20137
+ integration += vec2(1.0 - fresnel, fresnel) * visibility;
20138
+ }
20139
+ }
20140
+ return integration / float(sampleCount);
20141
+ }
20142
+
20143
+ vec3 integrateEnvironmentDirection(vec3 normal, int sampleCount)
20144
+ {
20145
+ vec3 accumulatedColor = vec3(0.0);
20146
+ float accumulatedWeight = 0.0;
20147
+
20148
+ for (int sampleIndex = 0; sampleIndex < 1024; sampleIndex++) {
20149
+ if (sampleIndex >= sampleCount) break;
20150
+ vec2 sequence = getEnvironmentHammersley(uint(sampleIndex), uint(sampleCount));
20151
+ vec3 direction;
20152
+ float sampleWeight;
20153
+
20154
+ if (pbrEnvironmentFilter.mode == 1) {
20155
+ float azimuth = 2.0 * ENVIRONMENT_PI * sequence.x;
20156
+ float cosine = sqrt(1.0 - sequence.y);
20157
+ float sine = sqrt(sequence.y);
20158
+ direction = normalize(
20159
+ getEnvironmentTangentBasis(normal) *
20160
+ vec3(cos(azimuth) * sine, sin(azimuth) * sine, cosine)
20161
+ );
20162
+ sampleWeight = 1.0;
20163
+ } else {
20164
+ vec3 halfVector = importanceSampleEnvironment(sequence, normal, pbrEnvironmentFilter.roughness);
20165
+ direction = normalize(2.0 * dot(normal, halfVector) * halfVector - normal);
20166
+ sampleWeight = max(dot(normal, direction), 0.0);
20167
+ }
20168
+
20169
+ accumulatedColor += sampleEnvironmentDirection(direction) * sampleWeight;
20170
+ accumulatedWeight += sampleWeight;
20171
+ }
20172
+
20173
+ return accumulatedColor / max(accumulatedWeight, 0.0001);
20174
+ }
20175
+
20176
+ void main(void)
20177
+ {
20178
+ int sampleCount = max(pbrEnvironmentFilter.sampleCount, 1);
20179
+ if (pbrEnvironmentFilter.mode == 2) {
20180
+ fragmentColor = vec4(integrateEnvironmentBRDF(uv, sampleCount), 0.0, 1.0);
20181
+ return;
20182
+ }
20183
+
20184
+ vec3 direction = getEnvironmentFaceDirection(pbrEnvironmentFilter.face, uv);
20185
+ fragmentColor = vec4(integrateEnvironmentDirection(direction, sampleCount), 1.0);
20186
+ }
20187
+ `
20188
+ );
20189
+ var PBR_ENVIRONMENT_FRAGMENT_WGSL = (
20190
+ /* wgsl */
20191
+ `const ENVIRONMENT_PI: f32 = 3.141592653589793;
20192
+
20193
+ fn getEnvironmentHammersley(index: u32, sampleCount: u32) -> vec2f {
20194
+ return vec2f(f32(index) / f32(sampleCount), f32(reverseBits(index)) * 2.3283064365386963e-10);
20195
+ }
20196
+
20197
+ fn getEnvironmentFaceDirection(face: i32, textureCoordinate: vec2f) -> vec3f {
20198
+ let coordinate = textureCoordinate * 2.0 - 1.0;
20199
+ if (face == 0) { return normalize(vec3f(1.0, -coordinate.y, -coordinate.x)); }
20200
+ if (face == 1) { return normalize(vec3f(-1.0, -coordinate.y, coordinate.x)); }
20201
+ if (face == 2) { return normalize(vec3f(coordinate.x, 1.0, coordinate.y)); }
20202
+ if (face == 3) { return normalize(vec3f(coordinate.x, -1.0, -coordinate.y)); }
20203
+ if (face == 4) { return normalize(vec3f(coordinate.x, -coordinate.y, 1.0)); }
20204
+ return normalize(vec3f(-coordinate.x, -coordinate.y, -1.0));
20205
+ }
20206
+
20207
+ fn getEnvironmentTangentBasis(normal: vec3f) -> mat3x3f {
20208
+ var up = vec3f(1.0, 0.0, 0.0);
20209
+ if (abs(normal.z) < 0.999) {
20210
+ up = vec3f(0.0, 0.0, 1.0);
20211
+ }
20212
+ let tangent = normalize(cross(up, normal));
20213
+ return mat3x3f(tangent, cross(normal, tangent), normal);
20214
+ }
20215
+
20216
+ fn sampleEnvironmentDirection(direction: vec3f) -> vec3f {
20217
+ let normalizedDirection = normalize(direction);
20218
+ let textureCoordinate = vec2f(
20219
+ atan2(normalizedDirection.z, normalizedDirection.x) / (2.0 * ENVIRONMENT_PI) + 0.5,
20220
+ acos(clamp(normalizedDirection.y, -1.0, 1.0)) / ENVIRONMENT_PI
20221
+ );
20222
+ var color = textureSampleLevel(
20223
+ pbrEnvironmentSource,
20224
+ pbrEnvironmentSourceSampler,
20225
+ textureCoordinate,
20226
+ 0.0
20227
+ ).rgb;
20228
+ if (pbrEnvironmentFilter.sourceEncoding == 1) {
20229
+ let lower = color / 12.92;
20230
+ let upper = pow((color + 0.055) / 1.055, vec3f(2.4));
20231
+ color = mix(lower, upper, step(vec3f(0.04045), color));
20232
+ }
20233
+ return color;
20234
+ }
20235
+
20236
+ fn importanceSampleEnvironment(sequence: vec2f, normal: vec3f, roughness: f32) -> vec3f {
20237
+ let alpha = max(roughness * roughness, 0.0001);
20238
+ let azimuth = 2.0 * ENVIRONMENT_PI * sequence.x;
20239
+ let cosine = sqrt(
20240
+ (1.0 - sequence.y) / max(1.0 + (alpha * alpha - 1.0) * sequence.y, 0.0001)
20241
+ );
20242
+ let sine = sqrt(max(1.0 - cosine * cosine, 0.0));
20243
+ let halfVector = vec3f(cos(azimuth) * sine, sin(azimuth) * sine, cosine);
20244
+ return normalize(getEnvironmentTangentBasis(normal) * halfVector);
20245
+ }
20246
+
20247
+ fn getEnvironmentGeometry(normalDotDirection: f32, roughness: f32) -> f32 {
20248
+ let alpha = roughness * roughness;
20249
+ let factor = alpha * 0.5;
20250
+ return normalDotDirection / max(normalDotDirection * (1.0 - factor) + factor, 0.0001);
20251
+ }
20252
+
20253
+ fn integrateEnvironmentBRDF(textureCoordinate: vec2f, sampleCount: u32) -> vec2f {
20254
+ let normalDotView = max(textureCoordinate.x, 0.001);
20255
+ let roughness = 1.0 - textureCoordinate.y;
20256
+ let viewDirection = vec3f(
20257
+ sqrt(max(1.0 - normalDotView * normalDotView, 0.0)),
20258
+ 0.0,
20259
+ normalDotView
20260
+ );
20261
+ var integration = vec2f(0.0);
20262
+
20263
+ for (var sampleIndex = 0u; sampleIndex < sampleCount; sampleIndex++) {
20264
+ let halfVector = importanceSampleEnvironment(
20265
+ getEnvironmentHammersley(sampleIndex, sampleCount),
20266
+ vec3f(0.0, 0.0, 1.0),
20267
+ roughness
20268
+ );
20269
+ let lightDirection = normalize(
20270
+ 2.0 * dot(viewDirection, halfVector) * halfVector - viewDirection
20271
+ );
20272
+ let normalDotLight = max(lightDirection.z, 0.0);
20273
+ let normalDotHalf = max(halfVector.z, 0.0);
20274
+ let viewDotHalf = max(dot(viewDirection, halfVector), 0.0);
20275
+ if (normalDotLight > 0.0) {
20276
+ let visibility = getEnvironmentGeometry(normalDotView, roughness) *
20277
+ getEnvironmentGeometry(normalDotLight, roughness) * viewDotHalf /
20278
+ max(normalDotHalf * normalDotView, 0.0001);
20279
+ let fresnel = pow(1.0 - viewDotHalf, 5.0);
20280
+ integration += vec2f(1.0 - fresnel, fresnel) * visibility;
20281
+ }
20282
+ }
20283
+ return integration / f32(sampleCount);
20284
+ }
20285
+
20286
+ fn integrateEnvironmentDirection(normal: vec3f, sampleCount: u32) -> vec3f {
20287
+ var accumulatedColor = vec3f(0.0);
20288
+ var accumulatedWeight = 0.0;
20289
+
20290
+ for (var sampleIndex = 0u; sampleIndex < sampleCount; sampleIndex++) {
20291
+ let sequence = getEnvironmentHammersley(sampleIndex, sampleCount);
20292
+ var direction: vec3f;
20293
+ var sampleWeight: f32;
20294
+
20295
+ if (pbrEnvironmentFilter.mode == 1) {
20296
+ let azimuth = 2.0 * ENVIRONMENT_PI * sequence.x;
20297
+ let cosine = sqrt(1.0 - sequence.y);
20298
+ let sine = sqrt(sequence.y);
20299
+ direction = normalize(
20300
+ getEnvironmentTangentBasis(normal) *
20301
+ vec3f(cos(azimuth) * sine, sin(azimuth) * sine, cosine)
20302
+ );
20303
+ sampleWeight = 1.0;
20304
+ } else {
20305
+ let halfVector = importanceSampleEnvironment(sequence, normal, pbrEnvironmentFilter.roughness);
20306
+ direction = normalize(2.0 * dot(normal, halfVector) * halfVector - normal);
20307
+ sampleWeight = max(dot(normal, direction), 0.0);
20308
+ }
20309
+
20310
+ accumulatedColor += sampleEnvironmentDirection(direction) * sampleWeight;
20311
+ accumulatedWeight += sampleWeight;
20312
+ }
20313
+ return accumulatedColor / max(accumulatedWeight, 0.0001);
20314
+ }
20315
+
20316
+ @fragment
20317
+ fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4f {
20318
+ // ClipSpace UVs increase upward; WebGPU texture rows increase downward.
20319
+ let textureCoordinate = vec2f(inputs.uv.x, 1.0 - inputs.uv.y);
20320
+ let sampleCount = u32(max(pbrEnvironmentFilter.sampleCount, 1));
20321
+ if (pbrEnvironmentFilter.mode == 2) {
20322
+ return vec4f(integrateEnvironmentBRDF(textureCoordinate, sampleCount), 0.0, 1.0);
20323
+ }
20324
+
20325
+ let direction = getEnvironmentFaceDirection(pbrEnvironmentFilter.face, textureCoordinate);
20326
+ return vec4f(integrateEnvironmentDirection(direction, sampleCount), 1.0);
20327
+ }
20328
+ `
20329
+ );
20330
+
20331
+ // src/modules/lighting/shadow/shadow-glsl.ts
20332
+ var SHADOW_GLSL = (
20333
+ /* glsl */
20334
+ `
20335
+ precision highp sampler2DArray;
20336
+ struct DirectionalShadowLightUniform {
20337
+ vec3 direction;
20338
+ float strength;
20339
+ float normalBias;
20340
+ float sourceAngularRadius;
20341
+ float cascadeBlendFraction;
20342
+ float farFadeFraction;
20343
+ float shadowDistance;
20344
+ };
20345
+ struct SpotShadowLightUniform {
20346
+ vec3 position;
20347
+ float range;
20348
+ vec3 direction;
20349
+ float outerConeCos;
20350
+ float sourceRadius;
20351
+ float normalBias;
20352
+ float strength;
20353
+ float nearPlane;
20354
+ };
20355
+ struct PointShadowLightUniform {
20356
+ vec3 position;
20357
+ float range;
20358
+ float sourceRadius;
20359
+ float normalBias;
20360
+ float strength;
20361
+ float nearPlane;
20362
+ };
20363
+ layout(std140) uniform shadowUniforms {
20364
+ int directionalLightCount;
20365
+ int spotLightCount;
20366
+ int pointLightCount;
20367
+ int cascadeCount;
20368
+ int blockerSampleCount;
20369
+ int filterSampleCount;
20370
+ vec4 cascadeSplits;
20371
+ mat4 directionalViewProjectionMatrices[4];
20372
+ mat4 spotViewProjectionMatrices[4];
20373
+ mat4 pointViewProjectionMatrices[24];
20374
+ DirectionalShadowLightUniform directionalLights[1];
20375
+ SpotShadowLightUniform spotLights[4];
20376
+ PointShadowLightUniform pointLights[4];
20377
+ } shadow;
20378
+ uniform sampler2DArray directionalShadowTexture;
20379
+ uniform sampler2DArray spotShadowTexture;
20380
+ uniform sampler2DArray pointShadowTexture;
20381
+ const float SHADOW_PI = 3.141592653589793;
20382
+ float shadow_hash(vec3 position) {
20383
+ return fract(sin(dot(position, vec3(12.9898, 78.233, 37.719))) * 43758.5453);
20384
+ }
20385
+ vec2 shadow_diskSample(int index, int count, float rotation) {
20386
+ float fraction = (float(index) + 0.5) / max(float(count), 1.0);
20387
+ float angle = float(index) * 2.39996323 + rotation;
20388
+ return sqrt(fraction) * vec2(cos(angle), sin(angle));
20389
+ }
20390
+ vec3 shadow_project(mat4 matrix, vec3 worldPosition) {
20391
+ vec4 clip = matrix * vec4(worldPosition, 1.0);
20392
+ vec3 projected = clip.xyz / max(abs(clip.w), 0.00001);
20393
+ // Shared matrices retain [0,1] depth; WebGL texture rows start at the bottom.
20394
+ return vec3(projected.xy * 0.5 + 0.5, projected.z);
20395
+ }
20396
+ bool shadow_validProjection(vec3 projected) {
20397
+ return all(greaterThanEqual(projected, vec3(0.0))) && all(lessThanEqual(projected, vec3(1.0)));
20398
+ }
20399
+ // Bilinear interpolation of four depth comparisons matches a hardware 2x2 PCF lookup.
20400
+ float shadow_compare(sampler2DArray shadowMap, vec2 coordinate, int layer, float reference) {
20401
+ ivec2 dimensions = textureSize(shadowMap, 0).xy;
20402
+ vec2 position = coordinate * vec2(dimensions) - 0.5;
20403
+ ivec2 base = ivec2(floor(position));
20404
+ vec2 fraction = fract(position);
20405
+ ivec2 maximum = dimensions - 1;
20406
+ float lowerLeft = step(reference, texelFetch(shadowMap, ivec3(clamp(base, ivec2(0), maximum), layer), 0).r);
20407
+ float lowerRight = step(reference, texelFetch(shadowMap, ivec3(clamp(base + ivec2(1,0), ivec2(0), maximum), layer), 0).r);
20408
+ float upperLeft = step(reference, texelFetch(shadowMap, ivec3(clamp(base + ivec2(0,1), ivec2(0), maximum), layer), 0).r);
20409
+ float upperRight = step(reference, texelFetch(shadowMap, ivec3(clamp(base + ivec2(1,1), ivec2(0), maximum), layer), 0).r);
20410
+ return mix(mix(lowerLeft, lowerRight, fraction.x), mix(upperLeft, upperRight, fraction.x), fraction.y);
20411
+ }
20412
+ float shadow_arrayPCSS(sampler2DArray shadowMap, int layer, vec3 projected, float searchRadius, float rotation, float penumbraScale) {
20413
+ if (!shadow_validProjection(projected)) return 1.0;
20414
+ float texel = 1.0 / float(textureSize(shadowMap, 0).x);
20415
+ searchRadius = max(texel, searchRadius);
20416
+ float blockerDepth = 0.0;
20417
+ float blockerCount = 0.0;
20418
+ for (int index = 0; index < 24; index++) {
20419
+ if (index >= shadow.blockerSampleCount) break;
20420
+ vec2 coordinate = clamp(projected.xy + shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius, vec2(0.0), vec2(1.0));
20421
+ float depth = textureLod(shadowMap, vec3(coordinate, float(layer)), 0.0).r;
20422
+ if (depth < projected.z) { blockerDepth += depth; blockerCount += 1.0; }
20423
+ }
20424
+ if (blockerCount == 0.0) return 1.0;
20425
+ float averageBlocker = blockerDepth / blockerCount;
20426
+ float penumbra = clamp((projected.z - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
20427
+ float radius = max(texel, searchRadius * (1.0 + penumbra * penumbraScale));
20428
+ float visibility = 0.0;
20429
+ for (int index = 0; index < 48; index++) {
20430
+ if (index >= shadow.filterSampleCount) break;
20431
+ vec2 coordinate = clamp(projected.xy + shadow_diskSample(index, shadow.filterSampleCount, rotation) * radius, vec2(0.0), vec2(1.0));
20432
+ visibility += shadow_compare(shadowMap, coordinate, layer, projected.z);
20433
+ }
20434
+ return visibility / max(float(shadow.filterSampleCount), 1.0);
20435
+ }
20436
+ int shadow_directionalCascadeIndex(float viewDepth) {
20437
+ int cascadeIndex = 0;
20438
+ for (int index = 0; index < 4; index++) {
20439
+ if (index >= shadow.cascadeCount) break;
20440
+ cascadeIndex = index;
20441
+ if (viewDepth <= shadow.cascadeSplits[index]) break;
20442
+ }
20443
+ return cascadeIndex;
20444
+ }
20445
+ int shadow_getDirectionalCascadeIndex(float viewDepth) {
20446
+ return shadow.directionalLightCount == 0 ? -1 : shadow_directionalCascadeIndex(viewDepth);
20447
+ }
20448
+ float shadow_directionalPCSS(int cascadeIndex, vec3 worldPosition, float viewDepth) {
20449
+ DirectionalShadowLightUniform light = shadow.directionalLights[0];
20450
+ return shadow_arrayPCSS(directionalShadowTexture, cascadeIndex,
20451
+ shadow_project(shadow.directionalViewProjectionMatrices[cascadeIndex], worldPosition),
20452
+ light.sourceAngularRadius * viewDepth / max(light.shadowDistance, 0.001),
20453
+ shadow_hash(worldPosition) * 2.0 * SHADOW_PI, 18.0);
20454
+ }
20455
+ float shadow_getDirectionalFactor(vec3 worldPosition, vec3 worldNormal, float viewDepth) {
20456
+ if (shadow.directionalLightCount == 0 || viewDepth <= 0.0) return 1.0;
20457
+ DirectionalShadowLightUniform light = shadow.directionalLights[0];
20458
+ if (viewDepth >= light.shadowDistance) return 1.0;
20459
+ vec3 position = worldPosition + normalize(worldNormal) * light.normalBias;
20460
+ int cascadeIndex = shadow_directionalCascadeIndex(viewDepth);
20461
+ float visibility = shadow_directionalPCSS(cascadeIndex, position, viewDepth);
20462
+ if (cascadeIndex + 1 < shadow.cascadeCount) {
20463
+ float cascadeStart = cascadeIndex > 0 ? shadow.cascadeSplits[cascadeIndex - 1] : 0.0;
20464
+ float cascadeEnd = shadow.cascadeSplits[cascadeIndex];
20465
+ float blendWidth = max((cascadeEnd - cascadeStart) * light.cascadeBlendFraction, 0.0001);
20466
+ float blend = smoothstep(cascadeEnd - blendWidth, cascadeEnd, viewDepth);
20467
+ if (blend > 0.0) visibility = mix(visibility, shadow_directionalPCSS(cascadeIndex + 1, position, viewDepth), blend);
20468
+ }
20469
+ float fadeWidth = max(light.shadowDistance * light.farFadeFraction, 0.0001);
20470
+ visibility = mix(visibility, 1.0, smoothstep(light.shadowDistance - fadeWidth, light.shadowDistance, viewDepth));
20471
+ return mix(1.0, visibility, light.strength);
20472
+ }
20473
+ float shadow_getSpotFactor(int lightIndex, vec3 worldPosition, vec3 worldNormal) {
20474
+ if (lightIndex < 0 || lightIndex >= shadow.spotLightCount) return 1.0;
20475
+ SpotShadowLightUniform light = shadow.spotLights[lightIndex];
20476
+ vec3 fromLight = worldPosition - light.position;
20477
+ float distanceToLight = length(fromLight);
20478
+ if (distanceToLight >= light.range || dot(normalize(fromLight), normalize(light.direction)) < light.outerConeCos) return 1.0;
20479
+ vec3 position = worldPosition + normalize(worldNormal) * light.normalBias;
20480
+ float visibility = shadow_arrayPCSS(spotShadowTexture, lightIndex,
20481
+ shadow_project(shadow.spotViewProjectionMatrices[lightIndex], position),
20482
+ light.sourceRadius / max(distanceToLight, 0.001) * 0.5,
20483
+ shadow_hash(worldPosition) * 2.0 * SHADOW_PI, 16.0);
20484
+ return mix(1.0, visibility, light.strength);
20485
+ }
20486
+ float shadow_pointReferenceDepth(float majorDistance, float nearPlane, float farPlane) {
20487
+ return farPlane / (farPlane - nearPlane) - (farPlane * nearPlane) / ((farPlane - nearPlane) * majorDistance);
20488
+ }
20489
+ vec3 shadow_pointCoordinate(int lightIndex, vec3 direction) {
20490
+ vec3 magnitude = abs(direction);
20491
+ int face = magnitude.x >= magnitude.y && magnitude.x >= magnitude.z ? (direction.x >= 0.0 ? 0 : 1)
20492
+ : magnitude.y >= magnitude.z ? (direction.y >= 0.0 ? 2 : 3) : (direction.z >= 0.0 ? 4 : 5);
20493
+ PointShadowLightUniform light = shadow.pointLights[lightIndex];
20494
+ vec3 projected = shadow_project(shadow.pointViewProjectionMatrices[lightIndex * 6 + face], light.position + direction * light.range * 0.5);
20495
+ return vec3(projected.xy, float(lightIndex * 6 + face));
20496
+ }
20497
+ float shadow_getPointFactor(int lightIndex, vec3 worldPosition, vec3 worldNormal) {
20498
+ if (lightIndex < 0 || lightIndex >= shadow.pointLightCount) return 1.0;
20499
+ PointShadowLightUniform light = shadow.pointLights[lightIndex];
20500
+ vec3 fromLight = worldPosition + normalize(worldNormal) * light.normalBias - light.position;
20501
+ float distanceToLight = length(fromLight);
20502
+ if (distanceToLight >= light.range || distanceToLight <= light.nearPlane) return 1.0;
20503
+ vec3 direction = normalize(fromLight);
20504
+ float referenceDepth = shadow_pointReferenceDepth(max(max(abs(fromLight.x), abs(fromLight.y)), abs(fromLight.z)), light.nearPlane, light.range);
20505
+ vec3 reference = abs(direction.y) > 0.99 ? vec3(0,0,1) : vec3(0,1,0);
20506
+ vec3 tangent = normalize(cross(direction, reference));
20507
+ vec3 bitangent = normalize(cross(direction, tangent));
20508
+ float searchRadius = light.sourceRadius / max(distanceToLight, 0.001);
20509
+ float rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;
20510
+ float blockerDepth = 0.0;
20511
+ float blockerCount = 0.0;
20512
+ for (int index = 0; index < 24; index++) {
20513
+ if (index >= shadow.blockerSampleCount) break;
20514
+ vec2 disk = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;
20515
+ vec3 coordinate = shadow_pointCoordinate(lightIndex, normalize(direction + tangent * disk.x + bitangent * disk.y));
20516
+ float depth = textureLod(pointShadowTexture, coordinate, 0.0).r;
20517
+ if (depth < referenceDepth) { blockerDepth += depth; blockerCount += 1.0; }
20518
+ }
20519
+ if (blockerCount == 0.0) return 1.0;
20520
+ float averageBlocker = blockerDepth / blockerCount;
20521
+ float penumbra = clamp((referenceDepth - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
20522
+ float radius = searchRadius * (1.0 + penumbra * 12.0);
20523
+ float visibility = 0.0;
20524
+ for (int index = 0; index < 48; index++) {
20525
+ if (index >= shadow.filterSampleCount) break;
20526
+ vec2 disk = shadow_diskSample(index, shadow.filterSampleCount, rotation) * radius;
20527
+ vec3 coordinate = shadow_pointCoordinate(lightIndex, normalize(direction + tangent * disk.x + bitangent * disk.y));
20528
+ visibility += shadow_compare(pointShadowTexture, coordinate.xy, int(coordinate.z), referenceDepth);
20529
+ }
20530
+ return mix(1.0, visibility / max(float(shadow.filterSampleCount), 1.0), light.strength);
20531
+ }
20532
+ `
20533
+ );
20534
+
20535
+ // src/modules/lighting/shadow/shadow.ts
20536
+ var MAX_CASCADES = 4;
20537
+ var MAX_SPOT_LIGHTS = 4;
20538
+ var MAX_POINT_LIGHTS = 4;
20539
+ var MAX_POINT_MATRICES = 24;
20540
+ var DIRECTIONAL_LIGHT_UNIFORM_TYPE = {
20541
+ direction: "vec3<f32>",
20542
+ strength: "f32",
20543
+ normalBias: "f32",
20544
+ sourceAngularRadius: "f32",
20545
+ cascadeBlendFraction: "f32",
20546
+ farFadeFraction: "f32",
20547
+ shadowDistance: "f32"
20548
+ };
20549
+ var SPOT_LIGHT_UNIFORM_TYPE = {
20550
+ position: "vec3<f32>",
20551
+ range: "f32",
20552
+ direction: "vec3<f32>",
20553
+ outerConeCos: "f32",
20554
+ sourceRadius: "f32",
20555
+ normalBias: "f32",
20556
+ strength: "f32",
20557
+ nearPlane: "f32"
20558
+ };
20559
+ var POINT_LIGHT_UNIFORM_TYPE = {
20560
+ position: "vec3<f32>",
20561
+ range: "f32",
20562
+ sourceRadius: "f32",
20563
+ normalBias: "f32",
20564
+ strength: "f32",
20565
+ nearPlane: "f32"
20566
+ };
20567
+ var SHADOW_WGSL = (
20568
+ /* wgsl */
20569
+ `const SHADOW_PI: f32 = 3.141592653589793;
20570
+
20571
+ struct DirectionalShadowLightUniform {
20572
+ direction: vec3f,
20573
+ strength: f32,
20574
+ normalBias: f32,
20575
+ sourceAngularRadius: f32,
20576
+ cascadeBlendFraction: f32,
20577
+ farFadeFraction: f32,
20578
+ shadowDistance: f32,
20579
+ };
20580
+
20581
+ struct SpotShadowLightUniform {
20582
+ position: vec3f,
20583
+ range: f32,
20584
+ direction: vec3f,
20585
+ outerConeCos: f32,
20586
+ sourceRadius: f32,
20587
+ normalBias: f32,
20588
+ strength: f32,
20589
+ nearPlane: f32,
20590
+ };
20591
+
20592
+ struct PointShadowLightUniform {
20593
+ position: vec3f,
20594
+ range: f32,
20595
+ sourceRadius: f32,
20596
+ normalBias: f32,
20597
+ strength: f32,
20598
+ nearPlane: f32,
20599
+ };
20600
+
20601
+ struct ShadowUniforms {
20602
+ directionalLightCount: i32,
20603
+ spotLightCount: i32,
20604
+ pointLightCount: i32,
20605
+ cascadeCount: i32,
20606
+ blockerSampleCount: i32,
20607
+ filterSampleCount: i32,
20608
+ cascadeSplits: vec4f,
20609
+ directionalViewProjectionMatrices: array<mat4x4f, 4>,
20610
+ spotViewProjectionMatrices: array<mat4x4f, 4>,
20611
+ pointViewProjectionMatrices: array<mat4x4f, 24>,
20612
+ directionalLights: array<DirectionalShadowLightUniform, 1>,
20613
+ spotLights: array<SpotShadowLightUniform, 4>,
20614
+ pointLights: array<PointShadowLightUniform, 4>,
20615
+ };
20616
+
20617
+ @group(2) @binding(auto) var<uniform> shadow: ShadowUniforms;
20618
+ @group(2) @binding(auto) var directionalShadowTexture: texture_depth_2d_array;
20619
+ @group(2) @binding(auto) var spotShadowTexture: texture_depth_2d_array;
20620
+ @group(2) @binding(auto) var pointShadowTexture: texture_depth_cube_array;
20621
+ @group(2) @binding(auto) var directionalShadowTextureSampler: sampler;
20622
+ @group(2) @binding(auto) var shadowComparisonSampler: sampler_comparison;
20623
+
20624
+ fn shadow_hash(position: vec3f) -> f32 {
20625
+ return fract(sin(dot(position, vec3f(12.9898, 78.233, 37.719))) * 43758.5453);
20626
+ }
20627
+
20628
+ fn shadow_diskSample(index: i32, count: i32, rotation: f32) -> vec2f {
20629
+ let fraction = (f32(index) + 0.5) / max(f32(count), 1.0);
20630
+ let angle = f32(index) * 2.39996323 + rotation;
20631
+ return sqrt(fraction) * vec2f(cos(angle), sin(angle));
20632
+ }
20633
+
20634
+ fn shadow_project(matrix: mat4x4f, worldPosition: vec3f) -> vec3f {
20635
+ let clip = matrix * vec4f(worldPosition, 1.0);
20636
+ let ndc = clip.xyz / max(abs(clip.w), 0.00001);
20637
+ return vec3f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5, ndc.z);
20638
+ }
20639
+
20640
+ fn shadow_validProjection(projected: vec3f) -> bool {
20641
+ return all(projected.xy >= vec2f(0.0)) && all(projected.xy <= vec2f(1.0)) &&
20642
+ projected.z >= 0.0 && projected.z <= 1.0;
20643
+ }
20644
+
20645
+ fn shadow_directionalCascadeIndex(viewDepth: f32) -> i32 {
20646
+ var cascadeIndex = 0;
20647
+ for (var index: i32 = 0; index < 4; index++) {
20648
+ if (index >= shadow.cascadeCount) { break; }
20649
+ cascadeIndex = index;
20650
+ if (viewDepth <= shadow.cascadeSplits[index]) { break; }
20651
+ }
20652
+ return cascadeIndex;
20653
+ }
20654
+
20655
+ fn shadow_getDirectionalCascadeIndex(viewDepth: f32) -> i32 {
20656
+ if (shadow.directionalLightCount == 0) { return -1; }
20657
+ return shadow_directionalCascadeIndex(viewDepth);
20658
+ }
20659
+
20660
+ fn shadow_directionalPCSS(cascadeIndex: i32, worldPosition: vec3f, viewDepth: f32) -> f32 {
20661
+ let projected = shadow_project(shadow.directionalViewProjectionMatrices[cascadeIndex], worldPosition);
20662
+ if (!shadow_validProjection(projected)) { return 1.0; }
20663
+ let dimensions = vec2f(textureDimensions(directionalShadowTexture));
20664
+ let texel = 1.0 / dimensions;
20665
+ let light = shadow.directionalLights[0];
20666
+ let rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;
20667
+ let searchRadius = max(texel.x, light.sourceAngularRadius * viewDepth / max(light.shadowDistance, 0.001));
20668
+ var blockerDepth = 0.0;
20669
+ var blockerCount = 0.0;
20670
+ for (var index: i32 = 0; index < 24; index++) {
20671
+ if (index >= shadow.blockerSampleCount) { break; }
20672
+ let offset = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;
20673
+ let sampleDepth = textureSampleLevel(
20674
+ directionalShadowTexture,
20675
+ directionalShadowTextureSampler,
20676
+ clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),
20677
+ cascadeIndex,
20678
+ 0
20679
+ );
20680
+ if (sampleDepth < projected.z) {
20681
+ blockerDepth += sampleDepth;
20682
+ blockerCount += 1.0;
20683
+ }
20684
+ }
20685
+ if (blockerCount == 0.0) { return 1.0; }
20686
+ let averageBlocker = blockerDepth / blockerCount;
20687
+ let penumbra = clamp((projected.z - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
20688
+ let filterRadius = max(texel.x, searchRadius * (1.0 + penumbra * 18.0));
20689
+ var visibility = 0.0;
20690
+ for (var index: i32 = 0; index < 48; index++) {
20691
+ if (index >= shadow.filterSampleCount) { break; }
20692
+ let offset = shadow_diskSample(index, shadow.filterSampleCount, rotation) * filterRadius;
20693
+ visibility += textureSampleCompareLevel(
20694
+ directionalShadowTexture,
20695
+ shadowComparisonSampler,
20696
+ clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),
20697
+ cascadeIndex,
20698
+ projected.z
20699
+ );
20700
+ }
20701
+ return visibility / max(f32(shadow.filterSampleCount), 1.0);
20702
+ }
20703
+
20704
+ fn shadow_getDirectionalFactor(worldPosition: vec3f, worldNormal: vec3f, viewDepth: f32) -> f32 {
20705
+ if (shadow.directionalLightCount == 0 || viewDepth <= 0.0) { return 1.0; }
20706
+ let light = shadow.directionalLights[0];
20707
+ if (viewDepth >= light.shadowDistance) { return 1.0; }
20708
+ let biasedPosition = worldPosition + normalize(worldNormal) * light.normalBias;
20709
+ let cascadeIndex = shadow_directionalCascadeIndex(viewDepth);
20710
+ var visibility = shadow_directionalPCSS(cascadeIndex, biasedPosition, viewDepth);
20711
+ if (cascadeIndex + 1 < shadow.cascadeCount) {
20712
+ var cascadeStart = 0.0;
20713
+ if (cascadeIndex > 0) { cascadeStart = shadow.cascadeSplits[cascadeIndex - 1]; }
20714
+ let cascadeEnd = shadow.cascadeSplits[cascadeIndex];
20715
+ let blendWidth = max((cascadeEnd - cascadeStart) * light.cascadeBlendFraction, 0.0001);
20716
+ let blend = smoothstep(cascadeEnd - blendWidth, cascadeEnd, viewDepth);
20717
+ if (blend > 0.0) {
20718
+ visibility = mix(
20719
+ visibility,
20720
+ shadow_directionalPCSS(cascadeIndex + 1, biasedPosition, viewDepth),
20721
+ blend
20722
+ );
20723
+ }
20724
+ }
20725
+ let fadeWidth = max(light.shadowDistance * light.farFadeFraction, 0.0001);
20726
+ let farFade = smoothstep(light.shadowDistance - fadeWidth, light.shadowDistance, viewDepth);
20727
+ visibility = mix(visibility, 1.0, farFade);
20728
+ return mix(1.0, visibility, light.strength);
20729
+ }
20730
+
20731
+ fn shadow_spotPCSS(lightIndex: i32, worldPosition: vec3f) -> f32 {
20732
+ let projected = shadow_project(shadow.spotViewProjectionMatrices[lightIndex], worldPosition);
20733
+ if (!shadow_validProjection(projected)) { return 1.0; }
20734
+ let light = shadow.spotLights[lightIndex];
20735
+ let dimensions = vec2f(textureDimensions(spotShadowTexture));
20736
+ let texel = 1.0 / dimensions;
20737
+ let distanceToLight = length(worldPosition - light.position);
20738
+ let searchRadius = max(texel.x, light.sourceRadius / max(distanceToLight, 0.001) * 0.5);
20739
+ let rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;
20740
+ var blockerDepth = 0.0;
20741
+ var blockerCount = 0.0;
20742
+ for (var index: i32 = 0; index < 24; index++) {
20743
+ if (index >= shadow.blockerSampleCount) { break; }
20744
+ let offset = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;
20745
+ let sampleDepth = textureSampleLevel(
20746
+ spotShadowTexture,
20747
+ directionalShadowTextureSampler,
20748
+ clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),
20749
+ lightIndex,
20750
+ 0
20751
+ );
20752
+ if (sampleDepth < projected.z) { blockerDepth += sampleDepth; blockerCount += 1.0; }
20753
+ }
20754
+ if (blockerCount == 0.0) { return 1.0; }
20755
+ let averageBlocker = blockerDepth / blockerCount;
20756
+ let penumbra = clamp((projected.z - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
20757
+ let filterRadius = max(texel.x, searchRadius * (1.0 + penumbra * 16.0));
20758
+ var visibility = 0.0;
20759
+ for (var index: i32 = 0; index < 48; index++) {
20760
+ if (index >= shadow.filterSampleCount) { break; }
20761
+ let offset = shadow_diskSample(index, shadow.filterSampleCount, rotation) * filterRadius;
20762
+ visibility += textureSampleCompareLevel(
20763
+ spotShadowTexture,
20764
+ shadowComparisonSampler,
20765
+ clamp(projected.xy + offset, vec2f(0.0), vec2f(1.0)),
20766
+ lightIndex,
20767
+ projected.z
20768
+ );
20769
+ }
20770
+ return visibility / max(f32(shadow.filterSampleCount), 1.0);
20771
+ }
20772
+
20773
+ fn shadow_getSpotFactor(lightIndex: i32, worldPosition: vec3f, worldNormal: vec3f) -> f32 {
20774
+ if (lightIndex < 0 || lightIndex >= shadow.spotLightCount) { return 1.0; }
20775
+ let light = shadow.spotLights[lightIndex];
20776
+ let fromLight = worldPosition - light.position;
20777
+ let distanceToLight = length(fromLight);
20778
+ let coneCos = dot(normalize(fromLight), normalize(light.direction));
20779
+ if (distanceToLight >= light.range || coneCos < light.outerConeCos) { return 1.0; }
20780
+ let biasedPosition = worldPosition + normalize(worldNormal) * light.normalBias;
20781
+ return mix(1.0, shadow_spotPCSS(lightIndex, biasedPosition), light.strength);
20782
+ }
20783
+
20784
+ fn shadow_pointReferenceDepth(majorDistance: f32, nearPlane: f32, farPlane: f32) -> f32 {
20785
+ // Match the cube-face projection and stored WebGPU depth in [0, 1].
20786
+ return farPlane / (farPlane - nearPlane) -
20787
+ (farPlane * nearPlane) / ((farPlane - nearPlane) * majorDistance);
20788
+ }
20789
+
20790
+ fn shadow_pointBasis(direction: vec3f) -> mat2x3f {
20791
+ let reference = select(vec3f(0.0, 1.0, 0.0), vec3f(0.0, 0.0, 1.0), abs(direction.y) > 0.99);
20792
+ let tangent = normalize(cross(direction, reference));
20793
+ return mat2x3f(tangent, normalize(cross(direction, tangent)));
20794
+ }
20795
+
20796
+ fn shadow_getPointFactor(lightIndex: i32, worldPosition: vec3f, worldNormal: vec3f) -> f32 {
20797
+ if (lightIndex < 0 || lightIndex >= shadow.pointLightCount) { return 1.0; }
20798
+ let light = shadow.pointLights[lightIndex];
20799
+ let biasedPosition = worldPosition + normalize(worldNormal) * light.normalBias;
20800
+ let fromLight = biasedPosition - light.position;
20801
+ let distanceToLight = length(fromLight);
20802
+ if (distanceToLight >= light.range || distanceToLight <= light.nearPlane) { return 1.0; }
20803
+ let direction = normalize(fromLight);
20804
+ let majorDistance = max(max(abs(fromLight.x), abs(fromLight.y)), abs(fromLight.z));
20805
+ let referenceDepth = shadow_pointReferenceDepth(majorDistance, light.nearPlane, light.range);
20806
+ let searchRadius = light.sourceRadius / max(distanceToLight, 0.001);
20807
+ let rotation = shadow_hash(worldPosition) * 2.0 * SHADOW_PI;
20808
+ let basis = shadow_pointBasis(direction);
20809
+ var blockerDepth = 0.0;
20810
+ var blockerCount = 0.0;
20811
+ for (var index: i32 = 0; index < 24; index++) {
20812
+ if (index >= shadow.blockerSampleCount) { break; }
20813
+ let disk = shadow_diskSample(index, shadow.blockerSampleCount, rotation) * searchRadius;
20814
+ let sampleDirection = normalize(direction + basis[0] * disk.x + basis[1] * disk.y);
20815
+ let sampleDepth = textureSampleLevel(
20816
+ pointShadowTexture,
20817
+ directionalShadowTextureSampler,
20818
+ sampleDirection,
20819
+ lightIndex,
20820
+ 0
20821
+ );
20822
+ if (sampleDepth < referenceDepth) { blockerDepth += sampleDepth; blockerCount += 1.0; }
20823
+ }
20824
+ if (blockerCount == 0.0) { return 1.0; }
20825
+ let averageBlocker = blockerDepth / blockerCount;
20826
+ let penumbra = clamp((referenceDepth - averageBlocker) / max(abs(averageBlocker), 0.001), 0.0, 1.0);
20827
+ let filterRadius = searchRadius * (1.0 + penumbra * 12.0);
20828
+ var visibility = 0.0;
20829
+ for (var index: i32 = 0; index < 48; index++) {
20830
+ if (index >= shadow.filterSampleCount) { break; }
20831
+ let disk = shadow_diskSample(index, shadow.filterSampleCount, rotation) * filterRadius;
20832
+ let sampleDirection = normalize(direction + basis[0] * disk.x + basis[1] * disk.y);
20833
+ visibility += textureSampleCompareLevel(
20834
+ pointShadowTexture,
20835
+ shadowComparisonSampler,
20836
+ sampleDirection,
20837
+ lightIndex,
20838
+ referenceDepth
20839
+ );
20840
+ }
20841
+ visibility /= max(f32(shadow.filterSampleCount), 1.0);
20842
+ return mix(1.0, visibility, light.strength);
20843
+ }
20844
+ `
20845
+ );
20846
+ var shadow = {
20847
+ name: "shadow",
20848
+ source: SHADOW_WGSL,
20849
+ fs: SHADOW_GLSL,
20850
+ props: {},
20851
+ uniforms: {},
20852
+ bindings: {},
20853
+ uniformTypes: {
20854
+ directionalLightCount: "i32",
20855
+ spotLightCount: "i32",
20856
+ pointLightCount: "i32",
20857
+ cascadeCount: "i32",
20858
+ blockerSampleCount: "i32",
20859
+ filterSampleCount: "i32",
20860
+ cascadeSplits: "vec4<f32>",
20861
+ directionalViewProjectionMatrices: ["mat4x4<f32>", MAX_CASCADES],
20862
+ spotViewProjectionMatrices: ["mat4x4<f32>", MAX_SPOT_LIGHTS],
20863
+ pointViewProjectionMatrices: ["mat4x4<f32>", MAX_POINT_MATRICES],
20864
+ directionalLights: [DIRECTIONAL_LIGHT_UNIFORM_TYPE, 1],
20865
+ spotLights: [SPOT_LIGHT_UNIFORM_TYPE, MAX_SPOT_LIGHTS],
20866
+ pointLights: [POINT_LIGHT_UNIFORM_TYPE, MAX_POINT_LIGHTS]
20867
+ },
20868
+ bindingLayout: [
20869
+ { name: "shadow", group: 2 },
20870
+ { name: "directionalShadowTexture", group: 2 },
20871
+ { name: "spotShadowTexture", group: 2 },
20872
+ { name: "pointShadowTexture", group: 2 },
20873
+ { name: "shadowComparisonSampler", group: 2 }
20874
+ ],
20875
+ getUniforms(props) {
20876
+ 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) {
20877
+ return {};
20878
+ }
20879
+ return {
20880
+ directionalLightCount: props.directionalLights.length,
20881
+ spotLightCount: props.spotLights.length,
20882
+ pointLightCount: props.pointLights.length,
20883
+ cascadeCount: props.cascadeCount,
20884
+ blockerSampleCount: props.blockerSampleCount,
20885
+ filterSampleCount: props.filterSampleCount,
20886
+ cascadeSplits: props.cascadeSplits,
20887
+ directionalViewProjectionMatrices: props.directionalViewProjectionMatrices,
20888
+ spotViewProjectionMatrices: props.spotViewProjectionMatrices,
20889
+ pointViewProjectionMatrices: props.pointViewProjectionMatrices,
20890
+ directionalLights: padDirectionalLights(props.directionalLights),
20891
+ spotLights: padSpotLights(props.spotLights),
20892
+ pointLights: padPointLights(props.pointLights),
20893
+ directionalShadowTexture: props.directionalShadowTexture,
20894
+ spotShadowTexture: props.spotShadowTexture,
20895
+ pointShadowTexture: props.pointShadowTexture,
20896
+ ...props.directionalShadowTexture.device.type === "webgpu" ? { shadowComparisonSampler: props.comparisonSampler } : {}
20897
+ };
20898
+ }
20899
+ };
20900
+ function padDirectionalLights(lights) {
20901
+ const light = lights[0];
20902
+ return [
20903
+ light ? {
20904
+ direction: light.direction,
20905
+ strength: light.strength ?? 1,
20906
+ normalBias: light.normalBias ?? 0.04,
20907
+ sourceAngularRadius: light.sourceAngularRadius ?? 465e-5,
20908
+ cascadeBlendFraction: light.cascadeBlendFraction ?? 0.1,
20909
+ farFadeFraction: light.farFadeFraction ?? 0.1,
20910
+ shadowDistance: light.shadowDistance ?? 1
20911
+ } : {
20912
+ direction: [0, 1, 0],
20913
+ strength: 0,
20914
+ normalBias: 0,
20915
+ sourceAngularRadius: 0,
20916
+ cascadeBlendFraction: 0,
20917
+ farFadeFraction: 0,
20918
+ shadowDistance: 1
20919
+ }
20920
+ ];
20921
+ }
20922
+ function padSpotLights(lights) {
20923
+ return padArray(
20924
+ lights.map((light) => ({
20925
+ position: light.position,
20926
+ range: light.range,
20927
+ direction: light.direction,
20928
+ outerConeCos: Math.cos(light.outerConeAngle),
20929
+ sourceRadius: light.sourceRadius ?? 0.2,
20930
+ normalBias: light.normalBias ?? 0.025,
20931
+ strength: light.strength ?? 1,
20932
+ nearPlane: light.nearPlane ?? 0.1
20933
+ })),
20934
+ MAX_SPOT_LIGHTS,
20935
+ {
20936
+ position: [0, 0, 0],
20937
+ range: 1,
20938
+ direction: [0, -1, 0],
20939
+ outerConeCos: 1,
20940
+ sourceRadius: 0,
20941
+ normalBias: 0,
20942
+ strength: 0,
20943
+ nearPlane: 0.1
20944
+ }
20945
+ );
20946
+ }
20947
+ function padPointLights(lights) {
20948
+ return padArray(
20949
+ lights.map((light) => ({
20950
+ position: light.position,
20951
+ range: light.range,
20952
+ sourceRadius: light.sourceRadius ?? 0.2,
20953
+ normalBias: light.normalBias ?? 0.025,
20954
+ strength: light.strength ?? 1,
20955
+ nearPlane: light.nearPlane ?? 0.1
20956
+ })),
20957
+ MAX_POINT_LIGHTS,
20958
+ {
20959
+ position: [0, 0, 0],
20960
+ range: 1,
20961
+ sourceRadius: 0,
20962
+ normalBias: 0,
20963
+ strength: 0,
20964
+ nearPlane: 0.1
20965
+ }
20966
+ );
20967
+ }
20968
+ function padArray(values, length, fallback) {
20969
+ const result = values.slice(0, length);
20970
+ while (result.length < length) {
20971
+ result.push(fallback);
20972
+ }
20973
+ return result;
20974
+ }
20975
+
20976
+ // src/modules/lighting/pbr-shadow/pbr-shadow.ts
20977
+ var pbrShadow = {
20978
+ name: "pbrShadow",
20979
+ dependencies: [pbrMaterial, shadow],
20980
+ bindingLayout: [{ name: "pbrShadow", group: 0 }],
20981
+ uniformTypes: { viewMatrix: "mat4x4<f32>", enabled: "i32" },
20982
+ defaultUniforms: {
20983
+ viewMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
20984
+ enabled: 1
20985
+ },
20986
+ getUniforms(properties = {}) {
20987
+ return {
20988
+ ...properties.viewMatrix ? { viewMatrix: properties.viewMatrix } : {},
20989
+ ...properties.enabled === void 0 ? {} : { enabled: Number(properties.enabled) }
20990
+ };
20991
+ },
20992
+ source: (
20993
+ /* wgsl */
20994
+ `
20995
+ struct PBRShadowUniforms {
20996
+ viewMatrix: mat4x4f,
20997
+ enabled: i32,
20998
+ };
20999
+ @group(0) @binding(auto) var<uniform> pbrShadow: PBRShadowUniforms;
21000
+ fn pbrShadow_filterColorWithDirectionalContact(vertexColor: vec4f, worldPosition: vec3f, worldNormal: vec3f, contactVisibility: f32) -> vec4f {
21001
+ var directionalVisibility = 1.0;
21002
+ var pointVisibility = vec4f(1.0);
21003
+ var spotVisibility = vec4f(1.0);
21004
+ if (pbrShadow.enabled != 0) {
21005
+ let normal = normalize(worldNormal);
21006
+ let viewDepth = -(pbrShadow.viewMatrix * vec4f(worldPosition, 1.0)).z;
21007
+ directionalVisibility = shadow_getDirectionalFactor(worldPosition, normal, viewDepth);
21008
+ for (var index = 0; index < 4; index++) {
21009
+ pointVisibility[index] = shadow_getPointFactor(index, worldPosition, normal);
21010
+ spotVisibility[index] = shadow_getSpotFactor(index, worldPosition, normal);
21011
+ }
21012
+ }
21013
+ return pbr_filterColorWithShadows(vertexColor, min(directionalVisibility, contactVisibility), pointVisibility, spotVisibility);
21014
+ }
21015
+ fn pbrShadow_filterColor(vertexColor: vec4f, worldPosition: vec3f, worldNormal: vec3f) -> vec4f {
21016
+ return pbrShadow_filterColorWithDirectionalContact(vertexColor, worldPosition, worldNormal, 1.0);
21017
+ }`
21018
+ ),
21019
+ fs: (
21020
+ /* glsl */
21021
+ `
21022
+ layout(std140) uniform pbrShadowUniforms {
21023
+ mat4 viewMatrix;
21024
+ int enabled;
21025
+ } pbrShadow;
21026
+ vec4 pbrShadow_filterColorWithDirectionalContact(vec4 vertexColor, vec3 worldPosition, vec3 worldNormal, float contactVisibility) {
21027
+ float directionalVisibility = 1.0;
21028
+ vec4 pointVisibility = vec4(1.0);
21029
+ vec4 spotVisibility = vec4(1.0);
21030
+ if (pbrShadow.enabled != 0) {
21031
+ vec3 normal = normalize(worldNormal);
21032
+ float viewDepth = -(pbrShadow.viewMatrix * vec4(worldPosition, 1.0)).z;
21033
+ directionalVisibility = shadow_getDirectionalFactor(worldPosition, normal, viewDepth);
21034
+ for (int index = 0; index < 4; index++) {
21035
+ pointVisibility[index] = shadow_getPointFactor(index, worldPosition, normal);
21036
+ spotVisibility[index] = shadow_getSpotFactor(index, worldPosition, normal);
21037
+ }
21038
+ }
21039
+ return pbr_filterColorWithShadows(vertexColor, min(directionalVisibility, contactVisibility), pointVisibility, spotVisibility);
21040
+ }
21041
+ vec4 pbrShadow_filterColor(vec4 vertexColor, vec3 worldPosition, vec3 worldNormal) {
21042
+ return pbrShadow_filterColorWithDirectionalContact(vertexColor, worldPosition, worldNormal, 1.0);
21043
+ }`
21044
+ )
21045
+ };
21046
+
21047
+ // src/modules/lighting/pbr-surface/pbr-surface.ts
21048
+ var pbrSurface = {
21049
+ name: "pbrSurface",
21050
+ dependencies: [pbrMaterial],
21051
+ bindingLayout: [{ name: "pbrSurface", group: 0 }],
21052
+ uniformTypes: { viewMatrix: "mat4x4<f32>", useMaterialNormals: "i32" },
21053
+ defaultUniforms: {
21054
+ viewMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
21055
+ useMaterialNormals: 0
21056
+ },
21057
+ getUniforms(properties = {}) {
21058
+ return {
21059
+ ...properties.viewMatrix ? { viewMatrix: properties.viewMatrix } : {},
21060
+ ...properties.useMaterialNormals === void 0 ? {} : { useMaterialNormals: Number(properties.useMaterialNormals) }
21061
+ };
21062
+ },
21063
+ source: (
21064
+ /* wgsl */
21065
+ `
21066
+ struct PBRSurfaceUniforms {
21067
+ viewMatrix: mat4x4f,
21068
+ useMaterialNormals: i32,
21069
+ };
21070
+ @group(0) @binding(auto) var<uniform> pbrSurface: PBRSurfaceUniforms;
21071
+ fn pbrSurface_getColor() -> vec4f {
21072
+ #ifdef HAS_NORMALS
21073
+ var normal = normalize(fragmentInputs.pbr_vNormal);
21074
+ #ifdef DOUBLE_SIDED
21075
+ if (!pbr_frontFacing) {normal = -normal;}
21076
+ #endif
21077
+ #else
21078
+ var normal = normalize(cross(dpdy(fragmentInputs.pbr_vPosition), dpdx(fragmentInputs.pbr_vPosition)));
21079
+ #endif
21080
+ #ifdef HAS_NORMALMAP
21081
+ if (pbrSurface.useMaterialNormals != 0) {
21082
+ let coordinates = getMaterialUV(pbrMaterial.normalUVSet, pbrMaterial.normalUVTransform);
21083
+ normal = getNormal(getTBN(coordinates), coordinates);
21084
+ }
21085
+ #else
21086
+ #ifdef HAS_BUMPMAP
21087
+ if (pbrSurface.useMaterialNormals != 0) {
21088
+ let coordinates = getMaterialUV(pbrMaterial.normalUVSet, pbrMaterial.normalUVTransform);
21089
+ normal = getNormal(getTBN(coordinates), coordinates);
21090
+ }
21091
+ #endif
21092
+ #endif
21093
+ let viewNormal = normalize((pbrSurface.viewMatrix * vec4f(normal, 0.0)).xyz);
21094
+ return vec4f(viewNormal * 0.5 + vec3f(0.5), 1.0);
21095
+ }`
21096
+ ),
21097
+ fs: (
21098
+ /* glsl */
21099
+ `
21100
+ layout(std140) uniform pbrSurfaceUniforms {
21101
+ mat4 viewMatrix;
21102
+ int useMaterialNormals;
21103
+ } pbrSurface;
21104
+ vec4 pbrSurface_getColor() {
21105
+ #ifdef HAS_NORMALS
21106
+ #ifdef HAS_TANGENTS
21107
+ vec3 normal = normalize(pbr_vTBN[2]);
21108
+ #else
21109
+ vec3 normal = normalize(pbr_vNormal);
21110
+ #endif
21111
+ #ifdef DOUBLE_SIDED
21112
+ if (!gl_FrontFacing) normal = -normal;
21113
+ #endif
21114
+ #else
21115
+ vec3 normal = normalize(cross(dFdx(pbr_vPosition), dFdy(pbr_vPosition)));
21116
+ #endif
21117
+ #ifdef HAS_NORMALMAP
21118
+ if (pbrSurface.useMaterialNormals != 0) {
21119
+ vec2 coordinates = getMaterialUV(pbrMaterial.normalUVSet, pbrMaterial.normalUVTransform);
21120
+ normal = getNormal(getTBN(coordinates), coordinates);
21121
+ }
21122
+ #else
21123
+ #ifdef HAS_BUMPMAP
21124
+ if (pbrSurface.useMaterialNormals != 0) {
21125
+ vec2 coordinates = getMaterialUV(pbrMaterial.normalUVSet, pbrMaterial.normalUVTransform);
21126
+ normal = getNormal(getTBN(coordinates), coordinates);
21127
+ }
21128
+ #endif
21129
+ #endif
21130
+ vec3 viewNormal = normalize((pbrSurface.viewMatrix * vec4(normal, 0.0)).xyz);
21131
+ return vec4(viewNormal * 0.5 + 0.5, 1.0);
21132
+ }`
21133
+ )
21134
+ };
21135
+
21136
+ // src/modules/lighting/contact-shadow/contact-shadow.ts
21137
+ var IDENTITY_MATRIX3 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
21138
+ var contactShadowTrace = {
21139
+ name: "contactShadowTrace",
21140
+ source: (
21141
+ /* wgsl */
21142
+ `struct contactShadowTraceUniforms {
21143
+ projectionMatrix: mat4x4f,
21144
+ inverseProjectionMatrix: mat4x4f,
21145
+ lightDirectionView: vec3f,
21146
+ maxDistance: f32,
21147
+ thickness: f32,
21148
+ stepCount: f32,
21149
+ frameIndex: f32,
21150
+ normalBias: f32,
21151
+ };
21152
+ @group(0) @binding(auto) var<uniform> contactShadowTrace: contactShadowTraceUniforms;
21153
+ @group(0) @binding(auto) var depthTexture: texture_depth_2d;
21154
+ @group(0) @binding(auto) var depthTextureSampler: sampler;
21155
+ @group(0) @binding(auto) var normalTexture: texture_2d<f32>;
21156
+ @group(0) @binding(auto) var normalTextureSampler: sampler;
21157
+
21158
+ fn contactShadow_reconstructViewPosition(uv: vec2f, depth: f32) -> vec3f {
21159
+ let clip = vec4f(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, depth, 1.0);
21160
+ let view = contactShadowTrace.inverseProjectionMatrix * clip;
21161
+ return view.xyz / max(abs(view.w), 0.00001);
21162
+ }
21163
+
21164
+ fn contactShadow_projectViewPosition(position: vec3f) -> vec2f {
21165
+ let clip = contactShadowTrace.projectionMatrix * vec4f(position, 1.0);
21166
+ let ndc = clip.xy / max(abs(clip.w), 0.00001);
21167
+ return vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
21168
+ }
21169
+
21170
+ fn contactShadowTrace_sampleColor(
21171
+ sourceTexture: texture_2d<f32>, sourceTextureSampler: sampler, texSize: vec2f, texCoord: vec2f
21172
+ ) -> vec4f {
21173
+ let centerDepth = textureSampleLevel(depthTexture, depthTextureSampler, texCoord, 0);
21174
+ if (centerDepth >= 0.99999) { return vec4f(1.0); }
21175
+ let centerNormal = normalize(textureSampleLevel(normalTexture, normalTextureSampler, texCoord, 0.0).xyz * 2.0 - 1.0);
21176
+ let centerPosition = contactShadow_reconstructViewPosition(texCoord, centerDepth) + centerNormal * contactShadowTrace.normalBias;
21177
+ let rayDirection = normalize(contactShadowTrace.lightDirectionView);
21178
+ let noise = fract(sin(dot(texCoord * texSize + contactShadowTrace.frameIndex, vec2f(12.9898, 78.233))) * 43758.5453);
21179
+ for (var index: i32 = 0; index < 40; index++) {
21180
+ if (f32(index) >= contactShadowTrace.stepCount) { break; }
21181
+ let fraction = (f32(index) + 0.35 + noise * 0.5) / max(contactShadowTrace.stepCount, 1.0);
21182
+ let travel = contactShadowTrace.maxDistance * fraction * fraction;
21183
+ let rayPosition = centerPosition + rayDirection * travel;
21184
+ let sampleUv = contactShadow_projectViewPosition(rayPosition);
21185
+ if (any(sampleUv <= vec2f(0.0)) || any(sampleUv >= vec2f(1.0))) { break; }
21186
+ let sampleDepth = textureSampleLevel(depthTexture, depthTextureSampler, sampleUv, 0);
21187
+ if (sampleDepth >= 0.99999) { continue; }
21188
+ let scenePosition = contactShadow_reconstructViewPosition(sampleUv, sampleDepth);
21189
+ let depthDelta = (-rayPosition.z) - (-scenePosition.z);
21190
+ let hitThickness = contactShadowTrace.thickness + travel * 0.02;
21191
+ if (depthDelta >= 0.0 && depthDelta <= hitThickness) {
21192
+ let distanceFade = 1.0 - clamp(travel / contactShadowTrace.maxDistance, 0.0, 1.0);
21193
+ return vec4f(vec3f(1.0 - distanceFade), 1.0);
21194
+ }
21195
+ if (depthDelta > hitThickness * 8.0) { break; }
21196
+ }
21197
+ return vec4f(1.0);
21198
+ }`
21199
+ ),
21200
+ fs: (
21201
+ /* glsl */
21202
+ `
21203
+ layout(std140) uniform contactShadowTraceUniforms {
21204
+ mat4 projectionMatrix;
21205
+ mat4 inverseProjectionMatrix;
21206
+ vec3 lightDirectionView;
21207
+ float maxDistance;
21208
+ float thickness;
21209
+ float stepCount;
21210
+ float frameIndex;
21211
+ float normalBias;
21212
+ } contactShadowTrace;
21213
+ uniform sampler2D depthTexture;
21214
+ uniform sampler2D normalTexture;
21215
+ vec3 contactShadow_reconstructViewPosition(vec2 uv, float depth) {
21216
+ vec4 view = contactShadowTrace.inverseProjectionMatrix * vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
21217
+ return view.xyz / max(abs(view.w), 0.00001);
21218
+ }
21219
+ vec2 contactShadow_projectViewPosition(vec3 position) {
21220
+ vec4 clip = contactShadowTrace.projectionMatrix * vec4(position,1.0);
21221
+ return clip.xy / max(abs(clip.w),0.00001) * 0.5 + 0.5;
21222
+ }
21223
+ vec4 contactShadowTrace_sampleColor(sampler2D sourceTexture,vec2 texSize,vec2 texCoord) {
21224
+ float centerDepth = texture(depthTexture,texCoord).r;
21225
+ if(centerDepth >= 0.99999) return vec4(1.0);
21226
+ vec3 centerNormal = normalize(texture(normalTexture,texCoord).xyz * 2.0 - 1.0);
21227
+ vec3 centerPosition = contactShadow_reconstructViewPosition(texCoord,centerDepth) + centerNormal * contactShadowTrace.normalBias;
21228
+ vec3 rayDirection = normalize(contactShadowTrace.lightDirectionView);
21229
+ float noise = fract(sin(dot(texCoord * texSize + contactShadowTrace.frameIndex,vec2(12.9898,78.233))) * 43758.5453);
21230
+ for(int index=0;index<40;index++) {
21231
+ if(float(index)>=contactShadowTrace.stepCount) break;
21232
+ float fraction=(float(index)+0.35+noise*0.5)/max(contactShadowTrace.stepCount,1.0);
21233
+ float travel=contactShadowTrace.maxDistance*fraction*fraction;
21234
+ vec3 rayPosition=centerPosition+rayDirection*travel;
21235
+ vec2 sampleUv=contactShadow_projectViewPosition(rayPosition);
21236
+ if(any(lessThanEqual(sampleUv,vec2(0.0)))||any(greaterThanEqual(sampleUv,vec2(1.0))))break;
21237
+ float sampleDepth=texture(depthTexture,sampleUv).r;
21238
+ if(sampleDepth>=0.99999)continue;
21239
+ vec3 scenePosition=contactShadow_reconstructViewPosition(sampleUv,sampleDepth);
21240
+ float depthDelta=(-rayPosition.z)-(-scenePosition.z);
21241
+ float hitThickness=contactShadowTrace.thickness+travel*0.02;
21242
+ if(depthDelta>=0.0&&depthDelta<=hitThickness) {
21243
+ float distanceFade=1.0-clamp(travel/contactShadowTrace.maxDistance,0.0,1.0);
21244
+ return vec4(vec3(1.0-distanceFade),1.0);
21245
+ }
21246
+ if(depthDelta>hitThickness*8.0)break;
21247
+ }
21248
+ return vec4(1.0);
21249
+ }
21250
+ `
21251
+ ),
21252
+ bindingLayout: [
21253
+ { name: "depthTexture", group: 0 },
21254
+ { name: "normalTexture", group: 0 }
21255
+ ],
21256
+ props: {},
21257
+ uniforms: {},
21258
+ bindings: {},
21259
+ uniformTypes: {
21260
+ projectionMatrix: "mat4x4<f32>",
21261
+ inverseProjectionMatrix: "mat4x4<f32>",
21262
+ lightDirectionView: "vec3<f32>",
21263
+ maxDistance: "f32",
21264
+ thickness: "f32",
21265
+ stepCount: "f32",
21266
+ frameIndex: "f32",
21267
+ normalBias: "f32"
21268
+ },
21269
+ propTypes: {
21270
+ projectionMatrix: { value: IDENTITY_MATRIX3, private: true },
21271
+ inverseProjectionMatrix: { value: IDENTITY_MATRIX3, private: true },
21272
+ lightDirectionView: { value: [0, 1, 0], private: true },
21273
+ maxDistance: { value: 4, min: 0.1, softMax: 16 },
21274
+ thickness: { value: 0.12, min: 5e-3, softMax: 1 },
21275
+ stepCount: { value: 24, min: 1, max: 40 },
21276
+ frameIndex: { value: 0, private: true },
21277
+ normalBias: { value: 0.035, min: 0, softMax: 0.2 }
21278
+ },
21279
+ passes: [{ sampler: true }]
21280
+ };
21281
+ var contactShadowBilateralBlur = {
21282
+ name: "contactShadowBilateralBlur",
21283
+ source: (
21284
+ /* wgsl */
21285
+ `struct contactShadowBilateralBlurUniforms {
21286
+ direction: vec2f,
21287
+ radius: f32,
21288
+ depthSigma: f32,
21289
+ normalPower: f32,
21290
+ };
21291
+ @group(0) @binding(auto) var<uniform> contactShadowBilateralBlur: contactShadowBilateralBlurUniforms;
21292
+ @group(0) @binding(auto) var depthTexture: texture_depth_2d;
21293
+ @group(0) @binding(auto) var depthTextureSampler: sampler;
21294
+ @group(0) @binding(auto) var normalTexture: texture_2d<f32>;
21295
+ @group(0) @binding(auto) var normalTextureSampler: sampler;
21296
+
21297
+ fn contactShadowBilateralBlur_sampleColor(
21298
+ sourceTexture: texture_2d<f32>, sourceTextureSampler: sampler, texSize: vec2f, texCoord: vec2f
21299
+ ) -> vec4f {
21300
+ let sourceDimensions = vec2f(textureDimensions(sourceTexture));
21301
+ let texel = contactShadowBilateralBlur.direction / sourceDimensions;
21302
+ let centerDepth = textureSampleLevel(depthTexture, depthTextureSampler, texCoord, 0);
21303
+ let centerNormal = normalize(textureSampleLevel(normalTexture, normalTextureSampler, texCoord, 0.0).xyz * 2.0 - 1.0);
21304
+ var result = textureSampleLevel(sourceTexture, sourceTextureSampler, texCoord, 0.0).r;
21305
+ var totalWeight = 1.0;
21306
+ for (var index: i32 = 1; index <= 6; index++) {
21307
+ if (f32(index) > contactShadowBilateralBlur.radius) { break; }
21308
+ for (var side: i32 = -1; side <= 1; side += 2) {
21309
+ let sampleUv = clamp(texCoord + texel * f32(index * side), vec2f(0.0), vec2f(1.0));
21310
+ let sampleDepth = textureSampleLevel(depthTexture, depthTextureSampler, sampleUv, 0);
21311
+ let sampleNormal = normalize(textureSampleLevel(normalTexture, normalTextureSampler, sampleUv, 0.0).xyz * 2.0 - 1.0);
21312
+ let depthDelta = abs(sampleDepth - centerDepth);
21313
+ let depthWeight = exp(-(depthDelta * depthDelta) / max(2.0 * contactShadowBilateralBlur.depthSigma * contactShadowBilateralBlur.depthSigma, 0.000001));
21314
+ let normalWeight = pow(max(dot(centerNormal, sampleNormal), 0.0), contactShadowBilateralBlur.normalPower);
21315
+ let spatialWeight = exp(-f32(index * index) / 8.0);
21316
+ let weight = depthWeight * normalWeight * spatialWeight;
21317
+ result += textureSampleLevel(sourceTexture, sourceTextureSampler, sampleUv, 0.0).r * weight;
21318
+ totalWeight += weight;
21319
+ }
21320
+ }
21321
+ let visibility = result / totalWeight;
21322
+ return vec4f(vec3f(visibility), 1.0);
21323
+ }`
21324
+ ),
21325
+ fs: (
21326
+ /* glsl */
21327
+ `
21328
+ layout(std140) uniform contactShadowBilateralBlurUniforms {
21329
+ vec2 direction;
21330
+ float radius;
21331
+ float depthSigma;
21332
+ float normalPower;
21333
+ } contactShadowBilateralBlur;
21334
+ uniform sampler2D depthTexture;
21335
+ uniform sampler2D normalTexture;
21336
+ vec4 contactShadowBilateralBlur_sampleColor(sampler2D sourceTexture,vec2 texSize,vec2 texCoord) {
21337
+ vec2 texel=contactShadowBilateralBlur.direction/vec2(textureSize(sourceTexture,0));
21338
+ float centerDepth=texture(depthTexture,texCoord).r;
21339
+ vec3 centerNormal=normalize(texture(normalTexture,texCoord).xyz*2.0-1.0);
21340
+ float result=texture(sourceTexture,texCoord).r;
21341
+ float totalWeight=1.0;
21342
+ for(int index=1;index<=6;index++) {
21343
+ if(float(index)>contactShadowBilateralBlur.radius)break;
21344
+ for(int side=-1;side<=1;side+=2) {
21345
+ vec2 sampleUv=clamp(texCoord+texel*float(index*side),vec2(0.0),vec2(1.0));
21346
+ float sampleDepth=texture(depthTexture,sampleUv).r;
21347
+ vec3 sampleNormal=normalize(texture(normalTexture,sampleUv).xyz*2.0-1.0);
21348
+ float depthDelta=abs(sampleDepth-centerDepth);
21349
+ float depthWeight=exp(-(depthDelta*depthDelta)/max(2.0*contactShadowBilateralBlur.depthSigma*contactShadowBilateralBlur.depthSigma,0.000001));
21350
+ float normalWeight=pow(max(dot(centerNormal,sampleNormal),0.0),contactShadowBilateralBlur.normalPower);
21351
+ float spatialWeight=exp(-float(index*index)/8.0);
21352
+ float weight=depthWeight*normalWeight*spatialWeight;
21353
+ result+=texture(sourceTexture,sampleUv).r*weight;totalWeight+=weight;
21354
+ }
21355
+ }
21356
+ return vec4(vec3(result/totalWeight),1.0);
21357
+ }
21358
+ `
21359
+ ),
21360
+ bindingLayout: [
21361
+ { name: "depthTexture", group: 0 },
21362
+ { name: "normalTexture", group: 0 }
21363
+ ],
21364
+ props: {},
21365
+ uniforms: {},
21366
+ bindings: {},
21367
+ uniformTypes: {
21368
+ direction: "vec2<f32>",
21369
+ radius: "f32",
21370
+ depthSigma: "f32",
21371
+ normalPower: "f32"
21372
+ },
21373
+ propTypes: {
21374
+ direction: { value: [1, 0], private: true },
21375
+ radius: { value: 4, min: 1, max: 6 },
21376
+ depthSigma: { value: 6e-3, min: 1e-4, softMax: 0.05 },
21377
+ normalPower: { value: 24, min: 1, softMax: 64 }
21378
+ },
21379
+ passes: [{ sampler: true }]
21380
+ };
21381
+ var contactShadowComposite = {
21382
+ name: "contactShadowComposite",
21383
+ source: (
21384
+ /* wgsl */
21385
+ `struct contactShadowCompositeUniforms {
21386
+ strength: f32,
21387
+ debugMode: f32,
21388
+ };
21389
+ @group(0) @binding(auto) var<uniform> contactShadowComposite: contactShadowCompositeUniforms;
21390
+ @group(0) @binding(auto) var contactShadowTexture: texture_2d<f32>;
21391
+ @group(0) @binding(auto) var contactShadowTextureSampler: sampler;
21392
+ @group(0) @binding(auto) var directionalDirectTexture: texture_2d<f32>;
21393
+ @group(0) @binding(auto) var directionalDirectTextureSampler: sampler;
21394
+ @group(0) @binding(auto) var shadowDebugTexture: texture_2d<f32>;
21395
+ @group(0) @binding(auto) var shadowDebugTextureSampler: sampler;
21396
+
21397
+ fn contactShadowComposite_sampleColor(
21398
+ sourceTexture: texture_2d<f32>, sourceTextureSampler: sampler, texSize: vec2f, texCoord: vec2f
21399
+ ) -> vec4f {
21400
+ let color = textureSampleLevel(sourceTexture, sourceTextureSampler, texCoord, 0.0);
21401
+ let contact = textureSampleLevel(contactShadowTexture, contactShadowTextureSampler, texCoord, 0.0).r;
21402
+ let appliedContact = mix(1.0, contact, contactShadowComposite.strength);
21403
+ if (contactShadowComposite.debugMode > 0.5 && contactShadowComposite.debugMode < 1.5) {
21404
+ return vec4f(vec3f(contact), 1.0);
21405
+ }
21406
+ if (contactShadowComposite.debugMode >= 1.5) {
21407
+ let factors = textureSampleLevel(shadowDebugTexture, shadowDebugTextureSampler, texCoord, 0.0).rgb;
21408
+ return vec4f(vec3f(factors.x * factors.y * factors.z * contact), 1.0);
21409
+ }
21410
+ let directionalDirect = textureSampleLevel(
21411
+ directionalDirectTexture,
21412
+ directionalDirectTextureSampler,
21413
+ texCoord,
21414
+ 0.0
21415
+ ).rgb;
21416
+ return vec4f(max(color.rgb - directionalDirect * (1.0 - appliedContact), vec3f(0.0)), color.a);
21417
+ }`
21418
+ ),
21419
+ fs: (
21420
+ /* glsl */
21421
+ `
21422
+ layout(std140) uniform contactShadowCompositeUniforms {
21423
+ float strength;
21424
+ float debugMode;
21425
+ } contactShadowComposite;
21426
+ uniform sampler2D contactShadowTexture;
21427
+ uniform sampler2D directionalDirectTexture;
21428
+ uniform sampler2D shadowDebugTexture;
21429
+ vec4 contactShadowComposite_sampleColor(sampler2D sourceTexture,vec2 texSize,vec2 texCoord) {
21430
+ vec4 color=texture(sourceTexture,texCoord);
21431
+ float contact=texture(contactShadowTexture,texCoord).r;
21432
+ float appliedContact=mix(1.0,contact,contactShadowComposite.strength);
21433
+ if(contactShadowComposite.debugMode>0.5&&contactShadowComposite.debugMode<1.5)return vec4(vec3(contact),1.0);
21434
+ if(contactShadowComposite.debugMode>=1.5){vec3 factors=texture(shadowDebugTexture,texCoord).rgb;return vec4(vec3(factors.x*factors.y*factors.z*contact),1.0);}
21435
+ vec3 directionalDirect=texture(directionalDirectTexture,texCoord).rgb;
21436
+ return vec4(max(color.rgb-directionalDirect*(1.0-appliedContact),vec3(0.0)),color.a);
21437
+ }
21438
+ `
21439
+ ),
21440
+ bindingLayout: [
21441
+ { name: "contactShadowTexture", group: 0 },
21442
+ { name: "directionalDirectTexture", group: 0 },
21443
+ { name: "shadowDebugTexture", group: 0 }
21444
+ ],
21445
+ props: {},
21446
+ uniforms: {},
21447
+ bindings: {},
21448
+ uniformTypes: { strength: "f32", debugMode: "f32" },
21449
+ propTypes: {
21450
+ strength: { value: 1, min: 0, max: 1 },
21451
+ debugMode: { value: 0, min: 0, max: 2, private: true }
21452
+ },
21453
+ passes: [{ sampler: true }]
21454
+ };
21455
+
21456
+ // src/modules/lighting/pbr-contact-shadow/pbr-contact-shadow.ts
21457
+ var pbrContactShadow = {
21458
+ name: "pbrContactShadow",
21459
+ dependencies: [pbrMaterial],
21460
+ bindingLayout: [
21461
+ { name: "pbrContactShadow", group: 0 },
21462
+ { name: "contactShadowTexture", group: 2 }
21463
+ ],
21464
+ uniformTypes: { viewport: "vec4<f32>", strength: "f32", enabled: "i32" },
21465
+ defaultUniforms: { viewport: [0, 0, 1, 1], strength: 1, enabled: 1 },
21466
+ getUniforms(properties = {}) {
21467
+ return {
21468
+ ...properties.viewport ? { viewport: properties.viewport } : {},
21469
+ ...properties.strength === void 0 ? {} : { strength: properties.strength },
21470
+ ...properties.enabled === void 0 ? {} : { enabled: Number(properties.enabled) },
21471
+ ...properties.contactShadowTexture ? { contactShadowTexture: properties.contactShadowTexture } : {}
21472
+ };
21473
+ },
21474
+ source: (
21475
+ /* wgsl */
21476
+ `
21477
+ struct PBRContactShadowUniforms {
21478
+ viewport: vec4f,
21479
+ strength: f32,
21480
+ enabled: i32,
21481
+ };
21482
+ @group(0) @binding(auto) var<uniform> pbrContactShadow: PBRContactShadowUniforms;
21483
+ @group(2) @binding(auto) var contactShadowTexture: texture_2d<f32>;
21484
+ @group(2) @binding(auto) var contactShadowTextureSampler: sampler;
21485
+ fn pbrContactShadow_getFactor(pixelPosition:vec2f)->f32 {
21486
+ if(pbrContactShadow.enabled==0){return 1.0;}
21487
+ let coordinates=(pixelPosition-pbrContactShadow.viewport.xy)/pbrContactShadow.viewport.zw;
21488
+ if(any(coordinates<vec2f(0.0))||any(coordinates>vec2f(1.0))){return 1.0;}
21489
+ let visibility=textureSampleLevel(contactShadowTexture,contactShadowTextureSampler,coordinates,0.0).r;
21490
+ return mix(1.0,clamp(visibility,0.0,1.0),clamp(pbrContactShadow.strength,0.0,1.0));
21491
+ }
21492
+ fn pbrContactShadow_filterColor(vertexColor:vec4f,pixelPosition:vec2f)->vec4f {
21493
+ return pbr_filterColorWithShadows(vertexColor,pbrContactShadow_getFactor(pixelPosition),vec4f(1.0),vec4f(1.0));
21494
+ }`
21495
+ ),
21496
+ fs: (
21497
+ /* glsl */
21498
+ `
21499
+ layout(std140) uniform pbrContactShadowUniforms {
21500
+ vec4 viewport;
21501
+ float strength;
21502
+ int enabled;
21503
+ } pbrContactShadow;
21504
+ uniform sampler2D contactShadowTexture;
21505
+ float pbrContactShadow_getFactor(vec2 pixelPosition) {
21506
+ if(pbrContactShadow.enabled==0)return 1.0;
21507
+ vec2 coordinates=(pixelPosition-pbrContactShadow.viewport.xy)/pbrContactShadow.viewport.zw;
21508
+ if(any(lessThan(coordinates,vec2(0.0)))||any(greaterThan(coordinates,vec2(1.0))))return 1.0;
21509
+ float visibility=texture(contactShadowTexture,coordinates).r;
21510
+ return mix(1.0,clamp(visibility,0.0,1.0),clamp(pbrContactShadow.strength,0.0,1.0));
21511
+ }
21512
+ vec4 pbrContactShadow_filterColor(vec4 vertexColor,vec2 pixelPosition) {
21513
+ return pbr_filterColorWithShadows(vertexColor,pbrContactShadow_getFactor(pixelPosition),vec4(1.0),vec4(1.0));
21514
+ }`
21515
+ )
21516
+ };
21517
+
21518
+ // src/modules/lighting/clustered-light-grid/clustered-light-grid.ts
21519
+ var clusteredLightGrid = {
21520
+ name: "clusteredLightGrid",
21521
+ source: (
21522
+ /* wgsl */
21523
+ `
21524
+ fn clusteredLightGrid_getIndex(coordinates: vec2f, viewDepth: f32, dimensions: vec3u, nearPlane: f32, farPlane: f32) -> u32 {
21525
+ let tile = min(vec2u(clamp(coordinates, vec2f(0.0), vec2f(0.999999)) * vec2f(dimensions.xy)), dimensions.xy - vec2u(1));
21526
+ let normalizedDepth = clamp(log(clamp(viewDepth, nearPlane, farPlane) / nearPlane) / log(farPlane / nearPlane), 0.0, 0.999999);
21527
+ let depthSlice = min(u32(normalizedDepth * f32(dimensions.z)), dimensions.z - 1u);
21528
+ return (depthSlice * dimensions.y + tile.y) * dimensions.x + tile.x;
21529
+ }
21530
+ `
21531
+ )
21532
+ };
21533
+
21534
+ // src/modules/lighting/pbr-ambient-occlusion/pbr-ambient-occlusion.ts
21535
+ var pbrAmbientOcclusion = {
21536
+ name: "pbrAmbientOcclusion",
21537
+ dependencies: [pbrMaterial],
21538
+ bindingLayout: [
21539
+ { name: "pbrAmbientOcclusion", group: 0 },
21540
+ { name: "ambientOcclusionTexture", group: 2 }
21541
+ ],
21542
+ uniformTypes: { viewport: "vec4<f32>", strength: "f32", enabled: "i32" },
21543
+ defaultUniforms: { viewport: [0, 0, 1, 1], strength: 1, enabled: 1 },
21544
+ getUniforms(properties = {}) {
21545
+ return {
21546
+ ...properties.viewport ? { viewport: properties.viewport } : {},
21547
+ ...properties.strength === void 0 ? {} : { strength: properties.strength },
21548
+ ...properties.enabled === void 0 ? {} : { enabled: Number(properties.enabled) },
21549
+ ...properties.ambientOcclusionTexture ? { ambientOcclusionTexture: properties.ambientOcclusionTexture } : {}
21550
+ };
21551
+ },
21552
+ source: (
21553
+ /* wgsl */
21554
+ `
21555
+ struct PBRAmbientOcclusionUniforms {
21556
+ viewport: vec4f,
21557
+ strength: f32,
21558
+ enabled: i32,
21559
+ };
21560
+ @group(0) @binding(auto) var<uniform> pbrAmbientOcclusion: PBRAmbientOcclusionUniforms;
21561
+ @group(2) @binding(auto) var ambientOcclusionTexture: texture_2d<f32>;
21562
+ @group(2) @binding(auto) var ambientOcclusionTextureSampler: sampler;
21563
+ fn pbrAmbientOcclusion_getFactor(pixelPosition:vec2f)->f32 {
21564
+ if(pbrAmbientOcclusion.enabled==0){return 1.0;}
21565
+ let coordinates=(pixelPosition-pbrAmbientOcclusion.viewport.xy)/pbrAmbientOcclusion.viewport.zw;
21566
+ if(any(coordinates<vec2f(0.0))||any(coordinates>vec2f(1.0))){return 1.0;}
21567
+ let visibility=textureSampleLevel(ambientOcclusionTexture,ambientOcclusionTextureSampler,coordinates,0.0).r;
21568
+ return mix(1.0,clamp(visibility,0.0,1.0),clamp(pbrAmbientOcclusion.strength,0.0,1.0));
21569
+ }
21570
+ fn pbrAmbientOcclusion_setFactor(pixelPosition:vec2f) {
21571
+ pbr_ambientVisibility = pbrAmbientOcclusion_getFactor(pixelPosition);
21572
+ }`
21573
+ ),
21574
+ fs: (
21575
+ /* glsl */
21576
+ `
21577
+ layout(std140) uniform pbrAmbientOcclusionUniforms {
21578
+ vec4 viewport;
21579
+ float strength;
21580
+ int enabled;
21581
+ } pbrAmbientOcclusion;
21582
+ uniform sampler2D ambientOcclusionTexture;
21583
+ float pbrAmbientOcclusion_getFactor(vec2 pixelPosition) {
21584
+ if(pbrAmbientOcclusion.enabled==0)return 1.0;
21585
+ vec2 coordinates=(pixelPosition-pbrAmbientOcclusion.viewport.xy)/pbrAmbientOcclusion.viewport.zw;
21586
+ if(any(lessThan(coordinates,vec2(0.0)))||any(greaterThan(coordinates,vec2(1.0))))return 1.0;
21587
+ float visibility=texture(ambientOcclusionTexture,coordinates).r;
21588
+ return mix(1.0,clamp(visibility,0.0,1.0),clamp(pbrAmbientOcclusion.strength,0.0,1.0));
21589
+ }
21590
+ void pbrAmbientOcclusion_setFactor(vec2 pixelPosition) {
21591
+ pbr_ambientVisibility = pbrAmbientOcclusion_getFactor(pixelPosition);
21592
+ }`
21593
+ )
21594
+ };
19877
21595
  return __toCommonJS(bundle_exports);
19878
21596
  })();
19879
21597
  return __exports__;