@zephyr3d/scene 0.9.27 → 0.10.0

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 (502) hide show
  1. package/README.md +60 -27
  2. package/dist/animation/animation_bank.js +634 -0
  3. package/dist/animation/animation_bank.js.map +1 -0
  4. package/dist/animation/animation_bank_gpu.js +256 -0
  5. package/dist/animation/animation_bank_gpu.js.map +1 -0
  6. package/dist/animation/animationset.js +54 -0
  7. package/dist/animation/animationset.js.map +1 -1
  8. package/dist/animation/cloth/gpu_cloth_component.js +406 -0
  9. package/dist/animation/cloth/gpu_cloth_component.js.map +1 -0
  10. package/dist/animation/cloth/gpu_cloth_system.js +332 -202
  11. package/dist/animation/cloth/gpu_cloth_system.js.map +1 -1
  12. package/dist/animation/gpu_animation_driver.js +289 -0
  13. package/dist/animation/gpu_animation_driver.js.map +1 -0
  14. package/dist/animation/joint_dynamics/collision.js +112 -1
  15. package/dist/animation/joint_dynamics/collision.js.map +1 -1
  16. package/dist/animation/joint_dynamics/controller.js +6 -0
  17. package/dist/animation/joint_dynamics/controller.js.map +1 -1
  18. package/dist/animation/joint_dynamics/joint_dynamics_system.js +11 -0
  19. package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
  20. package/dist/animation/joint_dynamics/solver.js +50 -12
  21. package/dist/animation/joint_dynamics/solver.js.map +1 -1
  22. package/dist/animation/joint_dynamics/types.js.map +1 -1
  23. package/dist/animation/skeleton.js +279 -55
  24. package/dist/animation/skeleton.js.map +1 -1
  25. package/dist/animation/skin_palette_atlas.js +252 -0
  26. package/dist/animation/skin_palette_atlas.js.map +1 -0
  27. package/dist/animation/spring/multi_chain_spring_system.js +298 -38
  28. package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
  29. package/dist/animation/spring/spring_chain.js +2 -0
  30. package/dist/animation/spring/spring_chain.js.map +1 -1
  31. package/dist/animation/spring/spring_collider.js +159 -21
  32. package/dist/animation/spring/spring_collider.js.map +1 -1
  33. package/dist/animation/spring/spring_particle.js +1 -0
  34. package/dist/animation/spring/spring_particle.js.map +1 -1
  35. package/dist/animation/spring/spring_solver.js +173 -0
  36. package/dist/animation/spring/spring_solver.js.map +1 -0
  37. package/dist/animation/spring/spring_system.js +249 -24
  38. package/dist/animation/spring/spring_system.js.map +1 -1
  39. package/dist/animation/spring_modifier.js +32 -1
  40. package/dist/animation/spring_modifier.js.map +1 -1
  41. package/dist/app/app.js +26 -0
  42. package/dist/app/app.js.map +1 -1
  43. package/dist/app/engine.js +86 -15
  44. package/dist/app/engine.js.map +1 -1
  45. package/dist/app/gamepad.js +191 -0
  46. package/dist/app/gamepad.js.map +1 -0
  47. package/dist/app/inputactions.js +271 -0
  48. package/dist/app/inputactions.js.map +1 -0
  49. package/dist/app/inputmgr.js +331 -22
  50. package/dist/app/inputmgr.js.map +1 -1
  51. package/dist/app/loading.js +138 -0
  52. package/dist/app/loading.js.map +1 -0
  53. package/dist/app/pipelinecache.js +209 -0
  54. package/dist/app/pipelinecache.js.map +1 -0
  55. package/dist/app/runtimescript.js +25 -0
  56. package/dist/app/runtimescript.js.map +1 -1
  57. package/dist/app/scriptingsystem.js +54 -0
  58. package/dist/app/scriptingsystem.js.map +1 -1
  59. package/dist/app/scriptregistry.js +7 -2
  60. package/dist/app/scriptregistry.js.map +1 -1
  61. package/dist/asset/asset_manifest.js +59 -0
  62. package/dist/asset/asset_manifest.js.map +1 -0
  63. package/dist/asset/asset_preload.js +258 -0
  64. package/dist/asset/asset_preload.js.map +1 -0
  65. package/dist/asset/assetmanager.js +117 -65
  66. package/dist/asset/assetmanager.js.map +1 -1
  67. package/dist/asset/loaders/ktx2/basis/LICENSE +201 -0
  68. package/dist/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
  69. package/dist/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
  70. package/dist/asset/loaders/ktx2/basis_location.js +17 -0
  71. package/dist/asset/loaders/ktx2/basis_location.js.map +1 -0
  72. package/dist/asset/loaders/ktx2/ktx2_loader.js +88 -0
  73. package/dist/asset/loaders/ktx2/ktx2_loader.js.map +1 -0
  74. package/dist/asset/loaders/ktx2/ktx2_targets.js +118 -0
  75. package/dist/asset/loaders/ktx2/ktx2_targets.js.map +1 -0
  76. package/dist/asset/loaders/ktx2/transcoder_pool.js +259 -0
  77. package/dist/asset/loaders/ktx2/transcoder_pool.js.map +1 -0
  78. package/dist/asset/mesh_lod_builder.js +104 -0
  79. package/dist/asset/mesh_lod_builder.js.map +1 -0
  80. package/dist/asset/mesh_settings.js +111 -0
  81. package/dist/asset/mesh_settings.js.map +1 -0
  82. package/dist/asset/meshopt_decoder.js +18 -0
  83. package/dist/asset/meshopt_decoder.js.map +1 -0
  84. package/dist/asset/meshopt_simplifier.js +26 -0
  85. package/dist/asset/meshopt_simplifier.js.map +1 -0
  86. package/dist/asset/model.js +171 -1
  87. package/dist/asset/model.js.map +1 -1
  88. package/dist/asset/texture_settings.js +231 -0
  89. package/dist/asset/texture_settings.js.map +1 -0
  90. package/dist/asset/zmsh_binary.js +131 -0
  91. package/dist/asset/zmsh_binary.js.map +1 -0
  92. package/dist/asset/zsdf_binary.js +111 -0
  93. package/dist/asset/zsdf_binary.js.map +1 -0
  94. package/dist/blitter/bilateralblur.js +1 -2
  95. package/dist/blitter/bilateralblur.js.map +1 -1
  96. package/dist/blitter/gaussianblur.js +1 -6
  97. package/dist/blitter/gaussianblur.js.map +1 -1
  98. package/dist/camera/base.js +31 -10
  99. package/dist/camera/base.js.map +1 -1
  100. package/dist/camera/camera.js +104 -178
  101. package/dist/camera/camera.js.map +1 -1
  102. package/dist/camera/follow.js +342 -0
  103. package/dist/camera/follow.js.map +1 -0
  104. package/dist/camera/fps.js +9 -7
  105. package/dist/camera/fps.js.map +1 -1
  106. package/dist/camera/orbit.js +46 -19
  107. package/dist/camera/orbit.js.map +1 -1
  108. package/dist/index.d.ts +25436 -15990
  109. package/dist/index.js +65 -11
  110. package/dist/index.js.map +1 -1
  111. package/dist/material/blinn.js +3 -0
  112. package/dist/material/blinn.js.map +1 -1
  113. package/dist/material/eye.js +1 -1
  114. package/dist/material/eye.js.map +1 -1
  115. package/dist/material/hair.js +2 -3
  116. package/dist/material/hair.js.map +1 -1
  117. package/dist/material/lambert.js +4 -1
  118. package/dist/material/lambert.js.map +1 -1
  119. package/dist/material/material.js +66 -4
  120. package/dist/material/material.js.map +1 -1
  121. package/dist/material/meshmaterial.js +118 -45
  122. package/dist/material/meshmaterial.js.map +1 -1
  123. package/dist/material/mixins/lightmodel/blinnphong.js +1 -1
  124. package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
  125. package/dist/material/mixins/lightmodel/lambert.js +1 -1
  126. package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
  127. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +34 -61
  128. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
  129. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +16 -2
  130. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  131. package/dist/material/mixins/lightmodel/pbrspecularglossness.js +2 -1
  132. package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
  133. package/dist/material/mixins/lit.js +191 -83
  134. package/dist/material/mixins/lit.js.map +1 -1
  135. package/dist/material/mixins/pbr/common.js +31 -206
  136. package/dist/material/mixins/pbr/common.js.map +1 -1
  137. package/dist/material/mtoon.js +1 -1
  138. package/dist/material/mtoon.js.map +1 -1
  139. package/dist/material/pbrmr.js +3 -0
  140. package/dist/material/pbrmr.js.map +1 -1
  141. package/dist/material/pbrsg.js +3 -0
  142. package/dist/material/pbrsg.js.map +1 -1
  143. package/dist/material/sdf_debug.js +78 -0
  144. package/dist/material/sdf_debug.js.map +1 -0
  145. package/dist/material/shader/helper.js +333 -191
  146. package/dist/material/shader/helper.js.map +1 -1
  147. package/dist/material/shader/terrain_blend.js +204 -43
  148. package/dist/material/shader/terrain_blend.js.map +1 -1
  149. package/dist/material/skin.js +331 -297
  150. package/dist/material/skin.js.map +1 -1
  151. package/dist/material/skinprofile.js +1058 -0
  152. package/dist/material/skinprofile.js.map +1 -0
  153. package/dist/material/sss.js +353 -0
  154. package/dist/material/sss.js.map +1 -0
  155. package/dist/material/sss_kernel.js +265 -0
  156. package/dist/material/sss_kernel.js.map +1 -0
  157. package/dist/material/sssprofile.js +879 -0
  158. package/dist/material/sssprofile.js.map +1 -0
  159. package/dist/material/subsurfaceprofile.js +4 -8
  160. package/dist/material/subsurfaceprofile.js.map +1 -1
  161. package/dist/material/terrain-cm.js +464 -188
  162. package/dist/material/terrain-cm.js.map +1 -1
  163. package/dist/material/unlit.js +3 -0
  164. package/dist/material/unlit.js.map +1 -1
  165. package/dist/material/water.js +1261 -1438
  166. package/dist/material/water.js.map +1 -1
  167. package/dist/node_modules/meshoptimizer/LICENSE +21 -0
  168. package/dist/node_modules/meshoptimizer/meshopt_decoder.js +197 -0
  169. package/dist/node_modules/meshoptimizer/meshopt_decoder.js.map +1 -0
  170. package/dist/node_modules/meshoptimizer/meshopt_simplifier.js +677 -0
  171. package/dist/node_modules/meshoptimizer/meshopt_simplifier.js.map +1 -0
  172. package/dist/physics/character.js +226 -0
  173. package/dist/physics/character.js.map +1 -0
  174. package/dist/physics/collider.js +209 -0
  175. package/dist/physics/collider.js.map +1 -0
  176. package/dist/physics/events.js +79 -0
  177. package/dist/physics/events.js.map +1 -0
  178. package/dist/physics/joint.js +272 -0
  179. package/dist/physics/joint.js.map +1 -0
  180. package/dist/physics/node_physics.js +234 -0
  181. package/dist/physics/node_physics.js.map +1 -0
  182. package/dist/physics/part.js +91 -0
  183. package/dist/physics/part.js.map +1 -0
  184. package/dist/physics/rigid_body.js +379 -0
  185. package/dist/physics/rigid_body.js.map +1 -0
  186. package/dist/physics/vehicle.js +335 -0
  187. package/dist/physics/vehicle.js.map +1 -0
  188. package/dist/physics/world.js +25 -0
  189. package/dist/physics/world.js.map +1 -0
  190. package/dist/posteffect/bloom.js +49 -14
  191. package/dist/posteffect/bloom.js.map +1 -1
  192. package/dist/posteffect/motionblur.js +106 -106
  193. package/dist/posteffect/motionblur.js.map +1 -1
  194. package/dist/posteffect/postsss.js +572 -0
  195. package/dist/posteffect/postsss.js.map +1 -0
  196. package/dist/posteffect/sao.js +2 -6
  197. package/dist/posteffect/sao.js.map +1 -1
  198. package/dist/posteffect/skinsss.js +605 -355
  199. package/dist/posteffect/skinsss.js.map +1 -1
  200. package/dist/posteffect/ssgi.js +91 -162
  201. package/dist/posteffect/ssgi.js.map +1 -1
  202. package/dist/posteffect/ssr.js +3 -11
  203. package/dist/posteffect/ssr.js.map +1 -1
  204. package/dist/posteffect/sss.js +4 -13
  205. package/dist/posteffect/sss.js.map +1 -1
  206. package/dist/posteffect/taa.js +1 -0
  207. package/dist/posteffect/taa.js.map +1 -1
  208. package/dist/render/abuffer_oit.js +3 -2
  209. package/dist/render/abuffer_oit.js.map +1 -1
  210. package/dist/render/clipmap.js +179 -64
  211. package/dist/render/clipmap.js.map +1 -1
  212. package/dist/render/cluster_light.js +958 -224
  213. package/dist/render/cluster_light.js.map +1 -1
  214. package/dist/render/cull_visitor.js +25 -5
  215. package/dist/render/cull_visitor.js.map +1 -1
  216. package/dist/render/debug_views.js +37 -0
  217. package/dist/render/debug_views.js.map +1 -0
  218. package/dist/render/depthpass.js +5 -1
  219. package/dist/render/depthpass.js.map +1 -1
  220. package/dist/render/drawable_mixin.js +53 -6
  221. package/dist/render/drawable_mixin.js.map +1 -1
  222. package/dist/render/dualdepthpeeling_oit.js +5 -5
  223. package/dist/render/dualdepthpeeling_oit.js.map +1 -1
  224. package/dist/render/envlight.js +196 -113
  225. package/dist/render/envlight.js.map +1 -1
  226. package/dist/render/fbm_wavegenerator.js +5 -10
  227. package/dist/render/fbm_wavegenerator.js.map +1 -1
  228. package/dist/render/fft_wavegenerator.js +247 -157
  229. package/dist/render/fft_wavegenerator.js.map +1 -1
  230. package/dist/render/gerstner_wavegenerator.js +16 -38
  231. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  232. package/dist/render/gi/probe_volume.js +1334 -0
  233. package/dist/render/gi/probe_volume.js.map +1 -0
  234. package/dist/render/gi/scene_gi.js +282 -0
  235. package/dist/render/gi/scene_gi.js.map +1 -0
  236. package/dist/render/gi/voxel_atlas.js +198 -0
  237. package/dist/render/gi/voxel_atlas.js.map +1 -0
  238. package/dist/render/gi/voxel_lighting.js +652 -0
  239. package/dist/render/gi/voxel_lighting.js.map +1 -0
  240. package/dist/render/gi/voxel_scene.js +642 -0
  241. package/dist/render/gi/voxel_scene.js.map +1 -0
  242. package/dist/render/gi/voxelize_pass.js +223 -0
  243. package/dist/render/gi/voxelize_pass.js.map +1 -0
  244. package/dist/render/globalbindgroup_allocator.js +3 -2
  245. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  246. package/dist/render/gpu_picking.js +9 -36
  247. package/dist/render/gpu_picking.js.map +1 -1
  248. package/dist/render/instance_culling.js +499 -0
  249. package/dist/render/instance_culling.js.map +1 -0
  250. package/dist/render/lightpass.js +19 -8
  251. package/dist/render/lightpass.js.map +1 -1
  252. package/dist/render/lod.js +46 -0
  253. package/dist/render/lod.js.map +1 -0
  254. package/dist/render/primitive.js +301 -9
  255. package/dist/render/primitive.js.map +1 -1
  256. package/dist/render/primitive_readback.js +209 -0
  257. package/dist/render/primitive_readback.js.map +1 -0
  258. package/dist/render/render_queue.js +162 -11
  259. package/dist/render/render_queue.js.map +1 -1
  260. package/dist/render/renderer.js +10 -7
  261. package/dist/render/renderer.js.map +1 -1
  262. package/dist/render/rendergraph/blackboard.js +14 -1
  263. package/dist/render/rendergraph/blackboard.js.map +1 -1
  264. package/dist/render/rendergraph/executor.js +136 -0
  265. package/dist/render/rendergraph/executor.js.map +1 -1
  266. package/dist/render/rendergraph/forward_plus_builder.js +272 -55
  267. package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
  268. package/dist/render/rendergraph/frame_graph_context.js +41 -0
  269. package/dist/render/rendergraph/frame_graph_context.js.map +1 -0
  270. package/dist/render/rendergraph/scene_render_context.js +31 -2
  271. package/dist/render/rendergraph/scene_render_context.js.map +1 -1
  272. package/dist/render/rendergraph/types.js.map +1 -1
  273. package/dist/render/renderpass.js +28 -5
  274. package/dist/render/renderpass.js.map +1 -1
  275. package/dist/render/sdf/box_allocator.js +98 -0
  276. package/dist/render/sdf/box_allocator.js.map +1 -0
  277. package/dist/render/sdf/global_sdf.js +865 -0
  278. package/dist/render/sdf/global_sdf.js.map +1 -0
  279. package/dist/render/sdf/scene_sdf.js +562 -0
  280. package/dist/render/sdf/scene_sdf.js.map +1 -0
  281. package/dist/render/sdf/sdf_atlas.js +227 -0
  282. package/dist/render/sdf/sdf_atlas.js.map +1 -0
  283. package/dist/render/sdf/sdf_programs.js +42 -0
  284. package/dist/render/sdf/sdf_programs.js.map +1 -0
  285. package/dist/render/sdf/sdf_view.js +199 -0
  286. package/dist/render/sdf/sdf_view.js.map +1 -0
  287. package/dist/render/sdf/waterline.js +610 -0
  288. package/dist/render/sdf/waterline.js.map +1 -0
  289. package/dist/render/shadow_mask_pass.js +142 -25
  290. package/dist/render/shadow_mask_pass.js.map +1 -1
  291. package/dist/render/shore_wave_generator.js +1193 -0
  292. package/dist/render/shore_wave_generator.js.map +1 -0
  293. package/dist/render/sky.js +516 -86
  294. package/dist/render/sky.js.map +1 -1
  295. package/dist/render/transmission_thickness_pass.js +477 -0
  296. package/dist/render/transmission_thickness_pass.js.map +1 -0
  297. package/dist/render/underwater.js +58 -158
  298. package/dist/render/underwater.js.map +1 -1
  299. package/dist/render/virtualtexture/virtual_texture.js +1214 -0
  300. package/dist/render/virtualtexture/virtual_texture.js.map +1 -0
  301. package/dist/render/volumetric_cloud/cloud_common.js +472 -0
  302. package/dist/render/volumetric_cloud/cloud_common.js.map +1 -0
  303. package/dist/render/volumetric_cloud/cloud_noise.js +189 -0
  304. package/dist/render/volumetric_cloud/cloud_noise.js.map +1 -0
  305. package/dist/render/volumetric_cloud/debug_view.js +131 -0
  306. package/dist/render/volumetric_cloud/debug_view.js.map +1 -0
  307. package/dist/render/volumetric_cloud/resources.js +380 -0
  308. package/dist/render/volumetric_cloud/resources.js.map +1 -0
  309. package/dist/render/volumetric_cloud/sky_cache.js +380 -0
  310. package/dist/render/volumetric_cloud/sky_cache.js.map +1 -0
  311. package/dist/render/volumetric_cloud/volumetric_cloud.js +318 -0
  312. package/dist/render/volumetric_cloud/volumetric_cloud.js.map +1 -0
  313. package/dist/render/water_caustics.js +134 -319
  314. package/dist/render/water_caustics.js.map +1 -1
  315. package/dist/render/water_foam.js +627 -0
  316. package/dist/render/water_foam.js.map +1 -0
  317. package/dist/render/water_interaction.js +1795 -0
  318. package/dist/render/water_interaction.js.map +1 -0
  319. package/dist/render/weightedblended_oit.js +3 -3
  320. package/dist/render/weightedblended_oit.js.map +1 -1
  321. package/dist/scene/basesprite.js +9 -4
  322. package/dist/scene/basesprite.js.map +1 -1
  323. package/dist/scene/batchgroup.js +108 -12
  324. package/dist/scene/batchgroup.js.map +1 -1
  325. package/dist/scene/environment.js +28 -7
  326. package/dist/scene/environment.js.map +1 -1
  327. package/dist/scene/foliage.js +1020 -0
  328. package/dist/scene/foliage.js.map +1 -0
  329. package/dist/scene/foliage_buckets.js +147 -0
  330. package/dist/scene/foliage_buckets.js.map +1 -0
  331. package/dist/scene/foliage_mask.js +358 -0
  332. package/dist/scene/foliage_mask.js.map +1 -0
  333. package/dist/scene/hair.js +15 -4
  334. package/dist/scene/hair.js.map +1 -1
  335. package/dist/scene/light.js +179 -13
  336. package/dist/scene/light.js.map +1 -1
  337. package/dist/scene/mesh.js +327 -45
  338. package/dist/scene/mesh.js.map +1 -1
  339. package/dist/scene/meshdrawable.js +6 -0
  340. package/dist/scene/meshdrawable.js.map +1 -1
  341. package/dist/scene/msdftext.js +9 -3
  342. package/dist/scene/msdftext.js.map +1 -1
  343. package/dist/scene/msdftextsprite.js +9 -3
  344. package/dist/scene/msdftextsprite.js.map +1 -1
  345. package/dist/scene/particlesys.js +8 -2
  346. package/dist/scene/particlesys.js.map +1 -1
  347. package/dist/scene/physics_settings.js +157 -0
  348. package/dist/scene/physics_settings.js.map +1 -0
  349. package/dist/scene/scene.js +190 -44
  350. package/dist/scene/scene.js.map +1 -1
  351. package/dist/scene/scene_node.js +147 -6
  352. package/dist/scene/scene_node.js.map +1 -1
  353. package/dist/scene/terrain-cm/bladegrassmaterial.js +735 -0
  354. package/dist/scene/terrain-cm/bladegrassmaterial.js.map +1 -0
  355. package/dist/scene/terrain-cm/grass.js +881 -42
  356. package/dist/scene/terrain-cm/grass.js.map +1 -1
  357. package/dist/scene/terrain-cm/grass_gpu.js +929 -0
  358. package/dist/scene/terrain-cm/grass_gpu.js.map +1 -0
  359. package/dist/scene/terrain-cm/grassmaterial.js +180 -9
  360. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  361. package/dist/scene/terrain-cm/terrain-cm.js +465 -36
  362. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  363. package/dist/scene/terrain-cm/terrain_gi_albedo.js +257 -0
  364. package/dist/scene/terrain-cm/terrain_gi_albedo.js.map +1 -0
  365. package/dist/scene/terrain-cm/terrain_rvt.js +395 -0
  366. package/dist/scene/terrain-cm/terrain_rvt.js.map +1 -0
  367. package/dist/scene/update_queue.js +54 -0
  368. package/dist/scene/update_queue.js.map +1 -0
  369. package/dist/scene/water.js +633 -268
  370. package/dist/scene/water.js.map +1 -1
  371. package/dist/scene/water_surface_sampler.js +324 -0
  372. package/dist/scene/water_surface_sampler.js.map +1 -0
  373. package/dist/scene/wind.js +317 -0
  374. package/dist/scene/wind.js.map +1 -0
  375. package/dist/shaders/atmosphere.js +388 -166
  376. package/dist/shaders/atmosphere.js.map +1 -1
  377. package/dist/shaders/fog.js +100 -32
  378. package/dist/shaders/fog.js.map +1 -1
  379. package/dist/shaders/gi.js +365 -0
  380. package/dist/shaders/gi.js.map +1 -0
  381. package/dist/shaders/ltc_rect.js +202 -0
  382. package/dist/shaders/ltc_rect.js.map +1 -0
  383. package/dist/shaders/misc.js +1 -10
  384. package/dist/shaders/misc.js.map +1 -1
  385. package/dist/shaders/noise.js +5 -1
  386. package/dist/shaders/noise.js.map +1 -1
  387. package/dist/shaders/probes.js +309 -0
  388. package/dist/shaders/probes.js.map +1 -0
  389. package/dist/shaders/sdf.js +537 -0
  390. package/dist/shaders/sdf.js.map +1 -0
  391. package/dist/shaders/shadow.js +104 -70
  392. package/dist/shaders/shadow.js.map +1 -1
  393. package/dist/shaders/skin_brdf.js +243 -0
  394. package/dist/shaders/skin_brdf.js.map +1 -0
  395. package/dist/shaders/ssgi.js +63 -0
  396. package/dist/shaders/ssgi.js.map +1 -0
  397. package/dist/shaders/ssgi_reprojection.js +118 -0
  398. package/dist/shaders/ssgi_reprojection.js.map +1 -0
  399. package/dist/shaders/ssr.js +6 -159
  400. package/dist/shaders/ssr.js.map +1 -1
  401. package/dist/shaders/temporal.js +38 -27
  402. package/dist/shaders/temporal.js.map +1 -1
  403. package/dist/shaders/water.js +32 -82
  404. package/dist/shaders/water.js.map +1 -1
  405. package/dist/shaders/water_medium.js +7 -21
  406. package/dist/shaders/water_medium.js.map +1 -1
  407. package/dist/shadow/dom.js +110 -19
  408. package/dist/shadow/dom.js.map +1 -1
  409. package/dist/shadow/esm.js +3 -3
  410. package/dist/shadow/esm.js.map +1 -1
  411. package/dist/shadow/pcf_opt.js +15 -48
  412. package/dist/shadow/pcf_opt.js.map +1 -1
  413. package/dist/shadow/pcf_pd.js +15 -53
  414. package/dist/shadow/pcf_pd.js.map +1 -1
  415. package/dist/shadow/pcss.js +34 -9
  416. package/dist/shadow/pcss.js.map +1 -1
  417. package/dist/shadow/shadow_impl.js +3 -8
  418. package/dist/shadow/shadow_impl.js.map +1 -1
  419. package/dist/shadow/shadow_region.js +40 -2
  420. package/dist/shadow/shadow_region.js.map +1 -1
  421. package/dist/shadow/shadowmapper.js +93 -53
  422. package/dist/shadow/shadowmapper.js.map +1 -1
  423. package/dist/shadow/ssm.js +18 -72
  424. package/dist/shadow/ssm.js.map +1 -1
  425. package/dist/shadow/vsm.js +3 -3
  426. package/dist/shadow/vsm.js.map +1 -1
  427. package/dist/shapes/shape.js +44 -1
  428. package/dist/shapes/shape.js.map +1 -1
  429. package/dist/utility/buoyancy.js +287 -0
  430. package/dist/utility/buoyancy.js.map +1 -0
  431. package/dist/utility/physical.js +10 -1
  432. package/dist/utility/physical.js.map +1 -1
  433. package/dist/utility/pmrem.js +17 -43
  434. package/dist/utility/pmrem.js.map +1 -1
  435. package/dist/utility/sdf/bake.js +435 -0
  436. package/dist/utility/sdf/bake.js.map +1 -0
  437. package/dist/utility/sdf/bvh.js +576 -0
  438. package/dist/utility/sdf/bvh.js.map +1 -0
  439. package/dist/utility/serialization/manager.js +34 -6
  440. package/dist/utility/serialization/manager.js.map +1 -1
  441. package/dist/utility/serialization/scene/animation.js +46 -5
  442. package/dist/utility/serialization/scene/animation.js.map +1 -1
  443. package/dist/utility/serialization/scene/batch.js +11 -4
  444. package/dist/utility/serialization/scene/batch.js.map +1 -1
  445. package/dist/utility/serialization/scene/camera.js +87 -254
  446. package/dist/utility/serialization/scene/camera.js.map +1 -1
  447. package/dist/utility/serialization/scene/cloth.js +25 -0
  448. package/dist/utility/serialization/scene/cloth.js.map +1 -0
  449. package/dist/utility/serialization/scene/common.js +14 -6
  450. package/dist/utility/serialization/scene/common.js.map +1 -1
  451. package/dist/utility/serialization/scene/foliage.js +821 -0
  452. package/dist/utility/serialization/scene/foliage.js.map +1 -0
  453. package/dist/utility/serialization/scene/hair.js +11 -4
  454. package/dist/utility/serialization/scene/hair.js.map +1 -1
  455. package/dist/utility/serialization/scene/light.js +115 -7
  456. package/dist/utility/serialization/scene/light.js.map +1 -1
  457. package/dist/utility/serialization/scene/material.js +388 -220
  458. package/dist/utility/serialization/scene/material.js.map +1 -1
  459. package/dist/utility/serialization/scene/mesh.js +69 -4
  460. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  461. package/dist/utility/serialization/scene/node.js +140 -5
  462. package/dist/utility/serialization/scene/node.js.map +1 -1
  463. package/dist/utility/serialization/scene/node_physics.js +1318 -0
  464. package/dist/utility/serialization/scene/node_physics.js.map +1 -0
  465. package/dist/utility/serialization/scene/particle.js +11 -4
  466. package/dist/utility/serialization/scene/particle.js.map +1 -1
  467. package/dist/utility/serialization/scene/physics.js +120 -0
  468. package/dist/utility/serialization/scene/physics.js.map +1 -0
  469. package/dist/utility/serialization/scene/primitive.js +124 -10
  470. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  471. package/dist/utility/serialization/scene/scene.js +1687 -51
  472. package/dist/utility/serialization/scene/scene.js.map +1 -1
  473. package/dist/utility/serialization/scene/sprite.js +11 -4
  474. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  475. package/dist/utility/serialization/scene/terrain.js +163 -33
  476. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  477. package/dist/utility/serialization/scene/text.js +11 -4
  478. package/dist/utility/serialization/scene/text.js.map +1 -1
  479. package/dist/utility/serialization/scene/water.js +1543 -264
  480. package/dist/utility/serialization/scene/water.js.map +1 -1
  481. package/dist/utility/serialization/types.js.map +1 -1
  482. package/dist/utility/shprojector.js +10 -22
  483. package/dist/utility/shprojector.js.map +1 -1
  484. package/dist/utility/textures/foam.js +170 -0
  485. package/dist/utility/textures/foam.js.map +1 -0
  486. package/dist/utility/textures/ggxlut.js +17 -43
  487. package/dist/utility/textures/ggxlut.js.map +1 -1
  488. package/dist/utility/textures/ltcdata.js +4 -4
  489. package/dist/utility/textures/ltcdata.js.map +1 -1
  490. package/dist/utility/textures/ltclut.js +16 -6
  491. package/dist/utility/textures/ltclut.js.map +1 -1
  492. package/dist/utility/unsupported.js +24 -0
  493. package/dist/utility/unsupported.js.map +1 -0
  494. package/dist/values.js +13 -1
  495. package/dist/values.js.map +1 -1
  496. package/package.json +25 -12
  497. package/src/asset/loaders/ktx2/basis/LICENSE +201 -0
  498. package/src/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
  499. package/src/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
  500. package/src/asset/meshoptimizer/LICENSE +21 -0
  501. package/dist/animation/joint_dynamics/convex_collider.js +0 -320
  502. package/dist/animation/joint_dynamics/convex_collider.js.map +0 -1
