@zephyr3d/scene 0.9.24 → 0.9.26

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 (264) hide show
  1. package/dist/animation/hair/gpu_hair_simulation.js +1090 -0
  2. package/dist/animation/hair/gpu_hair_simulation.js.map +1 -0
  3. package/dist/animation/joint_dynamics/controller.js +7 -5
  4. package/dist/animation/joint_dynamics/controller.js.map +1 -1
  5. package/dist/animation/joint_dynamics/joint_dynamics_system.js +81 -5
  6. package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
  7. package/dist/animation/morphtrack.js +3 -4
  8. package/dist/animation/morphtrack.js.map +1 -1
  9. package/dist/animation/skeleton.js +58 -24
  10. package/dist/animation/skeleton.js.map +1 -1
  11. package/dist/animation/spring/multi_chain_spring_system.js +577 -179
  12. package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
  13. package/dist/animation/spring/spring_collider.js +4 -3
  14. package/dist/animation/spring/spring_collider.js.map +1 -1
  15. package/dist/animation/spring/spring_node_pose_tracker.js +40 -0
  16. package/dist/animation/spring/spring_node_pose_tracker.js.map +1 -0
  17. package/dist/animation/spring/spring_particle.js +2 -0
  18. package/dist/animation/spring/spring_particle.js.map +1 -1
  19. package/dist/animation/spring/spring_system.js +11 -3
  20. package/dist/animation/spring/spring_system.js.map +1 -1
  21. package/dist/animation/spring_modifier.js +10 -0
  22. package/dist/animation/spring_modifier.js.map +1 -1
  23. package/dist/app/engine.js +53 -43
  24. package/dist/app/engine.js.map +1 -1
  25. package/dist/app/scriptingsystem.js +119 -6
  26. package/dist/app/scriptingsystem.js.map +1 -1
  27. package/dist/app/scriptregistry.js +3 -2
  28. package/dist/app/scriptregistry.js.map +1 -1
  29. package/dist/asset/assetmanager.js +284 -67
  30. package/dist/asset/assetmanager.js.map +1 -1
  31. package/dist/asset/loaders/zhair/zhair_format.js +288 -0
  32. package/dist/asset/loaders/zhair/zhair_format.js.map +1 -0
  33. package/dist/asset/loaders/zhair/zhair_loader.js +221 -0
  34. package/dist/asset/loaders/zhair/zhair_loader.js.map +1 -0
  35. package/dist/asset/model.js +178 -67
  36. package/dist/asset/model.js.map +1 -1
  37. package/dist/camera/camera.js +193 -21
  38. package/dist/camera/camera.js.map +1 -1
  39. package/dist/camera/perspectivecamera.js +3 -1
  40. package/dist/camera/perspectivecamera.js.map +1 -1
  41. package/dist/index.d.ts +4040 -1290
  42. package/dist/index.js +16 -6
  43. package/dist/index.js.map +1 -1
  44. package/dist/material/eye.js +932 -0
  45. package/dist/material/eye.js.map +1 -0
  46. package/dist/material/hair.js +917 -0
  47. package/dist/material/hair.js.map +1 -0
  48. package/dist/material/hairstrand.js +842 -0
  49. package/dist/material/hairstrand.js.map +1 -0
  50. package/dist/material/hairstrand_data.js +162 -0
  51. package/dist/material/hairstrand_data.js.map +1 -0
  52. package/dist/material/lambert.js +5 -2
  53. package/dist/material/lambert.js.map +1 -1
  54. package/dist/material/material.js +338 -300
  55. package/dist/material/material.js.map +1 -1
  56. package/dist/material/meshmaterial.js +120 -19
  57. package/dist/material/meshmaterial.js.map +1 -1
  58. package/dist/material/mixins/albedocolor.js +1 -1
  59. package/dist/material/mixins/albedocolor.js.map +1 -1
  60. package/dist/material/mixins/lightmodel/blinnphong.js +5 -3
  61. package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
  62. package/dist/material/mixins/lightmodel/lambert.js +5 -4
  63. package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
  64. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +3 -3
  65. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
  66. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +5 -5
  67. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  68. package/dist/material/mixins/lightmodel/pbrspecularglossness.js +3 -3
  69. package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
  70. package/dist/material/mixins/lit.js +73 -18
  71. package/dist/material/mixins/lit.js.map +1 -1
  72. package/dist/material/mixins/pbr/common.js +4 -4
  73. package/dist/material/mixins/pbr/common.js.map +1 -1
  74. package/dist/material/mixins/texture.js +48 -11
  75. package/dist/material/mixins/texture.js.map +1 -1
  76. package/dist/material/mtoon.js +6 -5
  77. package/dist/material/mtoon.js.map +1 -1
  78. package/dist/material/pbrblueprint.js +146 -28
  79. package/dist/material/pbrblueprint.js.map +1 -1
  80. package/dist/material/pbrblueprint_instance.js +501 -0
  81. package/dist/material/pbrblueprint_instance.js.map +1 -0
  82. package/dist/material/pbrmr.js +15 -16
  83. package/dist/material/pbrmr.js.map +1 -1
  84. package/dist/material/shader/helper.js +766 -456
  85. package/dist/material/shader/helper.js.map +1 -1
  86. package/dist/material/shader/terrain_blend.js +55 -0
  87. package/dist/material/shader/terrain_blend.js.map +1 -0
  88. package/dist/material/skin.js +192 -32
  89. package/dist/material/skin.js.map +1 -1
  90. package/dist/material/spriteblueprint.js +16 -1
  91. package/dist/material/spriteblueprint.js.map +1 -1
  92. package/dist/material/subsurfaceprofile.js +86 -23
  93. package/dist/material/subsurfaceprofile.js.map +1 -1
  94. package/dist/material/water.js +3 -1
  95. package/dist/material/water.js.map +1 -1
  96. package/dist/posteffect/bloom.js +167 -7
  97. package/dist/posteffect/bloom.js.map +1 -1
  98. package/dist/posteffect/coloradjust.js +2 -1
  99. package/dist/posteffect/coloradjust.js.map +1 -1
  100. package/dist/posteffect/compositor.js.map +1 -1
  101. package/dist/posteffect/fxaa.js +2 -1
  102. package/dist/posteffect/fxaa.js.map +1 -1
  103. package/dist/posteffect/grayscale.js +3 -1
  104. package/dist/posteffect/grayscale.js.map +1 -1
  105. package/dist/posteffect/motionblur.js +4 -1
  106. package/dist/posteffect/motionblur.js.map +1 -1
  107. package/dist/posteffect/posteffect.js.map +1 -1
  108. package/dist/posteffect/skinsss.js +368 -69
  109. package/dist/posteffect/skinsss.js.map +1 -1
  110. package/dist/posteffect/ssgi.js +16 -9
  111. package/dist/posteffect/ssgi.js.map +1 -1
  112. package/dist/posteffect/ssr.js +3 -0
  113. package/dist/posteffect/ssr.js.map +1 -1
  114. package/dist/posteffect/sss.js +91 -34
  115. package/dist/posteffect/sss.js.map +1 -1
  116. package/dist/posteffect/taa.js.map +1 -1
  117. package/dist/posteffect/tonemap.js +44 -1
  118. package/dist/posteffect/tonemap.js.map +1 -1
  119. package/dist/render/abuffer_oit.js +45 -39
  120. package/dist/render/abuffer_oit.js.map +1 -1
  121. package/dist/render/cluster_light.js +4 -4
  122. package/dist/render/cluster_light.js.map +1 -1
  123. package/dist/render/cull_visitor.js +19 -0
  124. package/dist/render/cull_visitor.js.map +1 -1
  125. package/dist/render/depthpass.js +36 -2
  126. package/dist/render/depthpass.js.map +1 -1
  127. package/dist/render/drawable_mixin.js +26 -1
  128. package/dist/render/drawable_mixin.js.map +1 -1
  129. package/dist/render/dualdepthpeeling_oit.js +52 -51
  130. package/dist/render/dualdepthpeeling_oit.js.map +1 -1
  131. package/dist/render/envlight.js +1 -1
  132. package/dist/render/envlight.js.map +1 -1
  133. package/dist/render/fbm_wavegenerator.js +3 -0
  134. package/dist/render/fbm_wavegenerator.js.map +1 -1
  135. package/dist/render/gerstner_wavegenerator.js +3 -0
  136. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  137. package/dist/render/globalbindgroup_allocator.js +3 -0
  138. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  139. package/dist/render/oit.js +29 -0
  140. package/dist/render/oit.js.map +1 -0
  141. package/dist/render/render_queue.js +3 -0
  142. package/dist/render/render_queue.js.map +1 -1
  143. package/dist/render/rendergraph/device_pool_allocator.js.map +1 -1
  144. package/dist/render/rendergraph/executor.js +2 -0
  145. package/dist/render/rendergraph/executor.js.map +1 -1
  146. package/dist/render/rendergraph/forward_plus_builder.js +61 -1
  147. package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
  148. package/dist/render/rendergraph/rendergraph.js.map +1 -1
  149. package/dist/render/rendergraph/texture_affinity_cache.js +1 -1
  150. package/dist/render/rendergraph/texture_affinity_cache.js.map +1 -1
  151. package/dist/render/rendergraph/types.js.map +1 -1
  152. package/dist/render/shadow_mask_pass.js +73 -8
  153. package/dist/render/shadow_mask_pass.js.map +1 -1
  154. package/dist/render/sky.js +166 -126
  155. package/dist/render/sky.js.map +1 -1
  156. package/dist/render/weightedblended_oit.js +36 -35
  157. package/dist/render/weightedblended_oit.js.map +1 -1
  158. package/dist/scene/basesprite.js +8 -0
  159. package/dist/scene/basesprite.js.map +1 -1
  160. package/dist/scene/batchgroup.js +3 -0
  161. package/dist/scene/batchgroup.js.map +1 -1
  162. package/dist/scene/environment.js +4 -1
  163. package/dist/scene/environment.js.map +1 -1
  164. package/dist/scene/hair.js +963 -0
  165. package/dist/scene/hair.js.map +1 -0
  166. package/dist/scene/light.js +5 -1
  167. package/dist/scene/light.js.map +1 -1
  168. package/dist/scene/mesh.js +481 -47
  169. package/dist/scene/mesh.js.map +1 -1
  170. package/dist/scene/meshdrawable.js +6 -0
  171. package/dist/scene/meshdrawable.js.map +1 -1
  172. package/dist/scene/msdftext.js +3 -0
  173. package/dist/scene/msdftext.js.map +1 -1
  174. package/dist/scene/msdftextsprite.js +3 -0
  175. package/dist/scene/msdftextsprite.js.map +1 -1
  176. package/dist/scene/particlesys.js +6 -0
  177. package/dist/scene/particlesys.js.map +1 -1
  178. package/dist/scene/scene.js +3 -0
  179. package/dist/scene/scene.js.map +1 -1
  180. package/dist/scene/scene_node.js +6 -1
  181. package/dist/scene/scene_node.js.map +1 -1
  182. package/dist/scene/terrain-cm/grass.js +273 -249
  183. package/dist/scene/terrain-cm/grass.js.map +1 -1
  184. package/dist/scene/terrain-cm/grassmaterial.js +3 -0
  185. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  186. package/dist/scene/terrain-cm/terrain-cm.js +6 -0
  187. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  188. package/dist/scene/terrain-cm/texture_cache.js +309 -0
  189. package/dist/scene/terrain-cm/texture_cache.js.map +1 -0
  190. package/dist/scene/water.js +6 -0
  191. package/dist/scene/water.js.map +1 -1
  192. package/dist/shaders/atmosphere.js.map +1 -1
  193. package/dist/shaders/diffusion.js +54 -0
  194. package/dist/shaders/diffusion.js.map +1 -0
  195. package/dist/shaders/shadow.js +33 -21
  196. package/dist/shaders/shadow.js.map +1 -1
  197. package/dist/shaders/temporal.js +71 -33
  198. package/dist/shaders/temporal.js.map +1 -1
  199. package/dist/shadow/dom.js +546 -0
  200. package/dist/shadow/dom.js.map +1 -0
  201. package/dist/shadow/pcf_opt.js +3 -5
  202. package/dist/shadow/pcf_opt.js.map +1 -1
  203. package/dist/shadow/pcf_pd.js +1 -1
  204. package/dist/shadow/pcf_pd.js.map +1 -1
  205. package/dist/shadow/receiver_bias.js +41 -0
  206. package/dist/shadow/receiver_bias.js.map +1 -0
  207. package/dist/shadow/shader.js +20 -60
  208. package/dist/shadow/shader.js.map +1 -1
  209. package/dist/shadow/shadow_impl.js +89 -0
  210. package/dist/shadow/shadow_impl.js.map +1 -1
  211. package/dist/shadow/shadow_region.js +50 -50
  212. package/dist/shadow/shadow_region.js.map +1 -1
  213. package/dist/shadow/shadowmapper.js +150 -37
  214. package/dist/shadow/shadowmapper.js.map +1 -1
  215. package/dist/shapes/shape.js +11 -2
  216. package/dist/shapes/shape.js.map +1 -1
  217. package/dist/shapes/sphere.js +50 -9
  218. package/dist/shapes/sphere.js.map +1 -1
  219. package/dist/utility/blueprint/common/constants.js +112 -2
  220. package/dist/utility/blueprint/common/constants.js.map +1 -1
  221. package/dist/utility/blueprint/material/func.js +142 -49
  222. package/dist/utility/blueprint/material/func.js.map +1 -1
  223. package/dist/utility/blueprint/material/ir.js +490 -25
  224. package/dist/utility/blueprint/material/ir.js.map +1 -1
  225. package/dist/utility/blueprint/material/texture.js +898 -4
  226. package/dist/utility/blueprint/material/texture.js.map +1 -1
  227. package/dist/utility/serialization/manager.js +431 -100
  228. package/dist/utility/serialization/manager.js.map +1 -1
  229. package/dist/utility/serialization/scene/animation.js +370 -3
  230. package/dist/utility/serialization/scene/animation.js.map +1 -1
  231. package/dist/utility/serialization/scene/batch.js +4 -0
  232. package/dist/utility/serialization/scene/batch.js.map +1 -1
  233. package/dist/utility/serialization/scene/camera.js +133 -1
  234. package/dist/utility/serialization/scene/camera.js.map +1 -1
  235. package/dist/utility/serialization/scene/common.js +26 -5
  236. package/dist/utility/serialization/scene/common.js.map +1 -1
  237. package/dist/utility/serialization/scene/hair.js +1141 -0
  238. package/dist/utility/serialization/scene/hair.js.map +1 -0
  239. package/dist/utility/serialization/scene/light.js +84 -4
  240. package/dist/utility/serialization/scene/light.js.map +1 -1
  241. package/dist/utility/serialization/scene/material.js +1867 -166
  242. package/dist/utility/serialization/scene/material.js.map +1 -1
  243. package/dist/utility/serialization/scene/mesh.js +231 -14
  244. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  245. package/dist/utility/serialization/scene/node.js +141 -7
  246. package/dist/utility/serialization/scene/node.js.map +1 -1
  247. package/dist/utility/serialization/scene/particle.js +4 -0
  248. package/dist/utility/serialization/scene/particle.js.map +1 -1
  249. package/dist/utility/serialization/scene/primitive.js +18 -0
  250. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  251. package/dist/utility/serialization/scene/sprite.js +4 -0
  252. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  253. package/dist/utility/serialization/scene/terrain.js +92 -69
  254. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  255. package/dist/utility/serialization/scene/text.js +4 -0
  256. package/dist/utility/serialization/scene/text.js.map +1 -1
  257. package/dist/utility/serialization/scene/water.js +4 -0
  258. package/dist/utility/serialization/scene/water.js.map +1 -1
  259. package/dist/utility/serialization/types.js.map +1 -1
  260. package/dist/utility/shprojector.js +96 -95
  261. package/dist/utility/shprojector.js.map +1 -1
  262. package/dist/values.js +105 -58
  263. package/dist/values.js.map +1 -1
  264. package/package.json +3 -3
