@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
@@ -5,6 +5,7 @@ import { ShadowMapPass } from '../shadowmap_pass.js';
5
5
  import { DepthPass } from '../depthpass.js';
6
6
  import { ClusteredLight } from '../cluster_light.js';
7
7
  import { ShadowMaskRenderer } from '../shadow_mask_pass.js';
8
+ import { TransmissionThicknessRenderer } from '../transmission_thickness_pass.js';
8
9
  import { getHiZFormat, buildHiZ } from '../hzb.js';
9
10
  import '../primitive.js';
10
11
  import '@zephyr3d/device';
@@ -25,6 +26,7 @@ import { OrderingScope } from './render_context.js';
25
26
  import { RenderPipeline } from './render_pipeline.js';
26
27
  import { mergeFrameResourceRequirements } from './frame_resource_requirements.js';
27
28
  import { renderObjectColors } from '../gpu_picking.js';
29
+ import '../../node_modules/meshoptimizer/meshopt_simplifier.js';
28
30
  import { BoxShape } from '../../shapes/box.js';
29
31
  import '../../shapes/capsule.js';
30
32
  import '../../shapes/cylinder.js';
@@ -38,11 +40,18 @@ import { UnderwaterRenderer, selectUnderwaterSource } from '../underwater.js';
38
40
  const _scenePass = new LightPass();
39
41
  const _depthPass = new DepthPass();
40
42
  const _shadowMapPass = new ShadowMapPass();
41
- const _clusters = [];
42
43
  const _shadowMaskRenderer = new ShadowMaskRenderer();
44
+ const _transmissionThicknessRenderer = new TransmissionThicknessRenderer();
43
45
  const _waterCausticsRenderer = new WaterCausticsRenderer();
44
46
  const _underwaterRenderer = new UnderwaterRenderer();
45
47
  const _devicePoolAllocator = new DevicePoolAllocator();
48
+ /**
49
+ * Clear value for the depth prepass's skin profile id target.
50
+ *
51
+ * @remarks
52
+ * 0 is "not skin". It has to differ from the depth target's clear, which is the
53
+ * far plane rather than an id.
54
+ */ const SKIN_PROFILE_ID_CLEAR = new Vector4(0, 0, 0, 0);
46
55
  const _textureAffinityCaches = new WeakMap();