@@ -1,22 +1,29 @@
1
1
  import { Vector3, Vector4, Vector2, REVERSE_Z, DEPTH_FARTHEST, DEPTH_REDUCE_CLOSER, DEPTH_REDUCE_FARTHER } from '@zephyr3d/base';
2
- import { RENDER_PASS_TYPE_DEPTH, RENDER_PASS_TYPE_SHADOWMAP, RENDER_PASS_TYPE_OBJECT_COLOR, RENDER_PASS_TYPE_LIGHT, MaterialVaryingFlags, MAX_SKIN_EXTRA_INFLUENCE_PAIRS, MORPH_WEIGHTS_VECTOR_COUNT, MORPH_TARGET_POSITION, MORPH_TARGET_NORMAL, MORPH_TARGET_TANGENT, MORPH_ATTRIBUTE_VECTOR_COUNT, LIGHT_TYPE_DIRECTIONAL } from '../../values.js';
2
+ import { RENDER_PASS_TYPE_DEPTH, RENDER_PASS_TYPE_SHADOWMAP, RENDER_PASS_TYPE_OBJECT_COLOR, RENDER_PASS_TYPE_VOXELIZE, RENDER_PASS_TYPE_LIGHT, MaterialVaryingFlags, MORPH_WEIGHTS_VECTOR_COUNT, MAX_SKIN_EXTRA_INFLUENCE_PAIRS, MORPH_TARGET_POSITION, MORPH_TARGET_NORMAL, MORPH_TARGET_TANGENT, MORPH_ATTRIBUTE_VECTOR_COUNT, LIGHT_TYPE_DIRECTIONAL } from '../../values.js';
3
3
  import { ProgramBuilder } from '@zephyr3d/device';