@@ -5,8 +5,8 @@ import { ESM } from './esm.js';
5
5
  import { VSM } from './vsm.js';
6
6
  import { PCFOPT } from './pcf_opt.js';
7
7
  import { PCSS } from './pcss.js';
8
- import { LIGHT_TYPE_NONE, LIGHT_TYPE_DIRECTIONAL, LIGHT_TYPE_POINT } from '../values.js';
9
- import { ShaderHelper } from '../material/shader/helper.js';
8
+ import { DOM } from './dom.js';
9
+ import { LIGHT_TYPE_NONE, LIGHT_TYPE_POINT, LIGHT_TYPE_DIRECTIONAL } from '../values.js';
10
10
  import { getDevice } from '../app/api.js';
11
11
  import { ShadowRegion } from './shadow_region.js';
12
12
 
@@ -38,6 +38,9 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
38
38
  /** @internal */ _pcssFilterSampleCount;
39
39
  /** @internal */ _pcssMaxFilterRadius;
40
40
  /** @internal */ _pcssTemporalJitter;
41
+ /** @internal */ _domLayerDistance;
42
+ /** @internal */ _domDensity;
43
+ /** @internal */ _domFilterSize;
41
44
  /** @internal */ _vsmBlurKernelSize;
42
45
  /** @internal */ _vsmBlurRadius;