47
56
  function getTextureAffinityCache(camera, device) {
48
57
  const current = _textureAffinityCaches.get(camera);
@@ -59,20 +68,14 @@ function getTextureAffinityCache(camera, device) {
59
68
  let _backDepthColorState = null;
60
69
  let _frontDepthColorState = null;
61
70
  const SURFACE_MRT_FLAGS = MaterialVaryingFlags.SCENE_STORE_ROUGHNESS | MaterialVaryingFlags.SSS_STORE_PROFILE | MaterialVaryingFlags.SSS_STORE_DIFFUSE | MaterialVaryingFlags.SCENE_STORE_NORMAL | MaterialVaryingFlags.SSS_STORE_TRANSMISSION | MaterialVaryingFlags.SKIN_SSS_STORE;
62
- function getClusteredLight() {
63
- return _clusters.length > 0 ? _clusters.pop() : new ClusteredLight();
64
- }
65
- function freeClusteredLight(cl) {
66
- _clusters.push(cl);
67
- }
68
71
  function getCoreMaterial(material) {
69
72
  return material?.coreMaterial ?? material ?? null;
70
73
  }
71
74
  function hasSSSMaterialCore(material) {
72
75
  return !!getCoreMaterial(material)?.subsurfaceProfile;
73
76
  }
74
- function hasSkinSSSMaterialCore(material) {
75
- return !!getCoreMaterial(material)?.skinSSS;
77
+ function hasPostSSSMaterialCore(material) {
78
+ return !!getCoreMaterial(material)?.postSSS;
76
79
  }
77
80
  function renderQueueHasActiveSSS(renderQueue) {
78
81
  const itemList = renderQueue.itemList;
@@ -92,7 +95,7 @@ function renderQueueHasActiveSSS(renderQueue) {
92
95
  }
93
96
  return false;
94
97
  }
95
- function renderQueueHasActiveSkinSSS(renderQueue) {
98
+ function renderQueueHasActivePostSSS(renderQueue) {
96
99
  const itemList = renderQueue.itemList;
97
100
  if (!itemList) {
98
101
  return false;
@@ -103,7 +106,7 @@ function renderQueueHasActiveSkinSSS(renderQueue) {
103
106
  ];
104
107
  for (const list of lists){
105
108
  for (const material of list.materialList){
106
- if (hasSkinSSSMaterialCore(material)) {
109
+ if (hasPostSSSMaterialCore(material)) {
107
110
  return true;
108
111
  }
109
112
  }
@@ -129,8 +132,10 @@ function cloneActualSSSListInfo(source, _targetQueue) {
129
132
  morphItemList: filterActualSSSItemList(source.morphItemList),
130
133
  skinAndMorphItemList: filterActualSSSItemList(source.skinAndMorphItemList),
131
134
  instanceItemList: filterActualSSSItemList(source.instanceItemList),
135
+ skinInstanceItemList: filterActualSSSItemList(source.skinInstanceItemList),
132
136
  materialList: filterActualSSSMaterialList(source.materialList),
133
137
  instanceList: {},
138
+ instanceLods: {},
134
139
  renderQueue: source.renderQueue
135
140
  };
136
141
  }
@@ -141,7 +146,7 @@ function cloneActualSSSBundle(source, targetQueue) {
141
146
  };
142
147
  }
143
148
  function hasAnyActualSSSItems(renderItems) {
144
- return renderItems.some((info)=>info.itemList.length > 0 || info.skinItemList.length > 0 || info.morphItemList.length > 0 || info.skinAndMorphItemList.length > 0 || info.instanceItemList.length > 0);
149
+ return renderItems.some((info)=>info.itemList.length > 0 || info.skinItemList.length > 0 || info.morphItemList.length > 0 || info.skinAndMorphItemList.length > 0 || info.instanceItemList.length > 0 || info.skinInstanceItemList.length > 0);
145
150
  }
146
151
  function createActualSSSRenderQueue(renderQueue) {
147
152
  const itemList = renderQueue.itemList;
@@ -253,22 +258,22 @@ function getLightPassColorAttachments(ctx, colorAttachment) {
253
258
  attachments.push(ctx.SSSTransmissionTexture);
254
259
  }
255
260
  if (ctx.materialFlags & MaterialVaryingFlags.SKIN_SSS_STORE) {
256
- attachments.push(ctx.SkinSSSTexture);
261
+ attachments.push(ctx.SSSMaskTexture);
257
262
  }
258
263
  return attachments.length === 1 ? attachments[0] : attachments;
259
264
  }
260
- /** Derive Forward+ options from scene and camera state. @internal */ function deriveForwardPlusOptions(scene, camera, deviceType, renderQueue) {
265
+ /** Derive Forward+ options from scene and camera state. @internal */ function deriveForwardPlusOptions(scene, camera, _deviceType, renderQueue) {
261
266
  const ssr = camera.SSR && scene.env.light.envLight && scene.env.light.envLight.hasRadiance();
262
267
  const ssgi = camera.SSGI && camera.HDR && camera.ssgiIntensity > 0 && scene.env.light.type === 'ibl' && !!scene.env.light.envLight?.hasRadiance() && !!scene.env.light.envLight?.hasIrradiance();
263
268
  const sss = camera.SSS && renderQueueHasActiveSSS(renderQueue);
264
- const skinSSS = camera.skinSSS && renderQueueHasActiveSkinSSS(renderQueue);
269
+ // No camera switch for this one: a skin material always wants its diffusion,
270
+ // and the queue already says whether the frame draws any.
271
+ const postSSS = renderQueueHasActivePostSSS(renderQueue);
265
272
  const needSceneColor = renderQueue.needSceneColor();
266
273
  const needSceneColorWithDepth = renderQueue.needSceneColorWithDepth();
267
274
  // Requested by materials rather than by a post effect, so it is resolved here
268
- // instead of in resolveFrameResourceRequirements. WebGL1 has no Hi-Z at all,
269
- // and a material asking for one there would turn a missing feature into a
270
- // thrown frame; the material is expected to shade without it.
271
- const needHiZNearest = (renderQueue.needHiZNearest() || camera.HiZNearest) && deviceType !== 'webgl';
275
+ // instead of in resolveFrameResourceRequirements.
276
+ const needHiZNearest = renderQueue.needHiZNearest() || camera.HiZNearest;
272
277
  return {
273
278
  depthPrepass: true,
274
279
  motionVectors: false,
@@ -285,12 +290,14 @@ function getLightPassColorAttachments(ctx, colorAttachment) {
285
290
  needSceneColorWithDepth,
286
291
  needsTransmissionDepthForSSR: !!ssr && needSceneColor && !needSceneColorWithDepth,
287
292
  sss: !!sss,
288
- skinSSS: !!skinSSS,
293
+ postSSS: !!postSSS,
294
+ // Driven entirely by postSSS: the id exists to serve the diffusion and the
295
+ // transmission thickness pass, and both are gated on the same condition.
296
+ sssProfileId: !!postSSS,
289
297
  fogPresents: !!scene.env.sky?.fogPresents
290
298
  };
291
299
  }
292
300
  function resolveFrameResourceRequirements(ctx, options, requirements) {
293
- const deviceType = ctx.device.type;
294
301
  options.motionVectors ||= !!requirements.motionVector;
295
302
  options.hiZNearest ||= !!requirements.hiZNearest;
296
303
  // The nearest channel lives on the same pyramid, so asking for it is asking
@@ -299,15 +306,6 @@ function resolveFrameResourceRequirements(ctx, options, requirements) {
299
306
  options.sceneNormal ||= !!requirements.sceneNormal;
300
307
  options.sceneRoughness ||= !!requirements.sceneRoughness;
301
308
  options.shadowMask ||= !!requirements.shadowMask;
302
- if (options.motionVectors && deviceType === 'webgl') {
303
- throw new Error('Forward+: MotionVector was requested but is not supported by the WebGL backend.');
304
- }
305
- if (options.hiZ && deviceType === 'webgl') {
306
- throw new Error('Forward+: HiZ was requested but is not supported by the WebGL backend.');
307
- }
308
- if (options.shadowMask && deviceType === 'webgl') {
309
- throw new Error('Forward+: ShadowMask was requested but is not supported by the WebGL backend.');
310
- }
311
309
  const surfaceAttachmentCount = Number(options.sceneNormal) + Number(options.sceneRoughness);
312
310
  if (surfaceAttachmentCount > 0) {
313
311
  const caps = ctx.device.getDeviceCaps();
@@ -321,6 +319,7 @@ function resolveFrameResourceRequirements(ctx, options, requirements) {
321
319
  }
322
320
  }
323
321
  ctx.motionVectors = options.motionVectors;
322
+ ctx.sssProfileId = options.sssProfileId;
324
323
  ctx.HiZ = options.hiZ;
325
324
  ctx.SSGI = options.ssgi;
326
325
  ctx.screenSpaceShadowMask = options.shadowMask;
@@ -388,8 +387,9 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
388
387
  ordering.emit(builder, 'ClusterLightsDone');
389
388
  builder.sideEffect();
390
389
  builder.setExecute(()=>{
391
- ctx.clusteredLight = getClusteredLight();
392
- ctx.clusteredLight.calculateLightIndex(ctx.camera, renderQueue, ctx.screenSpaceShadowMask, ShaderHelper.getPreExposure(ctx));
390
+ ctx.clusteredLight = ClusteredLight.acquire(ctx.renderWidth, ctx.renderHeight);
391
+ // The light passes draw into targets of the render size (see renderOpaqueScenePass).
392
+ ctx.clusteredLight.calculateLightIndex(ctx.camera, renderQueue, ctx.renderWidth, ctx.renderHeight, ctx.screenSpaceShadowMask, ShaderHelper.getPreExposure(ctx));
393
393
  });
394
394
  });
395
395
  }
@@ -439,7 +439,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
439
439
  * most likely to be looking through.
440
440
  * @internal
441
441
  */ function selectWaterCausticSource(ctx, renderQueue) {
442
- if (ctx.device.type === 'webgl' || renderQueue.waters.length === 0) {
442
+ if (renderQueue.waters.length === 0) {
443
443
  return null;
444
444
  }
445
445
  // The caustic light must own an additive shadow pass, because that is the pass
@@ -581,7 +581,8 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
581
581
  writes: [
582
582
  FrameResources.LinearDepth,
583
583
  FrameResources.MotionVector,
584
- FrameResources.SceneDepthAttachment
584
+ FrameResources.SceneDepthAttachment,
585
+ FrameResources.SSSProfileId
585
586
  ],
586
587
  prepare: ()=>({
587
588
  enabled: true
@@ -590,7 +591,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
590
591
  const { graph, ctx, frame, ordering, blackboard, options } = fg;
591
592
  const result = graph.addPass('DepthPrepass', (builder)=>{
592
593
  ordering.chainInto(builder);
593
- const format = ctx.device.type === 'webgl' ? ctx.SSRCalcThickness ? 'rgba16f' : 'rgba8unorm' : ctx.SSRCalcThickness ? 'rg32f' : 'r32f';
594
+ const format = ctx.SSRCalcThickness ? 'rg32f' : 'r32f';
594
595
  const mvFormat = 'rgba16f';
595
596
  const depthHandle = builder.createTexture({
596
597
  format,
@@ -602,6 +603,14 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
602
603
  label: 'motionVector',
603
604
  allocationKey: 'ForwardPlus.MotionVector'
604
605
  }) : undefined;
606
+ // r8unorm is exact for this: the material writes `SSSProfile.encodedId`,
607
+ // i.e. `id / 255`, and the table holds 256 rows, so every representable id
608
+ // survives the round trip. 0 means "not skin".
609
+ const sssProfileIdHandle = options.sssProfileId ? builder.createTexture({
610
+ format: 'r8unorm',
611
+ label: 'sssProfileId',
612
+ allocationKey: 'ForwardPlus.SSSProfileId'
613
+ }) : undefined;
605
614
  const finalDepthAttachment = ctx.finalFramebuffer?.getDepthAttachment();
606
615
  const externalDepthAttachment = finalDepthAttachment?.isTexture2D() ? finalDepthAttachment : null;
607
616
  const externalDepthAttachmentHandle = externalDepthAttachment ? graph.importTexture('externalSceneDepth') : undefined;
@@ -611,14 +620,23 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
611
620
  allocationKey: 'ForwardPlus.SceneDepth'
612
621
  });
613
622
  const depthAttachmentOrFormat = externalDepthAttachmentHandle ?? graphDepthAttachmentHandle ?? ctx.depthFormat;
623
+ // Order matters and has to agree with the fragment output declaration
624
+ // order in MeshMaterial._createProgram: linear depth, then the motion
625
+ // vector, then the profile id.
626
+ const prepassColorAttachments = [
627
+ depthHandle
628
+ ];
629
+ if (motionVectorHandle) {
630
+ prepassColorAttachments.push(motionVectorHandle);
631
+ }
632
+ if (sssProfileIdHandle) {
633
+ prepassColorAttachments.push(sssProfileIdHandle);
634
+ }
614
635
  const depthFramebufferHandle = builder.createFramebuffer({
615
636
  label: 'DepthPrepassFramebuffer',
616
637
  width: ctx.renderWidth,
617
638
  height: ctx.renderHeight,
618
- colorAttachments: motionVectorHandle ? [
619
- depthHandle,
620
- motionVectorHandle
621
- ] : depthHandle,
639
+ colorAttachments: prepassColorAttachments.length === 1 ? depthHandle : prepassColorAttachments,
622
640
  depthAttachment: depthAttachmentOrFormat,
623
641
  ignoreDepthStencil: false
624
642
  });
@@ -646,6 +664,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
646
664
  return {
647
665
  depthHandle,
648
666
  motionVectorHandle,
667
+ sssProfileIdHandle: sssProfileIdHandle,
649
668
  graphDepthAttachmentHandle,
650
669
  externalDepthAttachmentHandle,
651
670
  externalDepthAttachment,
@@ -658,6 +677,9 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
658
677
  if (result.motionVectorHandle) {
659
678
  blackboard.set(FrameResources.MotionVector, result.motionVectorHandle);
660
679
  }
680
+ if (result.sssProfileIdHandle) {
681
+ blackboard.set(FrameResources.SSSProfileId, result.sssProfileIdHandle);
682
+ }
661
683
  blackboard.set(FrameResources.SceneDepthAttachment, result.graphDepthAttachmentHandle ?? result.externalDepthAttachmentHandle);
662
684
  // Later modules share this attachment without owning it.
663
685
  fg.state.renderDepthAttachment = result.graphDepthAttachmentHandle ?? result.externalDepthAttachmentHandle ?? null;
@@ -675,7 +697,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
675
697
  prepare: ({ ctx, options, renderQueue })=>({
676
698
  enabled: options.shadowMask && renderQueue.shadowedLights.length > 0,
677
699
  requirements: {
678
- shadowMask: ctx.device.type !== 'webgl' && ctx.camera.screenSpaceShadowMask && renderQueue.shadowedLights.length > 0
700
+ shadowMask: ctx.camera.screenSpaceShadowMask && renderQueue.shadowedLights.length > 0
679
701
  }
680
702
  }),
681
703
  setup (fg) {
@@ -714,6 +736,107 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
714
736
  blackboard.set(FrameResources.ShadowMask, maskPassResult.maskHandle);
715
737
  }
716
738
  };
739
+ /** Orders a pass drawing scene materials after this camera's virtual texture update. @internal */ function readVirtualTextureToken(builder, blackboard) {
740
+ const token = blackboard.get(FrameResources.VirtualTexture);
741
+ if (token) {
742
+ builder.read(token);
743
+ }
744
+ }
745
+ /** Scene virtual texture clients active for this camera's frame. @internal */ function activeVirtualTextureClients(ctx, renderQueue) {
746
+ if (ctx.device.type !== 'webgpu') {
747
+ return [];
748
+ }
749
+ return ctx.scene.virtualTextureClients.filter((c)=>c.isActive?.(ctx, renderQueue) ?? true);
750
+ }
751
+ /**
752
+ * Updates the scene's virtual textures for this camera: mark from the depth prepass, map, fill.
753
+ */ const VirtualTextureModule = {
754
+ type: 'VirtualTextureUpdate',
755
+ reads: [
756
+ {
757
+ resource: FrameResources.LinearDepth
758
+ }
759
+ ],
760
+ writes: [
761
+ FrameResources.VirtualTexture
762
+ ],
763
+ prepare: ({ ctx, renderQueue })=>({
764
+ enabled: activeVirtualTextureClients(ctx, renderQueue).length > 0
765
+ }),
766
+ setup (fg) {
767
+ const { graph, ctx, renderQueue, blackboard } = fg;
768
+ const clients = activeVirtualTextureClients(ctx, renderQueue);
769
+ const token = graph.addPass('VirtualTextureUpdate', (builder)=>{
770
+ const depthHandle = blackboard.expect(FrameResources.LinearDepth);
771
+ builder.read(depthHandle);
772
+ // Page tables and pools are scene state the graph does not track
773
+ builder.sideEffect();
774
+ const token = builder.createToken('VirtualTextureDone');
775
+ builder.setExecute((rgCtx)=>{
776
+ const depthTex = rgCtx.getTexture(depthHandle);
777
+ const stamp = ctx.device.frameInfo.frameCounter;
778
+ for (const client of clients){
779
+ client.markFromDepth?.(ctx, depthTex);
780
+ }
781
+ for (const client of clients){
782
+ client.virtualTexture.update(stamp);
783
+ client.fill(ctx);
784
+ }
785
+ });
786
+ return token;
787
+ });
788
+ blackboard.set(FrameResources.VirtualTexture, token);
789
+ }
790
+ };
791
+ /** @internal */ const TransmissionThicknessModule = {
792
+ type: 'TransmissionThicknessPass',
793
+ writes: [
794
+ FrameResources.TransmissionThickness
795
+ ],
796
+ prepare: ({ options, renderQueue })=>({
797
+ // Not gated on the device: transmission lights the subject directly in the
798
+ // base pass, so it does not depend on the WebGPU-only PostSSS diffusion.
799
+ enabled: options.postSSS && renderQueue.shadowedLights.some((light)=>light.transmission)
800
+ }),
801
+ setup (fg) {
802
+ const { graph, ctx, renderQueue, blackboard } = fg;
803
+ const depthPassResult = requireBuildState(fg, 'depth', 'DepthPrepass', 'TransmissionThicknessPass');
804
+ // The ordinal space is shared with the shadow mask so that a light's
805
+ // thickness and its shadow factor are found with the same arithmetic; that
806
+ // means the layer count follows the shadow light count, not the (smaller)
807
+ // transmission light count.
808
+ const numShadowLights = renderQueue.shadowedLights.length;
809
+ const numLayers = ShadowMaskRenderer.getLayerCount(numShadowLights);
810
+ const passResult = graph.addPass('TransmissionThicknessPass', (builder)=>{
811
+ const depthHandle = blackboard.expect(FrameResources.LinearDepth);
812
+ const sssProfileIdHandle = blackboard.expect(FrameResources.SSSProfileId);
813
+ builder.read(depthHandle);
814
+ builder.read(sssProfileIdHandle);
815
+ builder.read(depthPassResult.depthFramebufferHandle);
816
+ const thicknessHandle = builder.createTexture({
817
+ format: 'rgba8unorm',
818
+ label: 'transmissionThickness',
819
+ arrayLayers: numLayers,
820
+ allocationKey: 'ForwardPlus.TransmissionThickness'
821
+ });
822
+ builder.setExecute((rgCtx)=>{
823
+ const depthTex = rgCtx.getTexture(depthHandle);
824
+ const thicknessTex = rgCtx.getTexture(thicknessHandle);
825
+ _transmissionThicknessRenderer.render(ctx, depthTex, rgCtx.getTexture(sssProfileIdHandle), renderQueue.shadowedLights, (layer)=>rgCtx.createFramebuffer({
826
+ width: thicknessTex.width,
827
+ height: thicknessTex.height,
828
+ colorAttachments: thicknessTex,
829
+ depthAttachment: null,
830
+ attachmentLayer: layer
831
+ }));
832
+ });
833
+ return {
834
+ thicknessHandle
835
+ };
836
+ });
837
+ blackboard.set(FrameResources.TransmissionThickness, passResult.thicknessHandle);
838
+ }
839
+ };
717
840
  /** @internal */ const TransmissionDepthForSSRModule = {
718
841
  type: 'TransmissionDepthForSSR',
719
842
  writes: [
@@ -811,10 +934,12 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
811
934
  const depthPassResult = requireBuildState(fg, 'depth', 'DepthPrepass', 'SSSProfile');
812
935
  const preLightTransmissionDepthToken = fg.state.preLightTransmissionDepthToken;
813
936
  const shadowMaskHandle = blackboard.get(FrameResources.ShadowMask);
937
+ const thicknessHandle = blackboard.get(FrameResources.TransmissionThickness);
814
938
  const waterCausticsHandle = blackboard.get(FrameResources.WaterCaustics);
815
939
  const renderDepthAttachment = fg.state.renderDepthAttachment;
816
940
  const sssProfileResult = graph.addPass('SSSProfile', (builder)=>{
817
941
  builder.read(blackboard.expect(FrameResources.LinearDepth));
942
+ readVirtualTextureToken(builder, blackboard);
818
943
  builder.read(depthPassResult.depthFramebufferHandle);
819
944
  if (preLightTransmissionDepthToken) {
820
945
  builder.read(preLightTransmissionDepthToken);
@@ -822,6 +947,9 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
822
947
  if (shadowMaskHandle) {
823
948
  builder.read(shadowMaskHandle);
824
949
  }
950
+ if (thicknessHandle) {
951
+ builder.read(thicknessHandle);
952
+ }
825
953
  if (waterCausticsHandle) {
826
954
  // Same reason as the shadow mask: the map reaches the shader through
827
955
  // ctx.waterCausticTexture, so only a declared read stops the executor
@@ -853,6 +981,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
853
981
  });
854
982
  builder.setExecute((rgCtx)=>{
855
983
  ctx.shadowMaskTexture = shadowMaskHandle ? rgCtx.getTexture(shadowMaskHandle) : null;
984
+ ctx.transmissionThicknessTexture = thicknessHandle ? rgCtx.getTexture(thicknessHandle) : null;
856
985
  renderForwardSSSProfile(frame, rgCtx.getFramebuffer(framebufferHandle), rgCtx.getTexture(profileHandle), rgCtx.getTexture(paramHandle), null);
857
986
  });
858
987
  return {
@@ -876,10 +1005,12 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
876
1005
  const depthPassResult = requireBuildState(fg, 'depth', 'DepthPrepass', 'SceneColorGrab');
877
1006
  const preLightTransmissionDepthToken = fg.state.preLightTransmissionDepthToken;
878
1007
  const shadowMaskHandle = blackboard.get(FrameResources.ShadowMask);
1008
+ const thicknessHandle = blackboard.get(FrameResources.TransmissionThickness);
879
1009
  const waterCausticsHandle = blackboard.get(FrameResources.WaterCaustics);
880
1010
  const renderDepthAttachment = fg.state.renderDepthAttachment;
881
1011
  const grabResult = graph.addPass('SceneColorGrab', (builder)=>{
882
1012
  builder.read(blackboard.expect(FrameResources.LinearDepth));
1013
+ readVirtualTextureToken(builder, blackboard);
883
1014
  builder.read(depthPassResult.depthFramebufferHandle);
884
1015
  if (preLightTransmissionDepthToken) {
885
1016
  builder.read(preLightTransmissionDepthToken);
@@ -887,6 +1018,9 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
887
1018
  if (shadowMaskHandle) {
888
1019
  builder.read(shadowMaskHandle);
889
1020
  }
1021
+ if (thicknessHandle) {
1022
+ builder.read(thicknessHandle);
1023
+ }
890
1024
  if (waterCausticsHandle) {
891
1025
  // Same reason as the shadow mask: the map reaches the shader through
892
1026
  // ctx.waterCausticTexture, so only a declared read stops the executor
@@ -928,6 +1062,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
928
1062
  }) : undefined;
929
1063
  builder.setExecute((rgCtx)=>{
930
1064
  ctx.shadowMaskTexture = shadowMaskHandle ? rgCtx.getTexture(shadowMaskHandle) : null;
1065
+ ctx.transmissionThicknessTexture = thicknessHandle ? rgCtx.getTexture(thicknessHandle) : null;
931
1066
  renderSceneColorGrab(frame, rgCtx, copyHandle, copyFramebufferHandle);
932
1067
  });
933
1068
  return {
@@ -946,7 +1081,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
946
1081
  FrameResources.SceneNormal,
947
1082
  FrameResources.SSSDiffuse,
948
1083
  FrameResources.SSSTransmission,
949
- FrameResources.SkinSSS
1084
+ FrameResources.PostSSS
950
1085
  ],
951
1086
  prepare: ()=>({
952
1087
  enabled: true
@@ -955,11 +1090,13 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
955
1090
  const { graph, ctx, frame, blackboard, options, backbuffer } = fg;
956
1091
  const depthPassResult = requireBuildState(fg, 'depth', 'DepthPrepass', 'LightPass');
957
1092
  const shadowMaskHandle = blackboard.get(FrameResources.ShadowMask);
1093
+ const thicknessHandle = blackboard.get(FrameResources.TransmissionThickness);
958
1094
  const waterCausticsHandle = blackboard.get(FrameResources.WaterCaustics);
959
1095
  const preLightTransmissionDepthToken = fg.state.preLightTransmissionDepthToken;
960
1096
  const hiZHandle = blackboard.get(FrameResources.HiZ);
961
1097
  const sssProfileHandle = blackboard.get(FrameResources.SSSProfile);
962
1098
  const sssParamHandle = blackboard.get(FrameResources.SSSParam);
1099
+ const sssProfileIdHandle = blackboard.get(FrameResources.SSSProfileId);
963
1100
  const sceneColorCopyHandle = blackboard.get(FrameResources.SceneColorCopy);
964
1101
  const useFinalFramebufferAsIntermediate = fg.state.useFinalFramebufferAsIntermediate;
965
1102
  const renderDepthAttachment = fg.state.renderDepthAttachment;
@@ -1022,10 +1159,14 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1022
1159
  }
1023
1160
  const opaquePassResult = graph.addPass('LightPass', (builder)=>{
1024
1161
  builder.read(blackboard.expect(FrameResources.LinearDepth));
1162
+ readVirtualTextureToken(builder, blackboard);
1025
1163
  builder.read(depthPassResult.depthFramebufferHandle);
1026
1164
  if (shadowMaskHandle) {
1027
1165
  builder.read(shadowMaskHandle);
1028
1166
  }
1167
+ if (thicknessHandle) {
1168
+ builder.read(thicknessHandle);
1169
+ }
1029
1170
  if (waterCausticsHandle) {
1030
1171
  // Same reason as the shadow mask: the map reaches the shader through
1031
1172
  // ctx.waterCausticTexture, so only a declared read stops the executor
@@ -1056,6 +1197,11 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1056
1197
  if (sssParamHandle) {
1057
1198
  builder.read(sssParamHandle);
1058
1199
  }
1200
+ // Reached through ctx.SSSProfileIdTexture, so the read must be declared or
1201
+ // the executor may recycle the prepass target before the effect samples it.
1202
+ if (sssProfileIdHandle) {
1203
+ builder.read(sssProfileIdHandle);
1204
+ }
1059
1205
  const surfaceAttachmentCount = Number(options.sceneRoughness) + Number(options.sceneNormal);
1060
1206
  // Surface MRT products are exposed through blackboard handles.
1061
1207
  const sceneRoughnessHandle = options.sceneRoughness ? builder.createTexture({
@@ -1073,8 +1219,8 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1073
1219
  if (writeSSSDiffuse && writeSSSTransmission && surfaceAttachmentCount > 0 && getSSSLightingTextureFormat(ctx, 2, surfaceAttachmentCount) !== ctx.colorFormat) {
1074
1220
  writeSSSTransmission = false;
1075
1221
  }
1076
- const writeSkinSSS = options.skinSSS;
1077
- const sssLightingAttachmentCount = (writeSSSDiffuse ? 1 : 0) + (writeSSSTransmission ? 1 : 0) + (writeSkinSSS ? 1 : 0);
1222
+ const writePostSSS = options.postSSS;
1223
+ const sssLightingAttachmentCount = (writeSSSDiffuse ? 1 : 0) + (writeSSSTransmission ? 1 : 0) + (writePostSSS ? 1 : 0);
1078
1224
  const sssLightingFormat = getSSSLightingTextureFormat(ctx, sssLightingAttachmentCount, surfaceAttachmentCount);
1079
1225
  const sssDiffuseHandle = writeSSSDiffuse ? builder.createTexture({
1080
1226
  format: sssLightingFormat,
@@ -1086,10 +1232,10 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1086
1232
  label: 'sssTransmission',
1087
1233
  allocationKey: 'ForwardPlus.SSSTransmission'
1088
1234
  }) : undefined;
1089
- const skinSSSHandle = writeSkinSSS ? builder.createTexture({
1235
+ const postSSSHandle = writePostSSS ? builder.createTexture({
1090
1236
  format: sssLightingFormat,
1091
- label: 'skinSSS',
1092
- allocationKey: 'ForwardPlus.SkinSSS'
1237
+ label: 'postSSS',
1238
+ allocationKey: 'ForwardPlus.PostSSS'
1093
1239
  }) : undefined;
1094
1240
  const sceneColorFramebufferHandle = useFinalFramebufferAsIntermediate ? undefined : builder.createFramebuffer({
1095
1241
  label: 'SceneColorFramebuffer',
@@ -1103,6 +1249,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1103
1249
  const sceneColorCopyTex = sceneColorCopyHandle ? rgCtx.getTexture(sceneColorCopyHandle) : null;
1104
1250
  // Legacy effects still read resolved MRT textures from DrawContext.
1105
1251
  ctx.shadowMaskTexture = shadowMaskHandle ? rgCtx.getTexture(shadowMaskHandle) : null;
1252
+ ctx.transmissionThicknessTexture = thicknessHandle ? rgCtx.getTexture(thicknessHandle) : null;
1106
1253
  ctx.HiZTexture = hiZHandle ? rgCtx.getTexture(hiZHandle) : null;
1107
1254
  ctx.SceneRoughnessTexture = sceneRoughnessHandle ? rgCtx.getTexture(sceneRoughnessHandle) : null;
1108
1255
  ctx.SceneNormalTexture = sceneNormalHandle ? rgCtx.getTexture(sceneNormalHandle) : null;
@@ -1110,7 +1257,8 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1110
1257
  ctx.SSSParamTexture = sssParamHandle ? rgCtx.getTexture(sssParamHandle) : null;
1111
1258
  ctx.SSSDiffuseTexture = sssDiffuseHandle ? rgCtx.getTexture(sssDiffuseHandle) : null;
1112
1259
  ctx.SSSTransmissionTexture = sssTransmissionHandle ? rgCtx.getTexture(sssTransmissionHandle) : null;
1113
- ctx.SkinSSSTexture = skinSSSHandle ? rgCtx.getTexture(skinSSSHandle) : null;
1260
+ ctx.SSSMaskTexture = postSSSHandle ? rgCtx.getTexture(postSSSHandle) : null;
1261
+ ctx.SSSProfileIdTexture = sssProfileIdHandle ? rgCtx.getTexture(sssProfileIdHandle) : null;
1114
1262
  const renderLightPass = ()=>renderOpaqueScenePass(frame, sceneColorTex, sceneColorCopyTex, rgCtx, sceneColorFramebufferHandle);
1115
1263
  if (historyManager && lightHistoryReadBindings.length > 0) {
1116
1264
  const ssgiIrradianceBinding = lightHistoryReadBindings.find((binding)=>binding.name === RGHistoryResources.SSGI_IRRADIANCE);
@@ -1139,7 +1287,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1139
1287
  sceneNormalHandle,
1140
1288
  sssDiffuseHandle,
1141
1289
  sssTransmissionHandle,
1142
- skinSSSHandle
1290
+ postSSSHandle: postSSSHandle
1143
1291
  };
1144
1292
  });
1145
1293
  const lightPassResult = opaquePassResult;
@@ -1157,8 +1305,8 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1157
1305
  if (lightPassResult.sssTransmissionHandle) {
1158
1306
  blackboard.set(FrameResources.SSSTransmission, lightPassResult.sssTransmissionHandle);
1159
1307
  }
1160
- if (lightPassResult.skinSSSHandle) {
1161
- blackboard.set(FrameResources.SkinSSS, lightPassResult.skinSSSHandle);
1308
+ if (lightPassResult.postSSSHandle) {
1309
+ blackboard.set(FrameResources.PostSSS, lightPassResult.postSSSHandle);
1162
1310
  }
1163
1311
  }
1164
1312
  };
@@ -1297,6 +1445,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1297
1445
  const hiZHandle = blackboard.get(FrameResources.HiZ);
1298
1446
  const sceneColorCopyHandle = blackboard.get(FrameResources.SceneColorCopy);
1299
1447
  const shadowMaskHandle = blackboard.get(FrameResources.ShadowMask);
1448
+ const thicknessHandle = blackboard.get(FrameResources.TransmissionThickness);
1300
1449
  const waterCausticsHandle = blackboard.get(FrameResources.WaterCaustics);
1301
1450
  const lightPassResult = requireBuildState(fg, 'lightPass', 'LightPass', 'CompositeTail');
1302
1451
  const renderDepthAttachment = fg.state.renderDepthAttachment;
@@ -1325,7 +1474,10 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1325
1474
  blackboard.get(FrameResources.SceneNormal),
1326
1475
  blackboard.get(FrameResources.SSSDiffuse),
1327
1476
  blackboard.get(FrameResources.SSSTransmission),
1328
- blackboard.get(FrameResources.SkinSSS)
1477
+ blackboard.get(FrameResources.PostSSS),
1478
+ // Reached through ctx.SSSProfileIdTexture: without the dependency the
1479
+ // allocator may hand the prepass target to a later pass.
1480
+ blackboard.get(FrameResources.SSSProfileId)
1329
1481
  ]){
1330
1482
  if (handle) {
1331
1483
  opaqueChainDeps.push(handle);
@@ -1350,6 +1502,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1350
1502
  // Transparent geometry writes a new version of the opaque-chain output.
1351
1503
  const sceneColorHandle = graph.addPass('TransparentPass', (builder)=>{
1352
1504
  builder.read(blackboard.expect(FrameResources.LinearDepth));
1505
+ readVirtualTextureToken(builder, blackboard);
1353
1506
  builder.read(depthPassResult.depthFramebufferHandle);
1354
1507
  if (hiZHandle) {
1355
1508
  // Transparent materials may ray-march HiZ.
@@ -1371,6 +1524,12 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1371
1524
  // after LightPass while this pass is still sampling it.
1372
1525
  builder.read(shadowMaskHandle);
1373
1526
  }
1527
+ if (thicknessHandle) {
1528
+ // Same reasoning as the shadow mask above: the thickness texture is
1529
+ // bound by the shared clustered-light bind group, so its lifetime has to
1530
+ // reach this pass too.
1531
+ builder.read(thicknessHandle);
1532
+ }
1374
1533
  if (waterCausticsHandle) {
1375
1534
  // Same lifetime argument as the shadow mask above.
1376
1535
  builder.read(waterCausticsHandle);
@@ -1382,6 +1541,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1382
1541
  // is the same, but relying on the leftover would make this pass depend
1383
1542
  // on execution order instead of on its own declared inputs.
1384
1543
  ctx.shadowMaskTexture = shadowMaskHandle ? rgCtx.getTexture(shadowMaskHandle) : null;
1544
+ ctx.transmissionThicknessTexture = thicknessHandle ? rgCtx.getTexture(thicknessHandle) : null;
1385
1545
  renderTransparentScenePass(frame, rgCtx, opaqueChainRan ? opaqueChainResult.color : null, lightPassResult.sceneColorFramebufferHandle);
1386
1546
  });
1387
1547
  return out;
@@ -1480,6 +1640,28 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1480
1640
  return outputBackbuffer;
1481
1641
  });
1482
1642
  }
1643
+ // Debug views of raw data go on the presented image, past tone mapping and exposure
1644
+ const sky = ctx.scene.env.sky;
1645
+ if (sky.cloudDebugViewActive) {
1646
+ const presentedBeforeDebug = presentedBackbuffer;
1647
+ presentedBackbuffer = graph.addPass('VolumetricCloudDebug', (builder)=>{
1648
+ const out = builder.write(presentedBeforeDebug);
1649
+ builder.sideEffect();
1650
+ builder.setExecute(()=>{
1651
+ const device = ctx.device;
1652
+ device.pushDeviceStates();
1653
+ try {
1654
+ device.setFramebuffer(ctx.finalFramebuffer ?? null);
1655
+ device.setViewport(null);
1656
+ device.setScissor(null);
1657
+ sky.drawCloudDebugView();
1658
+ } finally{
1659
+ device.popDeviceStates();
1660
+ }
1661
+ });
1662
+ return out;
1663
+ });
1664
+ }
1483
1665
  fg.state.presentedBackbuffer = presentedBackbuffer;
1484
1666
  // Downstream modules may replace this sink registration.
1485
1667
  blackboard.set(FrameResources.PresentedColor, presentedBackbuffer);
@@ -1492,7 +1674,9 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1492
1674
  ShadowMaps: ShadowMapsModule,
1493
1675
  WaterCaustics: WaterCausticsModule,
1494
1676
  DepthPrepass: DepthPrepassModule,
1677
+ VirtualTextureUpdate: VirtualTextureModule,
1495
1678
  ShadowMask: ShadowMaskModule,
1679
+ TransmissionThickness: TransmissionThicknessModule,
1496
1680
  TransmissionDepthForSSR: TransmissionDepthForSSRModule,
1497
1681
  HiZ: HiZModule,
1498
1682
  SSSProfile: SSSProfileModule,
@@ -1509,7 +1693,9 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
1509
1693
  ShadowMapsModule,
1510
1694
  WaterCausticsModule,
1511
1695
  DepthPrepassModule,
1696
+ VirtualTextureModule,
1512
1697
  ShadowMaskModule,
1698
+ TransmissionThicknessModule,
1513
1699
  TransmissionDepthForSSRModule,
1514
1700
  HiZModule,
1515
1701
  SSSProfileModule,
@@ -1539,11 +1725,17 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
1539
1725
  options.ssgi &&= supportsSSGIRenderTargets(ctx);
1540
1726
  ctx.SSS = !!options.sss;
1541
1727
  ctx.SSGI = !!options.ssgi;
1728
+ ctx.postSSS = !!options.postSSS;
1729
+ // Before the compositor is consulted below: it collects requirements from,
1730
+ // and builds passes for, only the effects that are enabled at that moment.
1731
+ ctx.camera?.setPostSSSActive(ctx.postSSS);
1542
1732
  ctx.SSGIIrradianceHistoryTexture = null;
1543
1733
  ctx.SSGISurfaceHistoryTexture = null;
1544
- ctx.SkinSSSTexture = null;
1734
+ ctx.SSSMaskTexture = null;
1735
+ ctx.SSSProfileIdTexture = null;
1545
1736
  // ShadowMask sets this only when it produces a texture.
1546
1737
  ctx.shadowMaskTexture = null;
1738
+ ctx.transmissionThicknessTexture = null;
1547
1739
  // WaterCaustics turns these on together once it has produced a map; they are
1548
1740
  // read by every light pass, so they must start cleared each frame.
1549
1741
  ctx.waterCaustics = false;
@@ -1584,6 +1776,10 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
1584
1776
  const requirements = ctx.compositor?.collectRequirements(ctx) ?? {};
1585
1777
  mergeFrameResourceRequirements(requirements, pipeline.collectRequirements(fg));
1586
1778
  resolveFrameResourceRequirements(ctx, options, requirements);
1779
+ // Must agree with TransmissionThicknessModule.prepare: the flag decides
1780
+ // whether the light pass declares and binds the thickness texture, so a
1781
+ // mismatch would leave the shader layout disagreeing with the bind group.
1782
+ ctx.transmissionThickness = options.postSSS && renderQueue.shadowedLights.some((light)=>light.transmission);
1587
1783
  pipeline.build(fg);
1588
1784
  validateProducedFrameResources(blackboard, options, renderQueue);
1589
1785
  const presented = blackboard.get(FrameResources.PresentedColor) ?? fg.state.presentedBackbuffer;
@@ -1680,7 +1876,7 @@ function disposeRenderQueue(frame) {
1680
1876
  }
1681
1877
  function releaseClusteredLight(frame) {
1682
1878
  if (!frame.clusteredLightReleased && frame.ctx.clusteredLight) {
1683
- freeClusteredLight(frame.ctx.clusteredLight);
1879
+ ClusteredLight.release(frame.ctx.clusteredLight);
1684
1880
  frame.ctx.clusteredLight = undefined;
1685
1881
  frame.clusteredLightReleased = true;
1686
1882
  }
@@ -1714,7 +1910,7 @@ function cleanupFrame(frame) {
1714
1910
  console.assert(!!existingDepthFb, 'renderSceneDepth: called without an existing depth framebuffer; the ' + 'self-allocating fallback path is untested in the forward+ pipeline.');
1715
1911
  if (!depthFramebuffer) {
1716
1912
  {
1717
- const format = ctx.device.type === 'webgl' ? ctx.SSRCalcThickness ? 'rgba16f' : 'rgba8unorm' : ctx.SSRCalcThickness ? 'rg32f' : 'r32f';
1913
+ const format = ctx.SSRCalcThickness ? 'rg32f' : 'r32f';
1718
1914
  const mvFormat = 'rgba16f';
1719
1915
  if (!ctx.finalFramebuffer) {
1720
1916
  depthFramebuffer = rgCtx.createFramebuffer({
@@ -1763,6 +1959,27 @@ function cleanupFrame(frame) {
1763
1959
  ctx.device.setFramebuffer(depthFramebuffer);
1764
1960
  _depthPass.encodeDepth = depthFramebuffer.getColorAttachments()[0].format === 'rgba8unorm';
1765
1961
  _depthPass.clearColor = transmission ? null : _depthPass.encodeDepth ? new Vector4(0, 0, 0, 1) : new Vector4(1, 1, 1, 1);
1962
+ // That clear value is chosen for the linear depth target, where 1 is the far
1963
+ // plane. The profile id target needs the opposite: 0 means "not skin", and
1964
+ // every other value addresses a real row of the profile table. Clearing it
1965
+ // alongside the depth made each background texel read as profile 255, so a
1966
+ // sampling disc wide enough to reach past the silhouette accepted those taps
1967
+ // and averaged the background into the skin instead of rejecting it — as
1968
+ // dark speckle, since only the pixels whose sequence happened to draw a
1969
+ // long radius reached that far.
1970
+ //
1971
+ // Assigning clearColor above resets the per-target list, so this has to come
1972
+ // after it; that also means a frame without the target cannot inherit a
1973
+ // stale list.
1974
+ if (!transmission && ctx.sssProfileId) {
1975
+ const attachmentCount = depthFramebuffer.getColorAttachments().length;
1976
+ if (attachmentCount > 1) {
1977
+ // The id is appended last; see DepthPrepassModule.
1978
+ const clearColors = new Array(attachmentCount).fill(_depthPass.clearColor);
1979
+ clearColors[attachmentCount - 1] = SKIN_PROFILE_ID_CLEAR;
1980
+ _depthPass.clearColors = clearColors;
1981
+ }
1982
+ }
1766
1983
  _depthPass.clearDepth = transmission ? null : DEPTH_CLEAR_VALUE;
1767
1984
  _depthPass.clearStencil = null;
1768
1985
  _depthPass.transmission = transmission;
@@ -1970,7 +2187,7 @@ function blitToCurrentColorAttachment(ctx, source) {
1970
2187
  ctx.materialFlags |= MaterialVaryingFlags.SSS_STORE_TRANSMISSION;
1971
2188
  }
1972
2189
  }
1973
- if (ctx.SkinSSSTexture) {
2190
+ if (ctx.SSSMaskTexture) {
1974
2191
  ctx.materialFlags |= MaterialVaryingFlags.SKIN_SSS_STORE;
1975
2192
  }
1976
2193
  // Prefer the graph framebuffer; final-target mode has no graph framebuffer.