4
- import { decodeNormalizedFloatFromRGBA, linearToGamma } from '../../shaders/misc.js';
4
+ import { linearToGamma } from '../../shaders/misc.js';
5
+ import { declareVoxelizeUniforms } from '../../shaders/gi.js';
5
6
  import { getSamplerOptions, fetchSampler } from '../../utility/misc.js';
6
- import { PHYSICAL_BAKE_EXPOSURE } from '../../utility/physical.js';
7
+ import { getGICacheExposure, PHYSICAL_BAKE_EXPOSURE } from '../../utility/physical.js';
7
8
  import { getDevice } from '../../app/api.js';
8
9
  import { getDefaultAtmosphereParams, getAtmosphereParamsStruct } from '../../shaders/atmosphere.js';
9
10
  import { getDefaultHeightFogParams, getHeightFogParamsStruct, calculateFog } from '../../shaders/fog.js';
10
11
  import { getShadowReceiverNoL, getShadowReceiverBiasFactor } from '../../shadow/receiver_bias.js';
12
+ import { declareGlobalSDFUniforms, declareDynamicSDFUniforms, GLOBAL_SDF_TEXTURE, sampleSceneSDF } from '../../shaders/sdf.js';
11
13
 
14
+ /** Clipmap levels of the scene field declared in a program, by program builder. */ const sceneSDFLevels = new WeakMap();
12
15
  const UNIFORM_NAME_LIGHT_BUFFER = 'Z_UniformLightBuffer';
13
16
  const UNIFORM_NAME_LIGHT_INDEX_TEXTURE = 'Z_UniformLightIndexTex';
17
+ const UNIFORM_NAME_CLUSTER_GRID = 'Z_ClusterGrid';
18
+ const UNIFORM_NAME_CLUSTER_LIGHT_LIST = 'Z_ClusterLightList';
14
19
  const UNIFORM_NAME_BAKED_SKY_MAP = 'Z_UniformBakedSky';