43
46
  /** @internal */ _vsmDarkness;
@@ -59,7 +62,7 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
59
62
  numCascades: 1,
60
63
  splitLambda: 0.5,
61
64
  depthBias: 0.003,
62
- normalBias: 0.2,
65
+ normalBias: 1.5,
63
66
  nearClip: 1
64
67
  };
65
68
  this._resourceDirty = true;
@@ -74,6 +77,9 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
74
77
  this._pcssFilterSampleCount = 32;
75
78
  this._pcssMaxFilterRadius = 32;
76
79
  this._pcssTemporalJitter = true;
80
+ this._domLayerDistance = 0.25;
81
+ this._domDensity = 1;
82
+ this._domFilterSize = 5;
77
83
  this._vsmBlurKernelSize = 5;
78
84
  this._vsmBlurRadius = 4;
79
85
  this._vsmDarkness = 0.3;
@@ -174,7 +180,20 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
174
180
  set depthBias(val) {
175
181
  this._config.depthBias = val;
176
182
  }
177
- /** Normal bias for the shadow map */ get normalBias() {
183
+ /**
184
+ * Normal offset bias, in shadow map texels.
185
+ *
186
+ * @remarks
187
+ * Before sampling, the receiver is moved along its normal by this many texel
188
+ * widths, scaled by `sin(theta)` between the normal and the light so the
189
+ * offset is zero facing the light and largest at grazing incidence. This is
190
+ * what removes grazing-angle self-shadowing acne; {@link depthBias} alone
191
+ * cannot, because the depth error over one texel grows without bound as the
192
+ * surface turns away from the light.
193
+ *
194
+ * Useful values are roughly 1 to 4. Too little leaves acne on curved surfaces
195
+ * near the terminator; too much detaches contact shadows from thin geometry.
196
+ */ get normalBias() {
178
197
  return this._config.normalBias;
179
198
  }
180
199
  set normalBias(val) {
@@ -215,7 +234,7 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
215
234
  this.shadowDistance = 120;
216
235
  this.splitLambda = 0.75;
217
236
  this.depthBias = 0.005;
218
- this.normalBias = 0.3;
237
+ this.normalBias = 2;
219
238
  this.nearClip = 0.2;
220
239
  this.pcfKernelSize = 5;
221
240
  this.numShadowCascades = this._light.isDirectionLight() ? 3 : 1;
@@ -226,7 +245,7 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
226
245
  this.shadowDistance = 800;
227
246
  this.splitLambda = 0.6;
228
247
  this.depthBias = 0.0035;
229
- this.normalBias = 0.22;
248
+ this.normalBias = 1.5;
230
249
  this.nearClip = 1;
231
250
  this.pcfKernelSize = 3;
232
251
  this.numShadowCascades = this._light.isDirectionLight() ? 4 : 1;
@@ -283,6 +302,69 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
283
302
  }
284
303
  }
285
304
  }
305
+ /**
306
+ * Depth spanned by the deep opacity map's layers, in world units along the
307
+ * light direction. Only meaningful when the shadow mode is `dom`.
308
+ *
309
+ * @remarks
310
+ * Set it to roughly the thickness of the hair. Too small and everything past
311
+ * the outer strands saturates the last layer, which is a hard shadow again;
312
+ * too large and all three layers fall inside the first strand, leaving the
313
+ * interior unshadowed.
314
+ */ get domLayerDistance() {
315
+ return this._domLayerDistance;
316
+ }
317
+ set domLayerDistance(val) {
318
+ val = Math.max(0.0001, Number(val) || 0);
319
+ if (val !== this._domLayerDistance) {
320
+ this._domLayerDistance = val;
321
+ const dom = this.asDOM();
322
+ if (dom) {
323
+ dom.layerDistance = this._domLayerDistance;
324
+ }
325
+ }
326
+ }
327
+ /**
328
+ * How strongly accumulated hair coverage attenuates light. Only meaningful when
329
+ * the shadow mode is `dom`.
330
+ *
331
+ * @remarks
332
+ * Raise it to darken the interior of a groom without moving the layers.
333
+ */ get domDensity() {
334
+ return this._domDensity;
335
+ }
336
+ set domDensity(val) {
337
+ val = Math.max(0, Number(val) || 0);
338
+ if (val !== this._domDensity) {
339
+ this._domDensity = val;
340
+ const dom = this.asDOM();
341
+ if (dom) {
342
+ dom.density = this._domDensity;
343
+ }
344
+ }
345
+ }
346
+ /**
347
+ * Width of the deep opacity map's sampling kernel in texels: 1, 3, 5 or 7. Only
348
+ * meaningful when the shadow mode is `dom`.
349
+ *
350
+ * @remarks
351
+ * The technique's own gradation runs along the light rather than across the
352
+ * map, so without a kernel the silhouette of a groom is as hard an edge as an
353
+ * unfiltered shadow map gives. This is what softens it, at one layer lookup per
354
+ * tap. Defaults to 5.
355
+ */ get domFilterSize() {
356
+ return this._domFilterSize;
357
+ }
358
+ set domFilterSize(val) {
359
+ val = val !== 1 && val !== 3 && val !== 5 && val !== 7 ? 5 : val;
360
+ if (val !== this._domFilterSize) {
361
+ this._domFilterSize = val;
362
+ const dom = this.asDOM();
363
+ if (dom) {
364
+ dom.filterSize = this._domFilterSize;
365
+ }
366
+ }
367
+ }
286
368
  /** Blocker search sample count for PCSS shadow */ get pcssBlockerSampleCount() {
287
369
  return this._pcssBlockerSampleCount;
288
370
  }