15
20
  const UNIFORM_NAME_AERIALPERSPECTIVE_LUT = 'Z_UniformAerialPerspectiveLUT';
16
21
  const UNIFORM_NAME_SKYDISTANTLIGHT_LUT = 'Z_UniformSkyDistantLightLUT';
22
+ const UNIFORM_NAME_FOG_SKYLIGHT_CUBEMAP = 'Z_UniformFogSkyLightCubemap';
17
23
  const UNIFORM_NAME_SHADOW_MAP = 'Z_UniformShadowMap';
18
24
  const UNIFORM_NAME_SHADOW_MASK = 'Z_UniformShadowMask';
19
25
  const UNIFORM_NAME_SHADOW_MASK_MODE = 'Z_UniformShadowMaskMode';
26
+ const UNIFORM_NAME_TRANSMISSION_THICKNESS = 'Z_UniformTransmissionThickness';
20
27
  const UNIFORM_NAME_CAUSTIC_MAP = 'Z_UniformCausticMap';
21
28
  const UNIFORM_NAME_CAUSTIC_HEIGHT_MAP = 'Z_UniformCausticHeightMap';
22
29
  /**
@@ -55,6 +62,7 @@ const UNIFORM_NAME_INSTANCE_DATA_OFFSET = 'Z_UniformInstanceDataOffset';
55
62
  const UNIFORM_NAME_BONE_MATRICES = 'Z_UniformBoneMatrices';
56
63
  const UNIFORM_NAME_BONE_TEXTURE_SIZE = 'Z_UniformBoneTexSize';
57
64
  const UNIFORM_NAME_BONE_INV_BIND_MATRIX = 'Z_UniformBoneInvBindMatrix';
65
+ const UNIFORM_NAME_SKIN_PALETTE_BASE = 'Z_UniformSkinPaletteBase';
58
66
  const UNIFORM_NAME_SKIN_INFLUENCE_DATA = 'Z_UniformSkinInfluenceData';
59
67
  const UNIFORM_NAME_SKIN_INFLUENCE_INFO = 'Z_UniformSkinInfluenceInfo';
60
68
  const UNIFORM_NAME_MORPH_DATA = 'Z_UniformMorphData';
@@ -68,6 +76,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
68
76
  static MATERIAL_INSTANCE_DATA_OFFSET = 9;
69
77
  /** @internal */ static defaultSunDir = Vector3.one().inplaceNormalize();
70
78
  /** @internal 1x1x1 fallback bound to the shadow-mask uniform when no mask exists this frame. */ static _dummyShadowMask = null;
79
+ static _dummyTransmissionThickness = null;
71
80
  /** @internal */ static SKIN_MATRIX_NAME = 'Z_SkinMatrix';
72
81
  static SKIN_PREV_MATRIX_NAME = 'Z_PrevSkinMatrix';
73
82
  static SKIN_BONE_OFFSET = 'Z_boneOffset';
@@ -127,6 +136,9 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
127
136
  static getBoneInvBindMatrixUniformName() {
128
137
  return UNIFORM_NAME_BONE_INV_BIND_MATRIX;
129
138
  }
139
+ static getSkinPaletteBaseUniformName() {
140
+ return UNIFORM_NAME_SKIN_PALETTE_BASE;
141
+ }
130
142
  static getSkinInfluenceDataUniformName() {
131
143
  return UNIFORM_NAME_SKIN_INFLUENCE_DATA;
132
144
  }
@@ -231,8 +243,12 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
231
243
  if (casterParams) {
232
244
  casterParams.impl?.declareCasterUniforms(scope, casterParams);
233
245
  }
234
- } else if (ctx.renderPass.type === RENDER_PASS_TYPE_DEPTH || ctx.renderPass.type === RENDER_PASS_TYPE_OBJECT_COLOR) {
246
+ } else if (ctx.renderPass.type === RENDER_PASS_TYPE_DEPTH || ctx.renderPass.type === RENDER_PASS_TYPE_OBJECT_COLOR || ctx.renderPass.type === RENDER_PASS_TYPE_VOXELIZE) {
235
247
  scope.camera = cameraStruct().uniform(0);
248
+ // Write-only storage textures are fragment-only in WebGPU
249
+ if (ctx.renderPass.type === RENDER_PASS_TYPE_VOXELIZE && pb.shaderKind === 'fragment') {
250
+ declareVoxelizeUniforms(scope, 0);
251
+ }
236
252
  } else if (ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT) {
237
253
  const useClusteredLighting = !ctx.currentShadowLight;
238
254
  if (ctx.materialFlags & MaterialVaryingFlags.APPLY_FOG) {
@@ -248,6 +264,8 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
248
264
  // Static samplers: LUTs are rgba16f without mipmaps
249
265
  scope[UNIFORM_NAME_AERIALPERSPECTIVE_LUT] = pb.tex2D().uniform(0).withSampler(getSamplerOptions('clamp_linear_nomip'));
250
266
  scope[UNIFORM_NAME_SKYDISTANTLIGHT_LUT] = pb.tex2D().uniform(0).withSampler(getSamplerOptions('clamp_linear_nomip'));
267
+ // Mipmapped radiance map, sampled at a roughness-selected LOD
268
+ scope[UNIFORM_NAME_FOG_SKYLIGHT_CUBEMAP] = pb.texCube().uniform(0).withSampler(getSamplerOptions('clamp_linear'));
251
269
  }
252
270
  const lightStruct = ctx.currentShadowLight ? pb.defineStruct([
253
271
  pb.vec3('sunDir'),
@@ -271,7 +289,12 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
271
289
  pb.float('envLightSpecularStrength'),
272
290
  pb.vec4('clusterParams'),
273
291
  pb.ivec4('countParams'),
292
+ // Buffer indices of the lights shaded outside the clusters, zero-terminated.
293
+ pb.ivec4('globalLights'),
274
294
  pb.ivec2('lightIndexTexSize'),
295
+ // Nonzero when the clusters were sliced for an orthographic camera
296
+ // (linear depth slices instead of exponential).
297
+ pb.int('clusterOrtho'),
275
298
  // Number of shadow-casting lights at the head of the clustered light
276
299
  // buffer (indices 1..N). Only present on the screen-space shadow mask
277
300
  // path (part of the global bind group hash), so the declared and bound
@@ -283,9 +306,17 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
283
306
  scope.camera = cameraStruct().uniform(0);
284
307
  scope.light = lightStruct().uniform(0);
285
308
  if (useClusteredLighting) {
286
- scope[UNIFORM_NAME_LIGHT_BUFFER] = pb.vec4[(this.getMaxClusterLights() + 1) * 4]().uniformBuffer(0);
287
- // Non-WebGL1 devices fetch the light index texture with textureLoad
288
- scope[UNIFORM_NAME_LIGHT_INDEX_TEXTURE] = (pb.getDevice().type === 'webgl' ? pb.tex2D() : pb.utex2D().noSampler()).uniform(0);
309
+ if (this.usesClusterLightLists()) {
310
+ // Storage buffers sized to the frame's lights: no cap on the lights in total or
311
+ // per cluster. See ClusteredLight for the layout of the grid and the lists.
312
+ scope[UNIFORM_NAME_LIGHT_BUFFER] = pb.vec4[0]().storageBufferReadonly(0);
313
+ scope[UNIFORM_NAME_CLUSTER_GRID] = pb.uint[0]().storageBufferReadonly(0);
314
+ scope[UNIFORM_NAME_CLUSTER_LIGHT_LIST] = pb.uint[0]().storageBufferReadonly(0);
315
+ } else {
316
+ scope[UNIFORM_NAME_LIGHT_BUFFER] = pb.vec4[(this.getMaxClusterLights() + 1) * 4]().uniformBuffer(0);
317
+ // The light index texture is fetched with textureLoad
318
+ scope[UNIFORM_NAME_LIGHT_INDEX_TEXTURE] = pb.utex2D().noSampler().uniform(0);
319
+ }
289
320
  // Screen-space shadow mask array (rgba8unorm, 4 shadow lights per layer).
290
321
  // Sampled by clustered lights whose buffer index is <= numShadowLights.
291
322
  // Presence is keyed into the global bind group hash, so declared and bound
@@ -298,6 +329,12 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
298
329
  // e.g. OIT hair, where the opaque-depth mask would be incorrect).
299
330
  scope[UNIFORM_NAME_SHADOW_MASK_MODE] = pb.int().uniform(0);
300
331
  }
332
+ // Light-space thickness for subsurface transmission, packed exactly like
333
+ // the shadow mask. Presence is keyed into the bind group hash through
334
+ // ctx.transmissionThickness, so declared and bound layouts always agree.
335
+ if (ctx.transmissionThickness) {
336
+ scope[UNIFORM_NAME_TRANSMISSION_THICKNESS] = pb.tex2DArray().uniform(0);
337
+ }
301
338
  }
302
339
  // Baked sky cube: rgba16f without mipmaps
303
340
  scope[UNIFORM_NAME_BAKED_SKY_MAP] = pb.texCube().uniform(0).withSampler(getSamplerOptions('clamp_linear_nomip'));
@@ -378,75 +415,13 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
378
415
  if (!this.hasSkinning(scope)) {
379
416
  return null;
380
417
  }
381
- const pb = scope.$builder;
382
- const isWebGL = pb.getDevice().type === 'webgl';
383
- const supportsTextureLoad = !isWebGL;
384
- const funcNameGetBoneMatrixFromTexture = 'Z_getBoneMatrixFromTexture';
385
- pb.func(funcNameGetBoneMatrixFromTexture, [
386
- pb.int('boneIndex')
387
- ], function() {
388
- const boneTexture = this[UNIFORM_NAME_BONE_MATRICES];
389
- this.$l.uvOffsets = pb.textureSampleLevel(boneTexture, pb.div(pb.vec2(0.5), this[UNIFORM_NAME_BONE_TEXTURE_SIZE]), 0);
390
- this.$l.currentOffset = pb.int(this.uvOffsets.x);
391
- this.$l.w = this[UNIFORM_NAME_BONE_TEXTURE_SIZE].x;
392
- this.$l.pixelIndex = pb.float(pb.mul(pb.add(this.boneIndex, this.currentOffset), 4));
393
- this.$l.xIndex = pb.mod(this.pixelIndex, this.w);
394
- this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, this.w));
395
- this.$l.u1 = pb.div(pb.add(this.xIndex, 0.5), this.w);
396
- this.$l.u2 = pb.div(pb.add(this.xIndex, 1.5), this.w);
397
- this.$l.u3 = pb.div(pb.add(this.xIndex, 2.5), this.w);
398
- this.$l.u4 = pb.div(pb.add(this.xIndex, 3.5), this.w);
399
- this.$l.v = pb.div(pb.add(this.yIndex, 0.5), this.w);
400
- this.$l.row1 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u1, this.v), 0);
401
- this.$l.row2 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u2, this.v), 0);
402
- this.$l.row3 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u3, this.v), 0);
403
- this.$l.row4 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u4, this.v), 0);
404
- this.$return(pb.mat4(this.row1, this.row2, this.row3, this.row4));
405
- });
406
- const funcNameGetSkinningMatrix = 'Z_getSkinningMatrix';
407
- pb.func(funcNameGetSkinningMatrix, [], function() {
408
- const invBindMatrix = this[UNIFORM_NAME_BONE_INV_BIND_MATRIX];
409
- const blendIndices = scope.$getVertexAttrib('blendIndices');
410
- const blendWeights = scope.$getVertexAttrib('blendWeights');
411
- this.$l.m0 = scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(blendIndices[0]));
412
- this.$l.m1 = scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(blendIndices[1]));
413
- this.$l.m2 = scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(blendIndices[2]));
414
- this.$l.m3 = scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(blendIndices[3]));
415
- this.$l.m = pb.add(pb.mul(this.m0, blendWeights.x), pb.mul(this.m1, blendWeights.y), pb.mul(this.m2, blendWeights.z), pb.mul(this.m3, blendWeights.w));
416
- this.$l.skinInfo = this[UNIFORM_NAME_SKIN_INFLUENCE_INFO];
417
- this.$if(pb.greaterThan(this.skinInfo.z, 4), function() {
418
- this.$l.vertexIndex = isWebGL ? pb.int(scope.$inputs.zFakeVertexID) : pb.int(scope.$builtins.vertexIndex);
419
- this.$l.texWidth = pb.int(this.skinInfo.x);
420
- this.$l.pairCount = pb.int(this.skinInfo.w);
421
- this.$for(pb.int('pairIndex'), 0, MAX_SKIN_EXTRA_INFLUENCE_PAIRS, function() {
422
- this.$if(pb.greaterThanEqual(this.pairIndex, this.pairCount), function() {
423
- this.$break();
424
- });
425
- this.$l.pixelIndex = pb.add(pb.mul(this.vertexIndex, this.pairCount), this.pairIndex);
426
- this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
427
- this.$l.yIndex = pb.div(this.pixelIndex, this.texWidth);
428
- if (supportsTextureLoad) {
429
- this.$l.extra = pb.textureLoad(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.ivec2(this.xIndex, this.yIndex), 0);
430
- } else {
431
- this.$l.u = pb.div(pb.add(pb.float(this.xIndex), 0.5), this.skinInfo.x);
432
- this.$l.v = pb.div(pb.add(pb.float(this.yIndex), 0.5), this.skinInfo.y);
433
- this.$l.extra = pb.textureSampleLevel(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.vec2(this.u, this.v), 0);
434
- }
435
- this.$if(pb.greaterThan(this.extra.y, 0), function() {
436
- this.m = pb.add(this.m, pb.mul(scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(this.extra.x)), this.extra.y));
437
- });
438
- this.$if(pb.greaterThan(this.extra.w, 0), function() {
439
- this.m = pb.add(this.m, pb.mul(scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(this.extra.z)), this.extra.w));
440
- });
441
- });
442
- });
443
- this.$return(pb.mul(invBindMatrix, this.m));
444
- });
445
- return scope.$g[funcNameGetSkinningMatrix]();
418
+ if (!scope[this.SKIN_MATRIX_NAME]) {
419
+ this.prepareSkinAnimation(scope);
420
+ }
421
+ return scope[this.SKIN_MATRIX_NAME];
446
422
  }