@@ -456,25 +538,6 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
456
538
  ctx.shadowMapInfo = null;
457
539
  }
458
540
  }
459
- /** @internal */ static computeShadowBias(shadowMapParams, scope, z, NdotL, linear) {
460
- const pb = scope.$builder;
461
- const depthBiasParam = ShaderHelper.getDepthBiasValues(scope);
462
- if (shadowMapParams.lightType === LIGHT_TYPE_DIRECTIONAL) {
463
- return pb.dot(pb.mul(depthBiasParam.xy, pb.vec2(1, pb.sub(1, NdotL))), pb.vec2(1, 1));
464
- } else {
465
- const nearFar = ShaderHelper.getShadowCameraParams(scope).xy;
466
- const linearDepth = linear ? z : ShaderHelper.nonLinearDepthToLinearNormalized(scope, z, nearFar);
467
- const biasScaleFactor = pb.mix(1, depthBiasParam.w, linearDepth);
468
- return pb.dot(pb.mul(depthBiasParam.xy, pb.vec2(1, pb.sub(1, NdotL)), biasScaleFactor), pb.vec2(1, 1));
469
- }
470
- }
471
- /** @internal */ static computeShadowBiasCSM(shadowMapParams, scope, NdotL, split) {
472
- const pb = scope.$builder;
473
- const depthBiasParam = ShaderHelper.getDepthBiasValues(scope);
474
- const splitFlags = pb.vec4(pb.float(pb.equal(split, 0)), pb.float(pb.equal(split, 1)), pb.float(pb.equal(split, 2)), pb.float(pb.equal(split, 3)));
475
- const depthBiasScale = pb.dot(ShaderHelper.getDepthBiasScales(scope), splitFlags);
476
- return pb.dot(pb.mul(depthBiasParam.xy, pb.vec2(1, pb.sub(1, NdotL)), depthBiasScale), pb.vec2(1, 1));
477
- }
478
541
  /** @internal */ isTextureInvalid(texture, target, format, width, height) {
479
542
  return texture && (texture.target !== target || texture.format !== format || texture.width !== width || texture.height !== height || texture.depth !== this.numShadowCascades);
480
543
  }
@@ -766,8 +829,15 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
766
829
  /** @internal */ calcDepthBiasParams(camera, shadowMapSize, depthBias, normalBias, depthScale, result) {
767
830
  const sizeNear = Math.min(camera.getProjectionMatrix().getNearPlaneWidth(), camera.getProjectionMatrix().getNearPlaneHeight());
768
831
  const sizeFar = Math.min(camera.getProjectionMatrix().getFarPlaneWidth(), camera.getProjectionMatrix().getFarPlaneHeight());
769
- const scaleFactor = sizeNear / shadowMapSize * 2;
770
- result.setXYZW(depthBias * scaleFactor, normalBias * 0.0001, depthScale, sizeFar / sizeNear);
832
+ // World-space size of one shadow map texel at the near plane. Both bias
833
+ // terms are expressed in multiples of it, so a tighter cascade automatically
834
+ // gets a proportionally smaller offset.
835
+ const texelWorldSize = sizeNear / shadowMapSize;
836
+ result.setXYZW(depthBias * texelWorldSize * 2, // Normal offset distance in world units. Unlike a depth bias this moves
837
+ // the receiver sideways out of the texel that produces grazing-angle
838
+ // acne, which no amount of depth bias can fix (the required depth offset
839
+ // diverges as NdotL approaches zero).
840
+ normalBias * texelWorldSize, depthScale, sizeFar / sizeNear);
771
841
  }