447
423
  static calculateMorphDelta(scope, attrib) {
448
424
  const pb = scope.$builder;
449
- const isWebGL1 = pb.getDevice().type === 'webgl';
450
425
  if (pb.shaderKind !== 'vertex') {
451
426
  throw new Error(`ShaderHelper.calculateMorphDelta(): must be called at vertex stage`);
452
427
  }
@@ -458,7 +433,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
458
433
  this.$if(pb.lessThan(this.offset, 0), function() {
459
434
  this.$return(pb.vec4(0));
460
435
  });
461
- this.$l.vertexIndex = isWebGL1 ? pb.int(scope.$inputs.zFakeVertexID) : pb.int(scope.$builtins.vertexIndex);
436
+ this.$l.vertexIndex = pb.int(scope.$builtins.vertexIndex);
462
437
  const morphInfo = scope[that.getMorphInfoUniformName()];
463
438
  this.$l.metaData = pb.ivec4(morphInfo[0]);
464
439
  this.$l.texWidth = pb.float(this.metaData.x);
@@ -466,43 +441,21 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
466
441
  this.$l.numVertices = this.metaData.z;
467
442
  this.$l.numTargets = this.metaData.w;
468
443
  this.$l.value = pb.vec4(0);
469
- if (isWebGL1) {
470
- this.$for(pb.int('i'), 0, MORPH_WEIGHTS_VECTOR_COUNT, function() {
471
- this.$for(pb.int('j'), 0, 4, function() {
472
- this.$l.index = pb.add(pb.mul(this.i, 4), this.j);
473
- this.$if(pb.greaterThanEqual(this.index, this.numTargets), function() {
474
- this.$return(this.value);
475
- });
476
- this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
477
- this.$if(pb.notEqual(this.weight, 0), function() {
478
- this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
479
- this.$l.pixelIndex = pb.float(pb.add(this.offset, pb.mul(this.targetIndex, this.numVertices), this.vertexIndex));
480
- this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
481
- this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, this.texWidth));
482
- this.$l.u = pb.div(pb.add(this.xIndex, 0.5), this.texWidth);
483
- this.$l.v = pb.div(pb.add(this.yIndex, 0.5), this.texHeight);
484
- this.$l.morphValue = pb.textureSampleLevel(this[that.getMorphDataUniformName()], pb.vec2(this.u, this.v), 0);
485
- this.value = pb.add(this.value, pb.mul(this.morphValue, this.weight));
486
- });
487
- });
488
- });
489
- } else {
490
- this.$for(pb.int('t'), 0, this.numTargets, function() {
491
- this.$l.i = pb.sar(this.t, 2);
492
- this.$l.j = pb.compAnd(this.t, 3);
493
- this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
494
- this.$if(pb.notEqual(this.weight, 0), function() {
495
- this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
496
- this.$l.pixelIndex = pb.float(pb.add(this.offset, pb.mul(this.targetIndex, this.numVertices), this.vertexIndex));
497
- this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
498
- this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, this.texWidth));
499
- this.$l.u = pb.div(pb.add(this.xIndex, 0.5), this.texWidth);
500
- this.$l.v = pb.div(pb.add(this.yIndex, 0.5), this.texHeight);
501
- this.$l.morphValue = pb.textureSampleLevel(this[that.getMorphDataUniformName()], pb.vec2(this.u, this.v), 0);
502
- this.value = pb.add(this.value, pb.mul(this.morphValue, this.weight));
503
- });
444
+ this.$for(pb.int('t'), 0, this.numTargets, function() {
445
+ this.$l.i = pb.sar(this.t, 2);
446
+ this.$l.j = pb.compAnd(this.t, 3);
447
+ this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
448
+ this.$if(pb.notEqual(this.weight, 0), function() {
449
+ this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
450
+ this.$l.pixelIndex = pb.float(pb.add(this.offset, pb.mul(this.targetIndex, this.numVertices), this.vertexIndex));
451
+ this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
452
+ this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, this.texWidth));
453
+ this.$l.u = pb.div(pb.add(this.xIndex, 0.5), this.texWidth);
454
+ this.$l.v = pb.div(pb.add(this.yIndex, 0.5), this.texHeight);
455
+ this.$l.morphValue = pb.textureSampleLevel(this[that.getMorphDataUniformName()], pb.vec2(this.u, this.v), 0);
456
+ this.value = pb.add(this.value, pb.mul(this.morphValue, this.weight));
504
457
  });
505
- }
458
+ });
506
459
  this.$return(this.value);
507
460
  });
508
461
  const pos = 1 + MORPH_WEIGHTS_VECTOR_COUNT * 2 + (attrib >> 2);
@@ -549,7 +502,6 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
549
502
  * @internal
550
503
  */ static calculateMorphDirectionByDisplacement(scope, base, attrib) {
551
504
  const pb = scope.$builder;
552
- const isWebGL1 = pb.getDevice().type === 'webgl';
553
505
  if (pb.shaderKind !== 'vertex') {
554
506
  throw new Error(`ShaderHelper.calculateMorphDirectionByDisplacement(): must be called at vertex stage`);
555
507
  }
@@ -561,7 +513,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
561
513
  pb.int('directionOffset'),
562
514
  pb.int('positionOffset')
563
515
  ], function() {
564
- this.$l.vertexIndex = isWebGL1 ? pb.int(scope.$inputs.zFakeVertexID) : pb.int(scope.$builtins.vertexIndex);
516
+ this.$l.vertexIndex = pb.int(scope.$builtins.vertexIndex);
565
517
  const morphInfo = scope[that.getMorphInfoUniformName()];
566
518
  this.$l.metaData = pb.ivec4(morphInfo[0]);
567
519
  this.$l.texWidth = pb.float(this.metaData.x);
@@ -599,27 +551,13 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
599
551
  this.weightSum = pb.add(this.weightSum, this.displacementWeight);
600
552
  });
601
553
  };
602
- if (isWebGL1) {
603
- this.$for(pb.int('i'), 0, MORPH_WEIGHTS_VECTOR_COUNT, function() {
604
- this.$for(pb.int('j'), 0, 4, function() {
605
- this.$l.index = pb.add(pb.mul(this.i, 4), this.j);
606
- this.$if(pb.greaterThanEqual(this.index, this.numTargets), function() {
607
- this.$break();
608
- });
609
- this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
610
- this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
611
- accumulateTarget(this);
612
- });
613
- });
614
- } else {
615
- this.$for(pb.int('t'), 0, this.numTargets, function() {
616
- this.$l.i = pb.sar(this.t, 2);
617
- this.$l.j = pb.compAnd(this.t, 3);
618
- this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
619
- this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
620
- accumulateTarget(this);
621
- });
622
- }
554
+ this.$for(pb.int('t'), 0, this.numTargets, function() {
555
+ this.$l.i = pb.sar(this.t, 2);
556
+ this.$l.j = pb.compAnd(this.t, 3);
557
+ this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
558
+ this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
559
+ accumulateTarget(this);
560
+ });
623
561
  this.$l.fallback = scope.$g[normalizeName](this.base, this.directionDelta);
624
562
  this.$if(pb.lessThan(this.weightSum, 1e-6), function() {
625
563
  this.$return(this.fallback);
@@ -663,8 +601,6 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
663
601
  }
664
602
  const that = this;
665
603
  const pb = scope.$builder;
666
- const isWebGL = pb.getDevice().type === 'webgl';
667
- const supportsTextureLoad = !isWebGL;
668
604
  const funcNameGetBoneMatrixFromTexture = 'Z_getBoneMatrixFromTexture';
669
605
  pb.func(funcNameGetBoneMatrixFromTexture, [
670
606
  pb.float('boneIndex'),
@@ -672,6 +608,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
672
608
  ], function() {
673
609
  const boneTexture = this[UNIFORM_NAME_BONE_MATRICES];
674
610
  this.$l.w = this[UNIFORM_NAME_BONE_TEXTURE_SIZE].x;
611
+ this.$l.h = this[UNIFORM_NAME_BONE_TEXTURE_SIZE].y;
675
612
  this.$l.pixelIndex = pb.mul(pb.add(this.boneIndex, this.boneOffset), 4);
676
613
  this.$l.xIndex = pb.mod(this.pixelIndex, this.w);
677
614
  this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, this.w));
@@ -679,7 +616,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
679
616
  this.$l.u2 = pb.div(pb.add(this.xIndex, 1.5), this.w);
680
617
  this.$l.u3 = pb.div(pb.add(this.xIndex, 2.5), this.w);
681
618
  this.$l.u4 = pb.div(pb.add(this.xIndex, 3.5), this.w);
682
- this.$l.v = pb.div(pb.add(this.yIndex, 0.5), this.w);
619
+ this.$l.v = pb.div(pb.add(this.yIndex, 0.5), this.h);
683
620
  this.$l.row1 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u1, this.v), 0);
684
621
  this.$l.row2 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u2, this.v), 0);
685
622
  this.$l.row3 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u3, this.v), 0);
@@ -700,7 +637,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
700
637
  this.$l.m = pb.add(pb.mul(this.m0, blendWeights.x), pb.mul(this.m1, blendWeights.y), pb.mul(this.m2, blendWeights.z), pb.mul(this.m3, blendWeights.w));
701
638
  this.$l.skinInfo = this[UNIFORM_NAME_SKIN_INFLUENCE_INFO];
702
639
  this.$if(pb.greaterThan(this.skinInfo.z, 4), function() {
703
- this.$l.vertexIndex = isWebGL ? pb.int(scope.$inputs.zFakeVertexID) : pb.int(scope.$builtins.vertexIndex);
640
+ this.$l.vertexIndex = pb.int(scope.$builtins.vertexIndex);
704
641
  this.$l.texWidth = pb.int(this.skinInfo.x);
705
642
  this.$l.pairCount = pb.int(this.skinInfo.w);
706
643
  this.$for(pb.int('pairIndex'), 0, MAX_SKIN_EXTRA_INFLUENCE_PAIRS, function() {
@@ -710,13 +647,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
710
647
  this.$l.pixelIndex = pb.add(pb.mul(this.vertexIndex, this.pairCount), this.pairIndex);
711
648
  this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
712
649
  this.$l.yIndex = pb.div(this.pixelIndex, this.texWidth);
713
- if (supportsTextureLoad) {
714
- this.$l.extra = pb.textureLoad(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.ivec2(this.xIndex, this.yIndex), 0);
715
- } else {
716
- this.$l.u = pb.div(pb.add(pb.float(this.xIndex), 0.5), this.skinInfo.x);
717
- this.$l.v = pb.div(pb.add(pb.float(this.yIndex), 0.5), this.skinInfo.y);
718
- this.$l.extra = pb.textureSampleLevel(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.vec2(this.u, this.v), 0);
719
- }
650
+ this.$l.extra = pb.textureLoad(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.ivec2(this.xIndex, this.yIndex), 0);
720
651
  this.$if(pb.greaterThan(this.extra.y, 0), function() {
721
652
  this.m = pb.add(this.m, pb.mul(scope.$g[funcNameGetBoneMatrixFromTexture](this.extra.x, this.boneOffset), this.extra.y));
722
653
  });
@@ -728,8 +659,12 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
728
659
  this.$return(pb.mul(invBindMatrix, this.m));
729
660
  });
730
661
  const motionVector = !!this.getUnjitteredViewProjectionMatrix(scope);
662
+ // The palette header slot holds the slot indices of the current and previous matrix sets
731
663
  const boneTexture = scope[UNIFORM_NAME_BONE_MATRICES];
732
- scope.$l[that.SKIN_BONE_OFFSET] = pb.textureSampleLevel(boneTexture, pb.div(pb.vec2(0.5), scope[UNIFORM_NAME_BONE_TEXTURE_SIZE]), 0).xy;
664
+ const paletteBase = scope[UNIFORM_NAME_SKIN_PALETTE_BASE] ?? this.getInstancedUniform(scope, 4).z;
665
+ const boneTextureSize = scope[UNIFORM_NAME_BONE_TEXTURE_SIZE];
666
+ scope.$l.Z_skinHeaderTexel = pb.mul(paletteBase, 4);
667
+ scope.$l[that.SKIN_BONE_OFFSET] = pb.textureSampleLevel(boneTexture, pb.div(pb.add(pb.vec2(pb.mod(scope.Z_skinHeaderTexel, boneTextureSize.x), pb.floor(pb.div(scope.Z_skinHeaderTexel, boneTextureSize.x))), pb.vec2(0.5)), boneTextureSize), 0).xy;
733
668
  scope.$l[that.SKIN_MATRIX_NAME] = scope[funcNameGetSkinningMatrix](scope[that.SKIN_BONE_OFFSET].x);
734
669
  if (motionVector) {
735
670
  scope.$l[that.SKIN_PREV_MATRIX_NAME] = scope[funcNameGetSkinningMatrix](scope[that.SKIN_BONE_OFFSET].y);
@@ -964,6 +899,9 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
964
899
  scope[UNIFORM_NAME_BONE_MATRICES] = pb.tex2D().uniform(1).sampleType('unfilterable-float');
965
900
  scope[UNIFORM_NAME_BONE_INV_BIND_MATRIX] = pb.mat4().uniform(1);
966
901
  scope[UNIFORM_NAME_BONE_TEXTURE_SIZE] = pb.vec2().uniform(1);
902
+ if (!instanced) {
903
+ scope[UNIFORM_NAME_SKIN_PALETTE_BASE] = pb.float().uniform(1);
904
+ }
967
905
  scope[UNIFORM_NAME_SKIN_INFLUENCE_DATA] = pb.tex2D().uniform(1).sampleType('unfilterable-float');
968
906
  scope[UNIFORM_NAME_SKIN_INFLUENCE_INFO] = pb.vec4().uniform(1);
969
907
  }
@@ -977,7 +915,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
977
915
  }
978
916
  /** @internal */ static setCameraUniforms(bindGroup, ctx, camera, linear) {
979
917
  const pos = camera.getWorldPosition();
980
- const useJitter = ctx.motionVectors && ctx.renderPass.type !== RENDER_PASS_TYPE_SHADOWMAP && ctx.renderPass.type !== RENDER_PASS_TYPE_OBJECT_COLOR;
918
+ const useJitter = ctx.motionVectors && ctx.renderPass.type !== RENDER_PASS_TYPE_SHADOWMAP && ctx.renderPass.type !== RENDER_PASS_TYPE_OBJECT_COLOR && ctx.renderPass.type !== RENDER_PASS_TYPE_VOXELIZE;
981
919
  const cameraStruct = {
982
920
  position: new Vector4(pos.x, pos.y, pos.z, 0),
983
921
  renderSize: new Vector2(ctx.renderWidth, ctx.renderHeight),
@@ -1026,12 +964,14 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1026
964
  viewMatrix: light.viewMatrix,
1027
965
  depthBias: shadowMapParams.depthBiasValues[0],
1028
966
  implParams: shadowMapParams.impl.getParams(this._lightUniformShadow.implParams),
1029
- lightType: light.lightType
967
+ // The shadow projection, not the light's own type: a rect light renders a point light's
968
+ // cube, so its casters must write the point light's radial depth.
969
+ lightType: shadowMapParams.lightType
1030
970
  });
1031
971
  shadowMapParams.impl?.applyCasterUniforms(bindGroup, shadowMapParams);
1032
972
  }
1033
973
  }