772
842
  /** @internal */ postRenderShadowMap(shadowMapParams) {
773
843
  this._impl.postRenderShadowMap(shadowMapParams);
@@ -779,7 +849,7 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
779
849
  const shadowMapParams = ShadowMapper.fetchShadowMapParams();
780
850
  shadowMapParams.impl = this._impl;
781
851
  shadowMapParams.lightType = this.light.lightType;
782
- shadowMapParams.numShadowCascades = shadowMapParams.lightType === LIGHT_TYPE_DIRECTIONAL ? this._config.numCascades ?? 1 : 1;
852
+ shadowMapParams.numShadowCascades = shadowMapParams.lightType === LIGHT_TYPE_DIRECTIONAL && this._impl.supportsCascades() ? this._config.numCascades ?? 1 : 1;
783
853
  ctx.shadowMapInfo.set(this.light, shadowMapParams);
784
854
  const scene = ctx.scene;
785
855
  const camera = ctx.camera;
@@ -791,6 +861,32 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
791
861
  shadowMapParams.depthClampEnabled = false;
792
862
  renderPass.clearColor = this._impl.getShadowMapClearColor(shadowMapParams);
793
863
  const depthScale = this._impl.getDepthScale();
864
+ const geometryPassCount = Math.max(1, this._impl.getGeometryPassCount(shadowMapParams) >> 0);
865
+ // Repeats the caster draw for implementations that need more than one look at
866
+ // the geometry (deep opacity maps). Single-pass implementations - every one
867
+ // but DOM - take the `pass === 0` path once and behave exactly as before.
868
+ const renderGeometry = (renderCamera, cullCamera)=>{
869
+ if (geometryPassCount === 1) {
870
+ renderPass.render(ctx, renderCamera, cullCamera);
871
+ return;
872
+ }
873
+ // Culling depends only on the camera, so the extra passes reuse the first
874
+ // pass's queue rather than walking the scene graph again per pass.
875
+ const renderQueue = renderPass.cullScene(ctx, cullCamera ?? renderCamera);
876
+ try {
877
+ for(let pass = 0; pass < geometryPassCount; pass++){
878
+ this._impl.beginGeometryPass(shadowMapParams, pass);
879
+ // The emitted caster shader differs between passes, and the shadow pass
880
+ // keys its programs on this hash, so it has to be refreshed per pass
881
+ // rather than computed once up front.
882
+ shadowMapParams.shaderHash = this.getShaderHash(shadowMapParams);
883
+ renderPass.clearColor = this._impl.getShadowMapClearColor(shadowMapParams);
884
+ renderPass.render(ctx, renderCamera, cullCamera, renderQueue);
885
+ }
886
+ } finally{
887
+ renderQueue.dispose();
888
+ }
889
+ };
794
890
  const directionalShadowRegion = this._shadowRegion.region;
795
891
  const shadowRegion = this._light.isDirectionLight() && directionalShadowRegion?.isValid() ? directionalShadowRegion : scene.boundingBox;
796
892
  if (this._light.isPointLight()) {
@@ -811,20 +907,24 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
811
907
  shadowMapRenderCamera.lookAtCubeFace(face);
812
908
  fb.setColorAttachmentCubeFace(0, face);
813
909
  fb.setDepthAttachmentCubeFace(face);
814
- renderPass.render(ctx, shadowMapRenderCamera);
910
+ renderGeometry(shadowMapRenderCamera);
815
911
  }
816
912
  shadowMapParams.shadowMatrices.set(Matrix4x4.identity());
817
913
  ShadowMapper.releaseCamera(shadowMapRenderCamera);
818
914
  } else {
819
- if (this._config.numCascades > 1) {
820
- const distances = this.calcSplitDistances(camera.getNearPlane(), Math.min(this._shadowDistance, camera.getFarPlane()), this._config.numCascades);
915
+ // Reads the resolved count, not the configured one: an implementation that
916
+ // cannot store cascades has already collapsed it to a single split, and the
917
+ // cascade path would index array layers its shadow map does not have.
918
+ const numCascades = shadowMapParams.numShadowCascades;
919
+ if (numCascades > 1) {
920
+ const distances = this.calcSplitDistances(camera.getNearPlane(), Math.min(this._shadowDistance, camera.getFarPlane()), numCascades);
821
921
  const cascadeCamera = ShadowMapper.fetchCameraForScene(scene);
822
922
  const shadowMapRenderCamera = ShadowMapper.fetchCameraForScene(scene);
823
923
  const shadowMapCullCamera = ShadowMapper.fetchCameraForScene(scene);
824
924
  shadowMapCullCamera.clipMask = AABB.ClipLeft | AABB.ClipRight | AABB.ClipBottom | AABB.ClipTop;
825
925
  cascadeCamera.parent = camera;
826
926
  shadowMapParams.depthClampEnabled = getDevice().getDeviceCaps().shaderCaps.supportFragmentDepth;
827
- for(let split = 0; split < this._config.numCascades; split++){
927
+ for(let split = 0; split < numCascades; split++){
828
928
  cascadeCamera.setPerspective(camera.getFOV(), camera.getAspect(), distances[split], distances[split + 1]);
829
929
  const snapMatrix = ShadowMapper._snapMatrix;
830
930
  const border = shadowMapParams.impl.getShadowMapBorder(shadowMapParams); //20 / this._config.shadowMapSize;
@@ -839,8 +939,8 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
839
939
  fb.setColorAttachmentLayer(0, split);
840
940
  fb.setDepthAttachmentLayer(split);
841
941
  } else {
842
- const numRows = this._config.numCascades > 2 ? 2 : 1;
843
- const numCols = this._config.numCascades > 1 ? 2 : 1;
942
+ const numRows = numCascades > 2 ? 2 : 1;
943
+ const numCols = numCascades > 1 ? 2 : 1;
844
944
  const adjMatrix = new Matrix4x4();
845
945
  const col = split % 2;
846
946
  const row = split >> 1;
@@ -867,7 +967,7 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
867
967
  }
868
968
  device.setFramebuffer(fb);
869
969
  device.setScissor(scissor);
870
- renderPass.render(ctx, shadowMapRenderCamera, shadowMapCullCamera);
970
+ renderGeometry(shadowMapRenderCamera, shadowMapCullCamera);
871
971
  shadowMapParams.shadowMatrices.set(Matrix4x4.transpose(shadowMapRenderCamera.viewProjectionMatrix), split * 16);
872
972
  shadowMapParams.cascadeDistances[split] = distances[split + 1];
873
973
  }
@@ -890,7 +990,7 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
890
990
  shadowMapParams.cameraParams.setXYZW(shadowMapRenderCamera.getNearPlane(), shadowMapRenderCamera.getFarPlane(), this._config.shadowMapSize, this._shadowDistance);
891
991
  device.setFramebuffer(fb);
892
992
  shadowMapRenderCamera.setProjectionMatrix(Matrix4x4.multiply(snapMatrix, shadowMapRenderCamera.getProjectionMatrix()));
893
- renderPass.render(ctx, shadowMapRenderCamera);
993
+ renderGeometry(shadowMapRenderCamera);
894
994
  shadowMapParams.shadowMatrices.set(Matrix4x4.transpose(shadowMapRenderCamera.viewProjectionMatrix));
895
995
  ShadowMapper.releaseCamera(shadowMapRenderCamera);
896
996
  }
@@ -898,10 +998,18 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
898
998
  this.postRenderShadowMap(shadowMapParams);
899
999
  }
900
1000
  /** @internal */ applyMode(mode) {
901
- if (mode !== 'hard' && mode !== 'vsm' && mode !== 'esm' && mode !== 'pcf' && mode !== 'pcf-pd' && mode !== 'pcf-opt' && mode !== 'pcss') {
1001
+ if (mode !== 'hard' && mode !== 'vsm' && mode !== 'esm' && mode !== 'pcf' && mode !== 'pcf-pd' && mode !== 'pcf-opt' && mode !== 'pcss' && mode !== 'dom') {
902
1002
  console.error(`ShadowMapper.setShadowMode() failed: invalid mode: ${mode}`);
903
1003
  return;
904
1004
  }
1005
+ if (mode === 'dom' && getDevice().type !== 'webgpu') {
1006
+ // The strand geometry this mode exists to shadow is WebGPU only, so the
1007
+ // WebGL paths were never built. Falling back keeps a scene authored with it
1008
+ // renderable rather than failing to produce a shadow map at all.
1009
+ console.warn(`ShadowMapper.setShadowMode(): 'dom' requires WebGPU, falling back to 'pcf'`);
1010
+ this.applyMode('pcf');
1011
+ return;
1012
+ }
905
1013
  this._impl = null;
906
1014
  if (mode === 'hard') {
907
1015
  this._impl = new SSM();
@@ -914,6 +1022,8 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
914
1022
  esm.logSpace = this._esmLogSpace;
915
1023
  } else if (mode === 'pcf' || mode === 'pcf-opt' || mode === 'pcf-pd') {
916
1024
  this._impl = new PCFOPT(this._pcfKernelSize);
1025
+ } else if (mode === 'dom') {
1026
+ this._impl = new DOM(this._domLayerDistance, this._domDensity, this._domFilterSize);
917
1027
  } else if (mode === 'pcss') {
918
1028
  this._impl = new PCSS(this._pcssLightRadius, this._pcssBlockerSampleCount, this._pcssFilterSampleCount, this._pcssMaxFilterRadius, this._pcssTemporalJitter);
919
1029
  }
@@ -921,6 +1031,9 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
921
1031
  /** @internal */ asVSM() {
922
1032
  return this._impl?.getType() === 'vsm' ? this._impl : null;
923
1033
  }
1034
+ /** @internal */ asDOM() {
1035
+ return this._impl?.getType() === 'dom' ? this._impl : null;
1036
+ }
924
1037
  /** @internal */ asESM() {
925
1038
  return this._impl?.getType() === 'esm' ? this._impl : null;
926
1039
  }