1034
- /** @internal */ static setFogUniforms(bindGroup, withAerialPerspective, fogType, additive, atmosphereParams, heightFogParams, aerialPerspectiveLUT, skyDistantLightLUT) {
974
+ /** @internal */ static setFogUniforms(bindGroup, withAerialPerspective, fogType, additive, atmosphereParams, heightFogParams, aerialPerspectiveLUT, skyDistantLightLUT, skyLightCubemap) {
1035
975
  this._fogUniforms.withAerialPerspective = withAerialPerspective;
1036
976
  this._fogUniforms.fogType = fogType;
1037
977
  this._fogUniforms.additive = additive;
@@ -1040,6 +980,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1040
980
  bindGroup.setValue('fog', this._fogUniforms);
1041
981
  bindGroup.setTexture(UNIFORM_NAME_AERIALPERSPECTIVE_LUT, aerialPerspectiveLUT);
1042
982
  bindGroup.setTexture(UNIFORM_NAME_SKYDISTANTLIGHT_LUT, skyDistantLightLUT);
983
+ bindGroup.setTexture(UNIFORM_NAME_FOG_SKYLIGHT_CUBEMAP, skyLightCubemap);
1043
984
  }
1044
985
  /**
1045
986
  * @internal
@@ -1053,6 +994,10 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1053
994
  * quantity on the CPU so the HDR target stays near 1.0 and downstream passes need no unit
1054
995
  * awareness. Legacy returns 1 so its uploads stay byte-identical.
1055
996
  */ static getPreExposure(ctx) {
997
+ // The voxelize pass writes the global illumination's caches, kept at a fixed exposure
998
+ if (ctx.renderPass?.type === RENDER_PASS_TYPE_VOXELIZE) {
999
+ return getGICacheExposure(ctx.scene.lightingMode);
1000
+ }
1056
1001
  return ctx.scene.lightingMode === 'physical' ? ctx.camera.exposure : 1;
1057
1002
  }
1058
1003
  /**
@@ -1100,24 +1045,29 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1100
1045
  const strength = env.light.strength ?? 0;
1101
1046
  return ctx.scene.lightingMode === 'physical' ? this.getPreExposure(ctx) / PHYSICAL_BAKE_EXPOSURE * strength : strength;
1102
1047
  }
1103
- /** @internal */ static setLightUniforms(bindGroup, ctx, clusterParams, countParams, lightBuffer, lightIndexTexture) {
1048
+ /** @internal */ static setLightUniforms(bindGroup, ctx, clusteredLight) {
1104
1049
  const envLightStrength = this.getEnvLightLuminance(ctx);
1050
+ const lightIndexTexture = clusteredLight.lightIndexTexture;
1105
1051
  bindGroup.setValue('light', {
1106
1052
  sunDir: ctx.sunLight ? ctx.sunLight.directionAndCutoff.xyz().scaleBy(-1) : this.defaultSunDir,
1107
- clusterParams: clusterParams,
1108
- countParams: countParams,
1053
+ clusterParams: clusteredLight.clusterParam,
1054
+ countParams: clusteredLight.countParam,
1055
+ globalLights: clusteredLight.globalLights,
1109
1056
  envLightStrength,
1110
1057
  envLightSpecularStrength: ctx.env.light.specularStrength ?? 1,
1111
- lightIndexTexSize: new Int32Array([
1112
- lightIndexTexture.width,
1113
- lightIndexTexture.height
1114
- ]),
1058
+ lightIndexTexSize: clusteredLight.lightIndexTexSize,
1059
+ clusterOrtho: clusteredLight.orthographic ? 1 : 0,
1115
1060
  ...ctx.screenSpaceShadowMask ? {
1116
- numShadowLights: ctx.clusteredLight?.numShadowLights ?? 0
1061
+ numShadowLights: clusteredLight.numShadowLights
1117
1062
  } : {}
1118
1063
  });
1119
- bindGroup.setBuffer(UNIFORM_NAME_LIGHT_BUFFER, lightBuffer);
1120
- bindGroup.setTexture(UNIFORM_NAME_LIGHT_INDEX_TEXTURE, lightIndexTexture);
1064
+ bindGroup.setBuffer(UNIFORM_NAME_LIGHT_BUFFER, clusteredLight.lightBuffer);
1065
+ if (this.usesClusterLightLists()) {
1066
+ bindGroup.setBuffer(UNIFORM_NAME_CLUSTER_GRID, clusteredLight.clusterGridBuffer);
1067
+ bindGroup.setBuffer(UNIFORM_NAME_CLUSTER_LIGHT_LIST, clusteredLight.lightListBuffer);
1068
+ } else {
1069
+ bindGroup.setTexture(UNIFORM_NAME_LIGHT_INDEX_TEXTURE, lightIndexTexture);
1070
+ }
1121
1071
  if (ctx.screenSpaceShadowMask) {
1122
1072
  // The mask array is declared for the clustered light pass whenever the flag is
1123
1073
  // on (keyed into the global bind group hash). When there are no shadow lights
@@ -1125,6 +1075,11 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1125
1075
  // be bound: fall back to a 1x1 dummy.
1126
1076
  bindGroup.setTexture(UNIFORM_NAME_SHADOW_MASK, ctx.shadowMaskTexture ?? this.getDummyShadowMask(ctx.device), fetchSampler('clamp_nearest'));
1127
1077
  }
1078
+ if (ctx.transmissionThickness) {
1079
+ // Falls back to a white dummy, not the shadow mask's zero one: 0 encodes
1080
+ // maximum optical depth here, which would read as full transmission.
1081
+ bindGroup.setTexture(UNIFORM_NAME_TRANSMISSION_THICKNESS, ctx.transmissionThicknessTexture ?? this.getDummyTransmissionThickness(ctx.device), fetchSampler('clamp_nearest'));
1082
+ }
1128
1083
  bindGroup.setTexture(UNIFORM_NAME_BAKED_SKY_MAP, ctx.scene.env.sky.getBakedSkyTexture(ctx));
1129
1084
  if (this.usesWaterCaustics(ctx)) {
1130
1085
  this.setWaterCausticUniforms(bindGroup, ctx);
@@ -1153,7 +1108,62 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1153
1108
  * @param ctx - Current draw context.
1154
1109
  * @returns True when the caustic uniforms take part in this pass.
1155
1110
  * @internal
1156
- */ static usesWaterCaustics(ctx) {
1111
+ */ /**
1112
+ * The scene's signed distance field as light-pass materials see it, or null
1113
+ * when it is off or cannot be built (WebGL2). Part of the light pass's global
1114
+ * bind group and shader variant hashes.
1115
+ * @internal
1116
+ */ static getSceneSDF(ctx) {
1117
+ if (ctx.renderPass?.type !== RENDER_PASS_TYPE_LIGHT || !ctx.scene?.sdf.enabled) {
1118
+ return null;
1119
+ }
1120
+ return ctx.scene.sdf.global;
1121
+ }
1122
+ /**
1123
+ * Declares the scene signed distance field in a material's bind group, when
1124
+ * the scene has one for this draw ({@link ShaderHelper.getSceneSDF}).
1125
+ * Opt-in per material, so the textures it binds only count against the
1126
+ * shaders that read them. Bind it with {@link ShaderHelper.applySceneSDF}.
1127
+ *
1128
+ * @returns Whether it was declared; without it, fall back to something else.
1129
+ */ static declareSceneSDF(scope, ctx, group) {
1130
+ const sdf = this.getSceneSDF(ctx);
1131
+ if (!sdf) {
1132
+ return false;
1133
+ }
1134
+ declareGlobalSDFUniforms(scope, group, sdf.levels);
1135
+ declareDynamicSDFUniforms(scope, group);
1136
+ sceneSDFLevels.set(scope.$builder, sdf.levels);
1137
+ return true;
1138
+ }
1139
+ /**
1140
+ * Binds what {@link ShaderHelper.declareSceneSDF} declared. The values change
1141
+ * every frame (the clipmap follows the camera, dynamic meshes move), so call
1142
+ * it from `applyUniforms` on every draw, not from `applyUniformValues`, which
1143
+ * only runs when the material itself changed.
1144
+ */ static applySceneSDF(bindGroup, ctx) {
1145
+ this.getSceneSDF(ctx)?.applyBindGroup(bindGroup);
1146
+ }
1147
+ /** Whether {@link ShaderHelper.declareSceneSDF} declared the field in this program. */ static hasSceneSDF(scope) {
1148
+ return !!scope[GLOBAL_SDF_TEXTURE];
1149
+ }
1150
+ /**
1151
+ * Signed distance in world units from a world position to the scene's
1152
+ * geometry: static meshes and terrain from the global clipmap, merged with
1153
+ * the nearest dynamic meshes at their current transforms. Negative inside.
1154
+ * Only valid when {@link ShaderHelper.hasSceneSDF} is true.
1155
+ *
1156
+ * Near surfaces the value is accurate to a fraction of the finest voxel
1157
+ * (0.25 m by default); farther away it is a lower bound clamped to a few
1158
+ * voxels of the level covering the point, safe for sphere tracing.
1159
+ */ static sampleSceneSDF(scope, worldPos) {
1160
+ const levels = sceneSDFLevels.get(scope.$builder);
1161
+ if (!levels) {
1162
+ throw new Error('ShaderHelper.sampleSceneSDF(): the scene SDF is not bound in this program');
1163
+ }
1164
+ return sampleSceneSDF(scope, worldPos, levels);
1165
+ }
1166
+ static usesWaterCaustics(ctx) {
1157
1167
  return !!ctx.waterCaustics && !!ctx.waterCausticLight && ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT;
1158
1168
  }
1159
1169
  /**
@@ -1425,7 +1435,13 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1425
1435
  // else in the column.
1426
1436
  this.$l.defocus = pb.float(1);
1427
1437
  }
1428
- this.$l.weight = pb.mul(this.cu.params.x, this.defocus, this.edge);
1438
+ // None in water shallower than the minimum depth, faded in across it:
1439
+ // where the water thins out on a beach the surface is too coarse to
1440
+ // say where the water ends, and the pattern there would come from
1441
+ // photons that never reached it
1442
+ this.$l.minDepth = this.cu.extinction.x;
1443
+ this.$l.shallow = pb.smoothStep(pb.mul(this.minDepth, 0.5), pb.add(pb.mul(this.minDepth, 1.5), 1e-4), this.depth);
1444
+ this.$l.weight = pb.mul(this.cu.params.x, this.defocus, this.edge, this.shallow);
1429
1445
  this.$l.caustic = pb.add(1, pb.mul(pb.sub(this.pattern, 1), this.weight));
1430
1446
  // Soften the waterline so the transmittance does not switch on abruptly.
1431
1447
  this.$l.submerged = pb.smoothStep(0, 0.05, this.depth);
@@ -1625,11 +1641,12 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1625
1641
  * would animate out of step with the surface they are refracted through.
1626
1642
  *
1627
1643
  * @param bindGroup - Bind group of the standalone program.
1628
- * @param ctx - Current draw context.
1644
+ * @param ctx - Current draw context; the current device when omitted, for
1645
+ * a program run outside drawing.
1629
1646
  * @internal
1630
1647
  */ static setStandaloneCameraTime(bindGroup, ctx) {
1631
1648
  bindGroup.setValue('camera', {
1632
- elapsedTime: ctx.device.frameInfo.elapsedOverall * 0.001,
1649
+ elapsedTime: (ctx?.device ?? getDevice()).frameInfo.elapsedOverall * 0.001,
1633
1650
  _pad0: 0,
1634
1651
  _pad1: 0,
1635
1652
  _pad2: 0
@@ -1698,6 +1715,20 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1698
1715
  return scope.light.countParams;
1699
1716
  }
1700
1717
  /**
1718
+ * Buffer indices of the clustered lights every fragment shades regardless of its
1719
+ * cluster, zero-terminated.
1720
+ * @internal
1721
+ */ static getGlobalLights(scope) {
1722
+ return scope.light.globalLights;
1723
+ }
1724
+ /**
1725
+ * Nonzero when the clusters were sliced for an orthographic camera: depth slices
1726
+ * are then linear in view depth rather than exponential.
1727
+ * @internal
1728
+ */ static getClusterOrthographic(scope) {
1729
+ return scope.light.clusterOrtho;
1730
+ }
1731
+ /**
1701
1732
  * Number of shadow-casting lights at the head of the clustered light buffer.
1702
1733
  * Only defined on the screen-space shadow mask path (ctx.screenSpaceShadowMask);
1703
1734
  * a clustered light whose buffer index is `<= N` samples the shadow mask.
@@ -1738,10 +1769,65 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1738
1769
  }
1739
1770
  return tex;
1740
1771
  }
1772
+ /**
1773
+ * Lazily-created 1x1x1 rgba8unorm array texture used as the transmission
1774
+ * thickness binding fallback.
1775
+ *
1776
+ * @remarks
1777
+ * Filled with 1, not left at the zero-initialised default. The thickness
1778
+ * channel encodes `1 - opticalDepth / 5`, so 0 is *maximum* optical depth —
1779
+ * the worst possible value to fall back to, and the opposite of what the real
1780
+ * pass clears its unused channels to. {@link ShaderHelper.getDummyShadowMask}
1781
+ * gets away with zeroes because 0 reads as "fully shadowed" there.
1782
+ *
1783
+ * On the default pipeline this is never sampled: `ctx.transmissionThickness`
1784
+ * and the thickness module's `enabled` are computed from the same condition.
1785
+ * A custom `camera.renderPipeline` that omits the module can break that tie,
1786
+ * and this fallback is what keeps that from turning into full-strength
1787
+ * transmission across the whole screen.
1788
+ * @internal
1789
+ */ static getDummyTransmissionThickness(device) {
1790
+ let tex = this._dummyTransmissionThickness;
1791
+ if (!tex || tex.disposed) {
1792
+ tex = device.createTexture2DArray('rgba8unorm', 1, 1, 1, {
1793
+ mipmapping: false
1794
+ });
1795
+ tex.name = 'DummyTransmissionThickness';
1796
+ tex.update(new Uint8Array([
1797
+ 255,
1798
+ 255,
1799
+ 255,
1800
+ 255
1801
+ ]), 0, 0, 0, 1, 1, 1);
1802
+ this._dummyTransmissionThickness = tex;
1803
+ }
1804
+ return tex;
1805
+ }
1741
1806
  /** @internal */ static getClusteredLightIndexTexture(scope) {
1742
1807
  return scope[UNIFORM_NAME_LIGHT_INDEX_TEXTURE];
1743
1808
  }
1744
1809
  /**
1810
+ * Whether clusters hold variable-length light lists in storage buffers (WebGPU),
1811
+ * rather than 16 byte-sized slots in a texture.
1812
+ * @internal
1813
+ */ static usesClusterLightLists() {
1814
+ return getDevice().type === 'webgpu';
1815
+ }
1816
+ /**
1817
+ * Per cluster, the offset of its light list and the number of lights in it, two
1818
+ * uints each. Only on {@link ShaderHelper.usesClusterLightLists}.
1819
+ * @internal
1820
+ */ static getClusterGrid(scope) {
1821
+ return scope[UNIFORM_NAME_CLUSTER_GRID];
1822
+ }
1823
+ /**
1824
+ * Light buffer indices of every cluster's lights, one list after another. Only on
1825
+ * {@link ShaderHelper.usesClusterLightLists}.
1826
+ * @internal
1827
+ */ static getClusterLightList(scope) {
1828
+ return scope[UNIFORM_NAME_CLUSTER_LIGHT_LIST];
1829
+ }
1830
+ /**
1745
1831
  * Clustered shadow factor for a light, in `[0,1]` (1 = fully lit).
1746
1832
  *
1747
1833
  * - Lights whose clustered buffer index is greater than `numShadowLights` are not
@@ -1784,6 +1870,30 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1784
1870
  return pb.getGlobalScope()[funcName](lightIndex);
1785
1871
  }
1786
1872
  /**
1873
+ * Encoded light-space thickness for a light.
1874
+ *
1875
+ * @internal
1876
+ */ static sampleTransmissionThickness(scope, lightIndex) {
1877
+ const pb = scope.$builder;
1878
+ const that = this;
1879
+ const funcName = 'Z_sampleTransmissionThickness';
1880
+ pb.func(funcName, [
1881
+ pb.int('lightIndex')
1882
+ ], function() {
1883
+ this.$if(pb.greaterThan(this.lightIndex, that.getNumShadowLights(this)), function() {
1884
+ this.$return(pb.float(1));
1885
+ });
1886
+ this.$l.ordinal = pb.sub(this.lightIndex, 1);
1887
+ this.$l.layer = pb.div(this.ordinal, 4);
1888
+ this.$l.channel = pb.sub(this.ordinal, pb.mul(this.layer, 4));
1889
+ this.$l.uv = pb.div(pb.vec2(this.$builtins.fragCoord.xy), that.getRenderSize(this));
1890
+ this.$l.texel = pb.textureArraySampleLevel(this[UNIFORM_NAME_TRANSMISSION_THICKNESS], this.uv, this.layer, 0);
1891
+ this.$l.selector = pb.vec4(pb.float(pb.equal(this.channel, 0)), pb.float(pb.equal(this.channel, 1)), pb.float(pb.equal(this.channel, 2)), pb.float(pb.equal(this.channel, 3)));
1892
+ this.$return(pb.dot(this.texel, this.selector));
1893
+ });
1894
+ return pb.getGlobalScope()[funcName](lightIndex);
1895
+ }
1896
+ /**
1787
1897
  * Gets the uniform variable that contains atmosphere parameters
1788
1898
  * @param scope - Current shader scope
1789
1899
  * @returns The atmosphere parameters
@@ -1906,16 +2016,21 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1906
2016
  }
1907
2017
  }
1908
2018
  /** @internal */ static getLightPositionAndRange(scope, lightIndex) {
1909
- return scope[UNIFORM_NAME_LIGHT_BUFFER].at(scope.$builder.mul(lightIndex, 4));
2019
+ return this.getLightVector(scope, lightIndex, 0);
1910
2020
  }
1911
2021
  /** @internal */ static getLightDirectionAndCutoff(scope, lightIndex) {
1912
- return scope[UNIFORM_NAME_LIGHT_BUFFER].at(scope.$builder.add(scope.$builder.mul(lightIndex, 4), 1));
2022
+ return this.getLightVector(scope, lightIndex, 1);
1913
2023
  }
1914
2024
  /** @internal */ static getLightColorAndIntensity(scope, lightIndex) {
1915
- return scope[UNIFORM_NAME_LIGHT_BUFFER].at(scope.$builder.add(scope.$builder.mul(lightIndex, 4), 2));
2025
+ return this.getLightVector(scope, lightIndex, 2);
1916
2026
  }
1917
2027
  /** @internal */ static getLightExtra(scope, lightIndex) {
1918
- return scope[UNIFORM_NAME_LIGHT_BUFFER].at(scope.$builder.add(scope.$builder.mul(lightIndex, 4), 3));
2028
+ return this.getLightVector(scope, lightIndex, 3);
2029
+ }
2030
+ /** Vector `k` of the 4 of a clustered light, read from the light buffer. */ static getLightVector(scope, lightIndex, k) {
2031
+ const pb = scope.$builder;
2032
+ const buffer = scope[UNIFORM_NAME_LIGHT_BUFFER];
2033
+ return buffer.at(pb.add(pb.mul(lightIndex, 4), k));
1919
2034
  }
1920
2035
  /**
1921
2036
  * Sets the clip space position in vertex shader
@@ -1997,6 +2112,36 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
1997
2112
  return pb.add(worldPos, pb.mul(unitNormal, offset));
1998
2113
  }
1999
2114
  /**
2115
+ * How far a rect light's shadow lookup can be trusted at a receiver, as a
2116
+ * weight for `mix(1, shadow, weight)`.
2117
+ *
2118
+ * @remarks
2119
+ * A rect light's shadow is a cube map rendered from its centre, but its
2120
+ * lighting integrates the whole panel. Where the surface turns away from the
2121
+ * centre the panel's far edge still lights it, while the centre already sees
2122
+ * the receiver occlude itself - so the centre's shadow cuts a hard, texel
2123
+ * stepped terminator through lit surface. The weight fades that lookup out as
2124
+ * the centre sinks to the receiver's horizon.
2125
+ *
2126
+ * The band is a fixed, narrow cosine range rather than one scaled by the
2127
+ * panel's size: the stepped edge only lives where the centre grazes the
2128
+ * surface, and its width is set by shadow bias and texel footprint, not by
2129
+ * the panel. A band as wide as the panel's angular radius also caught flat
2130
+ * receivers - a floor seen low from a large upright panel - and faded their
2131
+ * cast shadows, which have no terminator to hide.
2132
+ *
2133
+ * @param worldNormal - Geometric normal; the terminator follows the surface,
2134
+ * not its normal map.
2135
+ * @param posRange - Light position (xyz).
2136
+ * @internal
2137
+ */ static getRectLightShadowWeight(scope, worldPos, worldNormal, posRange) {
2138
+ const pb = scope.$builder;
2139
+ const toCenter = pb.sub(posRange.xyz, worldPos);
2140
+ const unitNormal = pb.mul(worldNormal, pb.div(1, pb.max(pb.length(worldNormal), 1e-6)));
2141
+ const cosCenter = pb.div(pb.dot(unitNormal, toCenter), pb.max(pb.length(toCenter), 1e-6));
2142
+ return pb.smoothStep(0, 0.01, cosCenter);
2143
+ }
2144
+ /**
2000
2145
  * Calculates shadow of current fragment
2001
2146
  *
2002
2147
  * @param scope - Shader scope
@@ -2024,17 +2169,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
2024
2169
  // would self-occlude it before projecting into shadow space. Scaled per
2025
2170
  // cascade, because each cascade has its own texel world size.
2026
2171
  this.$l.biasedPos = that.applyShadowNormalOffset(shadowMapParams.lightType, this, this.worldPos, this.worldNormal, this.NoL, that.getShadowCascadeBiasScale(this, this.split));
2027
- if (ctx.device.type === 'webgl') {
2028
- this.$l.shadowVertex = pb.vec4();
2029
- this.$for(pb.int('cascade'), 0, 4, function() {
2030
- this.$if(pb.equal(this.cascade, this.split), function() {
2031
- this.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.biasedPos, 1), this.cascade);
2032
- this.$break();
2033
- });
2034
- });
2035
- } else {
2036
- this.$l.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.biasedPos, 1), this.split);
2037
- }
2172
+ this.$l.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.biasedPos, 1), this.split);
2038
2173
  this.$l.shadow = shadowMapParams.impl.computeShadowCSM(shadowMapParams, this, this.shadowVertex, this.NoL, this.split);
2039
2174
  this.shadow = pb.clamp(this.shadow, 0, 1);
2040
2175
  this.$l.shadowDistance = that.getShadowCameraParams(scope).w;
@@ -2049,7 +2184,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
2049
2184
  // Normal offset bias - see the cascaded branch above.
2050
2185
  this.$l.biasedPos = that.applyShadowNormalOffset(shadowMapParams.lightType, this, this.worldPos, this.worldNormal, this.NoL);
2051
2186
  this.$l.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.biasedPos, 1));
2052
- this.$l.shadow = shadowMapParams.impl.computeShadow(shadowMapParams, this, this.shadowVertex, this.NoL);
2187
+ this.$l.shadow = shadowMapParams.impl.computeShadow(shadowMapParams, this, this.shadowVertex, this.NoL, this.worldNormal);
2053
2188
  this.shadow = pb.clamp(this.shadow, 0, 1);
2054
2189
  this.$l.shadowDistance = that.getShadowCameraParams(scope).w;
2055
2190
  this.shadow = pb.mix(this.shadow, 1, pb.smoothStep(pb.mul(this.shadowDistance, 0.8), this.shadowDistance, pb.distance(that.getCameraPosition(this), this.worldPos)));
@@ -2060,24 +2195,22 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
2060
2195
  });
2061
2196
  return pb.getGlobalScope()[funcName](worldPos, worldNormal, NoL);
2062
2197
  }
2063
- static applyFog(scope, worldPos, color, ctx) {
2198
+ static applyFog(scope, worldPos, color, ctx, premultiplied = true) {
2064
2199
  const pb = scope.$builder;
2065
2200
  const that = this;
2066
2201
  if (ctx.materialFlags & MaterialVaryingFlags.APPLY_FOG) {
2067
- const funcName = 'Z_applyFog';
2202
+ const funcName = premultiplied ? 'Z_applyFog' : 'Z_applyFogStraight';
2068
2203
  pb.func(funcName, [
2069
2204
  pb.vec3('worldPos'),
2070
2205
  pb.vec4('color').inout()
2071
2206
  ], function() {
2072
- this.$if(pb.notEqual(this.fog.additive, 0), function() {
2073
- this.$return();
2074
- });
2075
2207
  this.$l.uv = pb.div(pb.vec2(this.$builtins.fragCoord.xy), that.getRenderSize(this));
2076
- this.$l.fogging = calculateFog(this, this.fog.withAerialPerspective, this.fog.fogType, this.fog.atmosphereParams, this.fog.heightFogParams, this.uv, false, that.getCameraPosition(this).xyz, this.worldPos, this.fog.additive, this[UNIFORM_NAME_AERIALPERSPECTIVE_LUT], this[UNIFORM_NAME_SKYDISTANTLIGHT_LUT]);
2077
- this.$l.foggingAlpha = pb.sub(1, pb.mul(pb.sub(1, this.fogging.a), this.color.a));
2078
- this.$l.foggingRGB = pb.mul(this.fogging.rgb, this.color.a);
2079
- this.color = pb.vec4(pb.add(pb.mul(this.color.rgb, this.foggingAlpha), this.foggingRGB), this.color.a);
2080
- //this.color = pb.vec4(pb.vec3(pb.mix(this.u0, this.u1, this.factor)), this.color.a);
2208
+ this.$l.fogging = calculateFog(this, this.fog.withAerialPerspective, this.fog.fogType, this.fog.atmosphereParams, this.fog.heightFogParams, this.uv, false, that.getCameraPosition(this).xyz, this.worldPos, this.fog.additive, this[UNIFORM_NAME_AERIALPERSPECTIVE_LUT], this[UNIFORM_NAME_SKYDISTANTLIGHT_LUT], this[UNIFORM_NAME_FOG_SKYLIGHT_CUBEMAP]);
2209
+ // Premultiplied color: attenuate by the fog transmittance and add the in-scattering weighted
2210
+ // by coverage. Additive light passes and additive blending (alpha 0) get attenuation only --
2211
+ // calculateFog already zeroes the in-scattering for the former. Straight (non-premultiplied)
2212
+ // color is scaled by coverage later, so the in-scattering is added unweighted.
2213
+ this.color = pb.vec4(pb.add(pb.mul(this.color.rgb, this.fogging.a), premultiplied ? pb.mul(this.fogging.rgb, this.color.a) : this.fogging.rgb), this.color.a);
2081
2214
  });
2082
2215
  scope[funcName](worldPos, color);
2083
2216
  }
@@ -2210,7 +2343,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
2210
2343
  */ static sampleLinearDepth(scope, tex, uv, level) {
2211
2344
  const pb = scope.$builder;
2212
2345
  const depth = pb.textureSampleLevel(tex, uv, level);
2213
- return pb.getDevice().type === 'webgl' ? decodeNormalizedFloatFromRGBA(scope, depth) : depth.r;
2346
+ return depth.r;
2214
2347
  }
2215
2348
  static samplePositionFromDepth(scope, depthTex, uv, mat, cameraNearFar) {
2216
2349
  const pb = scope.$builder;
@@ -2261,8 +2394,17 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
2261
2394
  });
2262
2395
  return pb.getGlobalScope()[funcName](outputColor);
2263
2396
  }
2264
- /** @internal */ static getMaxClusterLights() {
2265
- return getDevice().type === 'webgl' ? 64 : 255;
2397
+ /**
2398
+ * Capacity of the clustered light buffer, excluding the unused slot 0.
2399
+ *
2400
+ * @remarks
2401
+ * On WebGPU the buffers are storage buffers grown to fit and light indices are 32
2402
+ * bits, so this only bounds the CPU-side work. On WebGL2 the buffer is 4 vec4 per
2403
+ * light in a uniform block, and 255 is the most a byte-sized cluster slot can address.
2404
+ * @internal
2405
+ */ static getMaxClusterLights() {
2406
+ const device = getDevice();
2407
+ return device.type === 'webgpu' ? 65535 : 255;
2266
2408
  }
2267
2409
  }
2268
2410