@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,245 +1,219 @@
1
- import { Vector4 } from '@zephyr3d/base';
1
+ import { Vector3, Vector2 } from '@zephyr3d/base';
2
2
  import { applyMaterialMixins, MeshMaterial } from './meshmaterial.js';
3
3
  import { mixinLight } from './mixins/lit.js';
4
4
  import { mixinVertexColor } from './mixins/vertexcolor.js';
5
5
  import { mixinTextureProps } from './mixins/texture.js';
6
6
  import { ShaderHelper } from './shader/helper.js';
7
- import { RENDER_PASS_TYPE_LIGHT, LIGHT_TYPE_POINT, MaterialVaryingFlags } from '../values.js';
7
+ import { RENDER_PASS_TYPE_LIGHT, RENDER_PASS_TYPE_DEPTH, LIGHT_TYPE_POINT, MaterialVaryingFlags } from '../values.js';
8
+ import { skinDualSpecularRoughness, skinSpecularEnergyTerms, skinDiffuseBRDF, skinTransmission, skinDualLobeSpecular } from '../shaders/skin_brdf.js';
9
+ import { SSSProfile } from './sssprofile.js';
10
+ import { fetchSampler } from '../utility/misc.js';
8
11
 
9
- /**
10
- * HDR range packed into the SkinSSS side buffer when the render graph falls
11
- * back to an 8-bit format. The material divides the scatter irradiance by this
12
- * factor on write and {@link SkinSSS} multiplies it back after the blur.
13
- * @public
12
+ /**
13
+ * HDR range that used to be packed into the PostSSS side buffer when the render
14
+ * graph fell back to an 8-bit format.
15
+ *
16
+ * @deprecated The skin scattering source is now recovered from `SceneColor` via
17
+ * the diffuse luminance stored in its alpha channel (matching UE5), so no side
18
+ * buffer and no LDR encoding is involved. Kept for source compatibility; it has
19
+ * no effect on rendering.
20
+ *
21
+ * @public
14
22
  */ const SKIN_SSS_LDR_ENCODE_RANGE = 4;
15
- /**
16
- * Stylized realtime skin material.
17
- *
18
- * @remarks
19
- * This material is designed for the {@link SkinSSS} post effect. It renders a regular lit color
20
- * and, when `camera.skinSSS` is enabled, writes the *diffusible* part of that color - the stylized
21
- * diffuse ramp plus back-lit transmission, without specular - into the `SkinSSSTexture` MRT.
22
- *
23
- * The side buffer holds exactly the same term that is already in the lit color, untinted and
24
- * unscaled. {@link SkinSSS} subtracts it and adds back a channel-dependent diffused version, so
25
- * the redistribution is energy conserving by construction and disabling the effect leaves the
26
- * image unchanged. Scatter tint, strength and radius therefore live on the post effect, not here.
27
- *
28
- * Stylization is split from diffusion on purpose. {@link SkinMaterial.diffuseWrap},
29
- * {@link SkinMaterial.diffuseSoftness}, {@link SkinMaterial.shadowTint} and
30
- * {@link SkinMaterial.brightening} shape the direct lighting ramp; the post effect then diffuses
31
- * whatever ramp they produced. A stylized ramp stays stylized after scattering rather than being
32
- * averaged away.
33
- *
34
- * The optional `subsurfaceTexture` uses R as skin mask, G as local softness and B as thickness
35
- * for the back-lit transmission term (thin regions such as ears and nostrils glow when lit from
36
- * behind; enable it by setting {@link SkinMaterial.transmissionStrength}).
37
- *
38
- * @public
39
- */ class SkinMaterial extends applyMaterialMixins(MeshMaterial, mixinLight, mixinVertexColor, mixinTextureProps('subsurface')) {
23
+ /**
24
+ * Physically-based skin material aligned with UE5's SubsurfaceProfile shading model.
25
+ *
26
+ * @remarks
27
+ * Uses a pre-integrated curvature-dependent diffuse BRDF and dual-lobe GGX specular
28
+ * driven by subsurface profile parameters. The **diffuse** luminance is written to
29
+ * `SceneColor.a` so the {@link PostSSS} post effect can recover the diffusible
30
+ * fraction as `saturate(SceneColor.a / luma(SceneColor.rgb))` — the same spec/diff
31
+ * separation UE5 performs in its SSS Setup and Recombine passes.
32
+ *
33
+ * The optional `subsurfaceTexture` uses R as the skin mask, which gates both the
34
+ * screen-space diffusion and the dual specular lobe. Its other channels are
35
+ * unused: transmission thickness is measured against the light's shadow map by
36
+ * the transmission thickness pass rather than painted, and curvature is not part
37
+ * of this path.
38
+ *
39
+ * @public
40
+ */ class SSSMaterial extends applyMaterialMixins(MeshMaterial, mixinLight, mixinVertexColor, mixinTextureProps('subsurface')) {
40
41
  static FEATURE_VERTEX_NORMAL = this.defineFeature();
41
42
  static FEATURE_VERTEX_TANGENT = this.defineFeature();
42
- _shininess;
43
- _specularStrength;
44
- _diffuseWrap;
45
- _diffuseSoftness;
46
- _scatterWrap;
47
- _scatterStrength;
48
- _scatterColor;
43
+ _roughness;
44
+ _specularF0;
49
45
  _transmissionStrength;
50
- _transmissionPower;
51
- _shadowTint;
52
- _brightening;
46
+ _profile;
47
+ _subsurfaceProfileChanged;
48
+ _lobeParams;
49
+ _profileTexelSize;
53
50
  constructor(){
54
51
  super();
55
- this._shininess = 72;
56
- this._specularStrength = 1;
57
- this._diffuseWrap = 0.28;
58
- this._diffuseSoftness = 0.45;
59
- this._scatterWrap = 0.65;
60
- this._scatterStrength = 1.5;
61
- this._scatterColor = new Vector4(1, 0.42, 0.28, 1);
62
- this._transmissionStrength = 0;
63
- this._transmissionPower = 4;
64
- this._shadowTint = new Vector4(0, 0, 0, 1);
65
- this._brightening = 0;
66
- this.useFeature(SkinMaterial.FEATURE_VERTEX_NORMAL, true);
67
- }
68
- /** Marker used by the forward render graph to allocate the SkinSSS MRT. */ get skinSSS() {
52
+ this._subsurfaceProfileChanged = ()=>this.uniformChanged();
53
+ // Created here and released in onDispose: the profile holds a row of a
54
+ // 256-entry GPU table, and tying its life to the material's is what keeps
55
+ // those rows from accumulating.
56
+ this._profile = SSSProfile.createOwned();
57
+ this._profile.addChangeListener(this._subsurfaceProfileChanged);
58
+ this._lobeParams = new Vector3();
59
+ this._profileTexelSize = new Vector2();
60
+ this._roughness = 0.5;
61
+ this._specularF0 = 0.04;
62
+ this._transmissionStrength = 1;
63
+ this.useFeature(SSSMaterial.FEATURE_VERTEX_NORMAL, true);
64
+ }
65
+ /** Releases the profile's table row along with the material. */ onDispose() {
66
+ super.onDispose();
67
+ this._profile.removeChangeListener(this._subsurfaceProfileChanged);
68
+ this._profile.dispose();
69
+ }
70
+ /**
71
+ * Marker used by the forward render graph to enable the PostSSS post effect.
72
+ *
73
+ * @remarks
74
+ * This no longer allocates a side buffer: the scattering source is recovered
75
+ * from `SceneColor` and its diffuse-luminance alpha.
76
+ */ get postSSS() {
69
77
  return true;
70
78
  }
79
+ /**
80
+ * The profile driving this material's subsurface scattering.
81
+ *
82
+ * @remarks
83
+ * Read-only and never null: the material creates one in its constructor and
84
+ * owns it for life, so edit it in place rather than assigning a new one.
85
+ *
86
+ * ```ts
87
+ * material.subsurfaceProfile.preset = 'jade';
88
+ * material.subsurfaceProfile.meanFreePathDistance = 0.03;
89
+ * ```
90
+ *
91
+ * Per-material ownership is what lets face, ears and lips scatter differently
92
+ * in a single screen-space pass: the material writes its profile's id per pixel
93
+ * and the diffusion looks the parameters up from there. Profiles are therefore
94
+ * not shared - to transfer a look, use {@link SSSProfile.copyFrom}.
95
+ *
96
+ * @public
97
+ */ get subsurfaceProfile() {
98
+ return this._profile;
99
+ }
100
+ /**
101
+ * Keeps the diffuse luminance written to `SceneColor.a` instead of letting the
102
+ * opaque path overwrite it with 1. {@link PostSSS} needs it to separate the
103
+ * diffusible energy from the specular it must leave untouched.
104
+ */ preservesOpaqueAlpha(ctx) {
105
+ return ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT;
106
+ }
107
+ /**
108
+ * Keeps `SceneColor.a` additive across the multi-pass light loop.
109
+ *
110
+ * @remarks
111
+ * The base implementation uses `zero/one` for alpha so that additive light
112
+ * passes preserve whatever the base pass wrote. That is wrong here: the alpha
113
+ * carries the diffuse luminance of the accumulated RGB, so it has to be summed
114
+ * alongside it — otherwise `SceneColor.a / luma(SceneColor.rgb)` under-reports
115
+ * the diffusible fraction as soon as a second light contributes, and the SSS
116
+ * passes would scatter only the first light's diffuse energy.
117
+ */ updateRenderStates(pass, stateSet, ctx) {
118
+ super.updateRenderStates(pass, stateSet, ctx);
119
+ if (ctx.lightBlending && ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT) {
120
+ const blendingState = stateSet.useBlendingState();
121
+ if (blendingState.enabled) {
122
+ blendingState.setBlendFuncAlpha('one', 'one');
123
+ }
124
+ }
125
+ }
71
126
  clone() {
72
- const other = new SkinMaterial();
127
+ const other = new SSSMaterial();
73
128
  other.copyFrom(this);
74
129
  return other;
75
130
  }
76
131
  copyFrom(other) {
77
132
  super.copyFrom(other);
133
+ // The values, not the profile: a clone owns its own row of the table, so
134
+ // that disposing either material cannot pull the parameters out from under
135
+ // the other.
136
+ this._profile.copyFrom(other._profile);
78
137
  this.vertexNormal = other.vertexNormal;
79
138
  this.vertexTangent = other.vertexTangent;
80
- this.shininess = other.shininess;
81
- this.specularStrength = other.specularStrength;
82
- this.diffuseWrap = other.diffuseWrap;
83
- this.diffuseSoftness = other.diffuseSoftness;
84
- this.scatterWrap = other.scatterWrap;
85
- this.scatterStrength = other.scatterStrength;
86
- this.scatterColor = other.scatterColor;
139
+ this.roughness = other.roughness;
140
+ this.specularF0 = other.specularF0;
87
141
  this.transmissionStrength = other.transmissionStrength;
88
- this.transmissionPower = other.transmissionPower;
89
- this.shadowTint = other.shadowTint;
90
- this.brightening = other.brightening;
91
142
  }
92
- /** true if vertex normal attribute presents */ get vertexNormal() {
93
- return this.featureUsed(SkinMaterial.FEATURE_VERTEX_NORMAL);
143
+ get vertexNormal() {
144
+ return this.featureUsed(SSSMaterial.FEATURE_VERTEX_NORMAL);
94
145
  }
95
146
  set vertexNormal(val) {
96
- this.useFeature(SkinMaterial.FEATURE_VERTEX_NORMAL, !!val);
147
+ this.useFeature(SSSMaterial.FEATURE_VERTEX_NORMAL, !!val);
97
148
  }
98
- /** true if vertex tangent attribute presents */ get vertexTangent() {
99
- return this.featureUsed(SkinMaterial.FEATURE_VERTEX_TANGENT);
149
+ get vertexTangent() {
150
+ return this.featureUsed(SSSMaterial.FEATURE_VERTEX_TANGENT);
100
151
  }
101
152
  set vertexTangent(val) {
102
- this.useFeature(SkinMaterial.FEATURE_VERTEX_TANGENT, !!val);
103
- }
104
- /** Blinn specular exponent. Higher values produce smaller highlights. */ get shininess() {
105
- return this._shininess;
106
- }
107
- set shininess(val) {
108
- const next = Math.max(1, val ?? 1);
109
- if (next !== this._shininess) {
110
- this._shininess = next;
111
- this.uniformChanged();
112
- }
113
- }
114
- /** Direct specular strength. Keep this restrained for soft skin. */ get specularStrength() {
115
- return this._specularStrength;
116
- }
117
- set specularStrength(val) {
118
- const next = Math.max(0, val ?? 0);
119
- if (next !== this._specularStrength) {
120
- this._specularStrength = next;
121
- this.uniformChanged();
122
- }
123
- }
124
- /** Wrap amount for visible diffuse lighting. */ get diffuseWrap() {
125
- return this._diffuseWrap;
126
- }
127
- set diffuseWrap(val) {
128
- const next = Math.max(0, val ?? 0);
129
- if (next !== this._diffuseWrap) {
130
- this._diffuseWrap = next;
153
+ this.useFeature(SSSMaterial.FEATURE_VERTEX_TANGENT, !!val);
154
+ }
155
+ /**
156
+ * GGX base roughness the two specular lobes are derived from.
157
+ *
158
+ * @remarks
159
+ * Defaults to UE5's material default of 0.5. The profile then tightens the
160
+ * narrow lobe and broadens the wide one around this value, so it is the centre
161
+ * of the dual-lobe highlight rather than the roughness of either lobe.
162
+ */ get roughness() {
163
+ return this._roughness;
164
+ }
165
+ set roughness(val) {
166
+ const next = Math.max(0.045, Math.min(1, val ?? 0.5));
167
+ if (next !== this._roughness) {
168
+ this._roughness = next;
131
169
  this.uniformChanged();
132
170
  }
133
171
  }
134
- /** Blend from hard Lambert lighting to wrapped diffuse lighting. */ get diffuseSoftness() {
135
- return this._diffuseSoftness;
136
- }
137
- set diffuseSoftness(val) {
138
- const next = Math.max(0, Math.min(1, val ?? 0));
139
- if (next !== this._diffuseSoftness) {
140
- this._diffuseSoftness = next;
141
- this.uniformChanged();
142
- }
143
- }
144
- /**
145
- * Wider wrap that used to shape the post-process scattering source.
146
- *
147
- * @deprecated No longer has any effect. The side buffer now carries the real
148
- * diffuse term rather than a hand-built difference that was nonzero only near
149
- * the terminator, so there is nothing for a second wrap to shape. Use
150
- * {@link SkinSSS.scatterRadius} and the profile's channel radii to control how
151
- * far light travels, and {@link SkinMaterial.diffuseWrap} to shape the ramp.
152
- */ get scatterWrap() {
153
- return this._scatterWrap;
154
- }
155
- set scatterWrap(val) {
156
- const next = Math.max(0, val ?? 0);
157
- if (next !== this._scatterWrap) {
158
- this._scatterWrap = next;
159
- this.uniformChanged();
160
- }
161
- }
162
- /**
163
- * Strength of the multiplier written into the SkinSSS side buffer.
164
- *
165
- * @deprecated No longer has any effect. Scaling the side buffer would break
166
- * the identity that makes the diffusion energy conserving - it must equal the
167
- * diffusible term already present in the lit color. Use
168
- * {@link SkinSSS.strength} for the overall amount and {@link SkinSSS.glow} for
169
- * an additive, non-conserving bleed on top.
170
- */ get scatterStrength() {
171
- return this._scatterStrength;
172
- }
173
- set scatterStrength(val) {
174
- const next = Math.max(0, val ?? 0);
175
- if (next !== this._scatterStrength) {
176
- this._scatterStrength = next;
172
+ /**
173
+ * Fresnel reflectance at normal incidence.
174
+ *
175
+ * @remarks
176
+ * Defaults to 0.04, what UE5's default `Specular` of 0.5 gives through
177
+ * `F0 = 0.08 * Specular`; to port a UE5 material, use that conversion. This is
178
+ * the only control over highlight strength, since a post-multiplier would skew
179
+ * the grazing end of the Fresnel curve and bypass the energy terms, so the
180
+ * specular would stop paying for itself out of the diffuse.
181
+ *
182
+ * The profile's IOR does not feed this: that drives transmission's refraction,
183
+ * while the specular Fresnel stays on the dielectric mapping.
184
+ */ get specularF0() {
185
+ return this._specularF0;
186
+ }
187
+ set specularF0(val) {
188
+ const next = Math.max(0, Math.min(0.2, val ?? 0.04));
189
+ if (next !== this._specularF0) {
190
+ this._specularF0 = next;
177
191
  this.uniformChanged();
178
192
  }
179
193
  }
180
- /**
181
- * Strength of the back-lit transmission term. Requires a `subsurfaceTexture`
182
- * with thickness in the B channel; 0 (the default) disables transmission.
194
+ /**
195
+ * Overall multiplier on the back-lit transmission.
196
+ *
197
+ * @remarks
198
+ * UE5 has no equivalent - its transmission is fully determined by the profile
199
+ * and the measured thickness - so 1 is the faithful value and this exists only
200
+ * to dial the effect back or push it further.
201
+ *
202
+ * Transmission additionally requires {@link PunctualLight.transmission} on at
203
+ * least one shadow-casting light, since the thickness it needs is measured
204
+ * against that light's shadow map. With no such light this has no effect.
205
+ *
206
+ * @public
183
207
  */ get transmissionStrength() {
184
208
  return this._transmissionStrength;
185
209
  }
186
210
  set transmissionStrength(val) {
187
- const next = Math.max(0, val ?? 0);
211
+ const next = Math.max(0, val ?? 1);
188
212
  if (next !== this._transmissionStrength) {
189
213
  this._transmissionStrength = next;
190
214
  this.uniformChanged();
191
215
  }
192
216
  }
193
- /** Exponent of the back-lit transmission falloff. Higher values tighten the glow. */ get transmissionPower() {
194
- return this._transmissionPower;
195
- }
196
- set transmissionPower(val) {
197
- const next = Math.max(1, val ?? 1);
198
- if (next !== this._transmissionPower) {
199
- this._transmissionPower = next;
200
- this.uniformChanged();
201
- }
202
- }
203
- /**
204
- * Warm tint for the blurred skin lighting contribution.
205
- *
206
- * @deprecated No longer has any effect; moved to {@link SkinSSS.scatterTint}.
207
- * Tinting here would tint the diffuse in the lit color too, which is what made
208
- * the old look read as a red haze over the whole face. On the post effect the
209
- * tint applies only to the light that actually moved, and per-channel scatter
210
- * radii from the profile already produce the red bleed on their own.
211
- */ get scatterColor() {
212
- return this._scatterColor;
213
- }
214
- set scatterColor(val) {
215
- if (!val.equalsTo(this._scatterColor)) {
216
- this._scatterColor.set(val);
217
- this.uniformChanged();
218
- }
219
- }
220
- /**
221
- * NPR-style shadow tint: the dark end of the diffuse ramp lifts toward this
222
- * color instead of black (black, the default, reproduces plain wrapped
223
- * lambert). A rosy/lavender tint gives the stylized beauty-shot shadow look.
224
- */ get shadowTint() {
225
- return this._shadowTint;
226
- }
227
- set shadowTint(val) {
228
- if (!val.equalsTo(this._shadowTint)) {
229
- this._shadowTint.set(val);
230
- this.uniformChanged();
231
- }
232
- }
233
- /** Whitening gain applied to the whole diffuse response. 0 (the default) is neutral. */ get brightening() {
234
- return this._brightening;
235
- }
236
- set brightening(val) {
237
- const next = Math.max(0, val ?? 0);
238
- if (next !== this._brightening) {
239
- this._brightening = next;
240
- this.uniformChanged();
241
- }
242
- }
243
217
  vertexShader(scope) {
244
218
  super.vertexShader(scope);
245
219
  const pb = scope.$builder;
@@ -260,20 +234,30 @@ import { RENDER_PASS_TYPE_LIGHT, LIGHT_TYPE_POINT, MaterialVaryingFlags } from '
260
234
  super.fragmentShader(scope);
261
235
  const pb = scope.$builder;
262
236
  const that = this;
237
+ const lightPass = this.drawContext.renderPass.type === RENDER_PASS_TYPE_LIGHT;
238
+ // Declared outside `needFragmentColorInput` deliberately: that predicate is
239
+ // false in the prepass for an ordinary opaque material, so a declaration
240
+ // inside it would leave the uniform undefined and the prepass would write id
241
+ // 0 for every skin pixel.
242
+ const depthPassProfileId = this.drawContext.renderPass.type === RENDER_PASS_TYPE_DEPTH && this.drawContext.sssProfileId;
243
+ if (lightPass || depthPassProfileId) {
244
+ scope.zSSSProfileId = pb.float().uniform(2);
245
+ }
263
246
  if (this.needFragmentColorInput()) {
264
- if (this.drawContext.renderPass.type === RENDER_PASS_TYPE_LIGHT) {
265
- scope.zSkinShininess = pb.float().uniform(2);
266
- scope.zSkinSpecularStrength = pb.float().uniform(2);
267
- scope.zSkinDiffuseWrap = pb.float().uniform(2);
268
- scope.zSkinDiffuseSoftness = pb.float().uniform(2);
269
- scope.zSkinShadowTint = pb.vec4().uniform(2);
270
- scope.zSkinBrightening = pb.float().uniform(2);
271
- // Encodes HDR scatter irradiance into the SkinSSS buffer range when the
272
- // render graph falls back to rgba8unorm (see getSSSLightingTextureFormat).
273
- scope.zSkinScatterEncodeScale = pb.float().uniform(2);
274
- if (this.subsurfaceTexture) {
275
- scope.zSkinTransmissionStrength = pb.float().uniform(2);
276
- scope.zSkinTransmissionPower = pb.float().uniform(2);
247
+ if (lightPass) {
248
+ scope.zSSSRoughness = pb.float().uniform(2);
249
+ scope.zSSSSpecularF0 = pb.float().uniform(2);
250
+ scope.zSSSLobeParams = pb.vec3().uniform(2);
251
+ // Transmission is compiled in only when the thickness it needs exists.
252
+ // The flag is part of the shader's cache key (render/lightpass.ts), and
253
+ // it also guards against the per-light additive path, which has no
254
+ // thickness texture and hands the BxDF a placeholder.
255
+ if (this.drawContext.transmissionThickness) {
256
+ scope.zSSSTransmissionStrength = pb.float().uniform(2);
257
+ // rgba32f, so WebGPU will only accept a non-filtering sampler here;
258
+ // the transmission profile is interpolated by hand for that reason.
259
+ scope.zSSSProfileTex = pb.tex2D().sampleType('unfilterable-float').uniform(2);
260
+ scope.zSSSProfileTexelSize = pb.vec2().uniform(2);
277
261
  }
278
262
  }
279
263
  scope.$l.albedo = this.calculateAlbedoColor(scope);
@@ -282,34 +266,53 @@ import { RENDER_PASS_TYPE_LIGHT, LIGHT_TYPE_POINT, MaterialVaryingFlags } from '
282
266
  }
283
267
  if (this.drawContext.renderPass.type === RENDER_PASS_TYPE_LIGHT) {
284
268
  const baseLightPass = !this.drawContext.lightBlending;
285
- // TBN[2] is the pre-normal-map geometric normal, needed for shadow
286
- // normal offset bias. Feeding the normal mapped shading normal instead
287
- // would displace the shadow lookup along pore detail.
288
269
  scope.$l.normalInfo = this.calculateNormalAndTBN(scope, scope.$inputs.worldPos, scope.$inputs.wNorm, scope.$inputs.wTangent, scope.$inputs.wBinormal);
289
270
  scope.$l.normal = scope.normalInfo.normal;
290
271
  scope.$l.viewVec = this.calculateViewVector(scope, scope.$inputs.worldPos);
291
- scope.$l.roughness = pb.sqrt(pb.div(2, pb.add(scope.zSkinShininess, 2)));
272
+ scope.$l.roughness = scope.zSSSRoughness;
292
273
  scope.$l.skinMask = pb.float(1);
293
- scope.$l.skinSoftness = pb.float(0);
294
- scope.$l.skinThickness = pb.float(0);
295
274
  if (this.subsurfaceTexture) {
296
275
  scope.$l.subsurfaceTexel = this.sampleSubsurfaceTexture(scope);
297
276
  scope.skinMask = pb.clamp(scope.subsurfaceTexel.r, 0, 1);
298
- scope.skinSoftness = pb.clamp(scope.subsurfaceTexel.g, 0, 1);
299
- scope.skinThickness = pb.clamp(scope.subsurfaceTexel.b, 0, 1);
300
277
  }
301
278
  scope.$l.diffuseLighting = pb.vec3(0);
302
279
  scope.$l.transmissionLighting = pb.vec3(0);
303
280
  scope.$l.specularLighting = pb.vec3(0);
304
- scope.$l.NoV = pb.clamp(pb.dot(scope.normal, scope.viewVec), 0, 1);
281
+ scope.$l.envSpecular = pb.vec3(0);
282
+ scope.$l.NoV = pb.clamp(pb.dot(scope.normal, scope.viewVec), 0.0001, 1);
283
+ // The lobe roughnesses depend only on the material and profile, so they
284
+ // are resolved once rather than per light. The skin mask stands in for
285
+ // UE5's per-pixel subsurface opacity, which is what fades the dual lobe
286
+ // out where the surface stops being skin.
287
+ scope.$l.lobeRoughness = skinDualSpecularRoughness(scope, scope.roughness, scope.skinMask, scope.zSSSLobeParams.x, scope.zSSSLobeParams.y);
288
+ // Multiple-scattering compensation. UE5 takes the average lobe roughness
289
+ // here rather than computing a term per lobe, and applies the result to
290
+ // both the direct and the ambient contribution.
291
+ scope.$l.avgLobeRoughness = pb.mix(scope.lobeRoughness.x, scope.lobeRoughness.y, scope.zSSSLobeParams.z);
292
+ scope.$l.energyTerms = skinSpecularEnergyTerms(scope, scope.avgLobeRoughness, scope.NoV, scope.zSSSSpecularF0);
293
+ // x scales specular back up to unit albedo; 1 - y is the energy left for
294
+ // the diffuse underneath.
295
+ scope.$l.energyConservation = scope.energyTerms.x;
296
+ scope.$l.energyPreservation = pb.sub(1, scope.energyTerms.y);
297
+ // --- Environment lighting ---
305
298
  if (this.needCalculateEnvLight() && baseLightPass) {
306
299
  scope.$l.envDiffuse = this.getEnvLightIrradiance(scope, scope.normal);
307
300
  scope.diffuseLighting = pb.add(scope.diffuseLighting, scope.envDiffuse);
308
301
  scope.$l.reflectVec = this.calculateReflectionVector(scope, scope.normal, scope.viewVec);
309
- scope.$l.envFresnel = pb.add(0.028, pb.mul(0.972, pb.pow(pb.sub(1, scope.NoV), 5)));
310
- scope.specularLighting = pb.add(scope.specularLighting, pb.mul(this.getEnvLightRadiance(scope, scope.reflectVec, scope.roughness), scope.envFresnel, scope.zSkinSpecularStrength));
302
+ // UE5's ReflectionEnvironment:
303
+ //
304
+ // EnergyTerms = ComputeGGXSpecEnergyTerms(GBuffer.Roughness, NoV, SpecularColor)
305
+ // Color.rgb = GatherRadiance(R, GBuffer.Roughness) * EnergyTerms.E
306
+ //
307
+ // Two consequences. The roughness is the raw **material** value, since
308
+ // UE5 applies the dual lobe only in the direct BxDF. And the weight is
309
+ // the directional albedo `E` - the split-sum DFG with multiple-scattering
310
+ // folded in, not a bare Fresnel - which already carries the multi-scatter
311
+ // gain, so it must not also be multiplied by the direct path's `W`.
312
+ scope.$l.envEnergyTerms = skinSpecularEnergyTerms(scope, scope.roughness, scope.NoV, scope.zSSSSpecularF0);
313
+ scope.envSpecular = pb.mul(this.getEnvLightRadiance(scope, scope.reflectVec, scope.roughness), scope.envEnergyTerms.y);
311
314
  }
312
- this.forEachLight(scope, function(type, posRange, dirCutoff, colorIntensity, extra, shadow) {
315
+ this.forEachLight(scope, function(type, posRange, dirCutoff, colorIntensity, extra, shadow, thickness) {
313
316
  this.$l.diffuseScale = pb.float(1);
314
317
  this.$l.specularScale = pb.float(1);
315
318
  this.$l.sourceRadiusFactor = pb.float(0);
@@ -320,76 +323,88 @@ import { RENDER_PASS_TYPE_LIGHT, LIGHT_TYPE_POINT, MaterialVaryingFlags } from '
320
323
  });
321
324
  this.$l.lightAtten = that.calculateLightAttenuation(this, type, this.$inputs.worldPos, posRange, dirCutoff, extra);
322
325
  this.$l.lightDir = that.calculateLightDirection(this, type, this.$inputs.worldPos, posRange, dirCutoff);
323
- this.$l.rawNoL = pb.dot(this.normal, this.lightDir);
324
- this.$l.NoL = pb.clamp(this.rawNoL, 0, 1);
325
- this.$l.NoLWrap = pb.clamp(pb.div(pb.add(this.rawNoL, this.zSkinDiffuseWrap), pb.add(1, this.zSkinDiffuseWrap)), 0, 1);
326
- // calculateShadow() samples with implicit derivatives (dpdx); WGSL
327
- // requires the call to stay in uniform control flow, so never wrap
328
- // it in a dynamic branch such as NoLWrap > 0.
329
- //
330
- // Diffuse and specular share this term unmodified. An earlier version
331
- // faded it toward 1 around the terminator to hide shadow map acne
332
- // there, but that released the shadow in the wrong band (only ~9% at
333
- // NdotL = 0, by which point the acne is at its worst) and made cast
334
- // shadows dissolve wherever a surface turned away from the light.
335
- // Normal offset bias removes the acne at the source instead.
336
- this.$l.shadowTerm = shadow ? that.calculateShadow(this, this.$inputs.worldPos, scope.normalInfo.TBN[2], pb.max(this.NoL, 1e-5)) : pb.float(1);
326
+ this.$l.rawNdotL = pb.dot(this.normal, this.lightDir);
327
+ this.$l.NoL = pb.clamp(this.rawNdotL, 0, 1);
328
+ this.$l.VdotL = pb.dot(this.viewVec, this.lightDir);
329
+ // Shadow: pre-integrated BRDF handles NdotL internally, so pass a
330
+ // fixed bias to keep normal-offset stable at the terminator.
331
+ this.$l.shadowTerm = shadow ? that.calculateShadow(this, this.$inputs.worldPos, scope.normalInfo.TBN[2], pb.float(0.5)) : pb.float(1);
337
332
  this.$l.lightColor = pb.mul(colorIntensity.rgb, colorIntensity.a, this.lightAtten);
338
- this.$l.diffuseLightColor = pb.mul(this.lightColor, this.shadowTerm);
339
333
  this.$l.halfVec = pb.normalize(pb.add(this.viewVec, this.lightDir));
340
334
  this.$l.NoH = pb.clamp(pb.dot(this.normal, this.halfVec), 0, 1);
341
- this.$l.LoH = pb.clamp(pb.dot(this.lightDir, this.halfVec), 0, 1);
342
- this.$l.pointShininess = pb.max(pb.div(this.zSkinShininess, pb.add(1, pb.mul(this.sourceRadiusFactor, 32))), 1);
343
- this.$l.softness = pb.clamp(pb.add(this.zSkinDiffuseSoftness, pb.mul(this.skinSoftness, 0.35)), 0, 1);
344
- this.$l.NoLVis = pb.mix(this.NoL, this.NoLWrap, this.softness);
345
- // NPR-style diffuse ramp: the dark end lifts toward the shadow tint
346
- // instead of black. A black tint reproduces plain wrapped lambert.
347
- this.$l.diffuseRamp = pb.mix(this.zSkinShadowTint.rgb, pb.vec3(1), this.NoLVis);
348
- this.diffuseLighting = pb.add(this.diffuseLighting, pb.mul(this.diffuseLightColor, this.diffuseRamp, this.diffuseScale, 1 / Math.PI));
349
- if (that.subsurfaceTexture) {
350
- // Back-lit transmission: thin regions (B channel of the subsurface
351
- // texture) glow when the light faces the camera through the
352
- // surface.
353
- //
354
- // This deliberately uses the unshadowed lightColor. A back-lit
355
- // fragment is self-shadowed by definition - its own front face is
356
- // the occluder - so multiplying by the shadow term would cancel
357
- // exactly the case the term exists to model. The cost is that an
358
- // external caster does not block the glow, which a shadow map
359
- // cannot distinguish from self occlusion anyway.
360
- this.$l.transmission = pb.mul(pb.pow(pb.clamp(pb.dot(pb.neg(this.lightDir), this.viewVec), 0, 1), this.zSkinTransmissionPower), this.skinThickness, this.zSkinTransmissionStrength);
361
- this.transmissionLighting = pb.add(this.transmissionLighting, pb.mul(this.lightColor, this.transmission, this.diffuseScale, 1 / Math.PI));
335
+ this.$l.VoH = pb.clamp(pb.dot(this.viewVec, this.halfVec), 0, 1);
336
+ // Burley diffuse with the NoL cosine, as UE5's SubsurfaceProfileBxDF
337
+ // evaluates it. The soft terminator is the screen-space diffusion's job;
338
+ // bending this term to fake it double-counts the effect.
339
+ this.$l.skinDiff = skinDiffuseBRDF(this, this.NoV, this.NoL, this.VoH, this.roughness);
340
+ this.diffuseLighting = pb.add(this.diffuseLighting, pb.mul(this.lightColor, this.shadowTerm, this.skinDiff, this.NoL, this.diffuseScale));
341
+ // Back-lit transmission, after UE5's SubsurfaceProfileBxDF. UE5
342
+ // attenuates by the *transmission* shadow, which is the encoded
343
+ // optical depth itself - so the same value both indexes the profile
344
+ // and scales it, which is why `thickness` appears twice.
345
+ //
346
+ // No surface shadow and no NoL: the light arrives from behind, so the
347
+ // camera-facing surface is shadowed and turned away from it by
348
+ // construction. Applying either would zero out exactly the pixels
349
+ // this term exists for.
350
+ if (that.drawContext.transmissionThickness) {
351
+ this.$l.transmission = skinTransmission(this, this.zSSSProfileTex, this.zSSSProfileTexelSize, this.zSSSProfileId, thickness, this.normal, this.viewVec, this.lightDir);
352
+ this.transmissionLighting = pb.add(this.transmissionLighting, pb.mul(this.lightColor, this.transmission, thickness, this.zSSSTransmissionStrength, this.diffuseScale));
362
353
  }
363
- // Normalized Blinn with Schlick Fresnel (skin F0 = 0.028). Specular
364
- // keeps the unfaded shadow term and hard NoL masking.
365
- this.$l.specNormalization = pb.div(pb.add(this.pointShininess, 8), 8 * Math.PI);
366
- this.$l.specFresnel = pb.add(0.028, pb.mul(0.972, pb.pow(pb.sub(1, this.LoH), 5)));
367
- this.$l.specular = pb.mul(this.lightColor, this.shadowTerm, pb.pow(this.NoH, this.pointShininess), this.specNormalization, this.specFresnel, this.zSkinSpecularStrength, this.specularScale, this.NoL);
368
- this.specularLighting = pb.add(this.specularLighting, this.specular);
354
+ // Dual-lobe GGX specular, with the NoL cosine UE5 applies alongside it.
355
+ this.$l.spec = skinDualLobeSpecular(this, this.NoH, this.NoV, this.NoL, this.VoH, this.lobeRoughness, this.zSSSLobeParams.z, this.zSSSSpecularF0);
356
+ this.specularLighting = pb.add(this.specularLighting, pb.mul(this.lightColor, this.shadowTerm, this.spec, this.NoL, this.specularScale));
369
357
  });
370
- // Whitening: lift the whole diffuse response (direct and ambient).
371
- scope.diffuseLighting = pb.mul(scope.diffuseLighting, pb.add(1, scope.zSkinBrightening));
372
- // Everything that scatters through the surface: the stylized diffuse
373
- // ramp plus back-lit transmission. Specular stays out - it reflects off
374
- // the oil layer and never enters the skin.
375
- scope.$l.diffusible = pb.mul(scope.albedo.rgb, pb.add(scope.diffuseLighting, scope.transmissionLighting));
358
+ // --- Assemble ---
359
+ //
360
+ // UE5 applies the energy terms to the accumulated lighting rather than per
361
+ // light: the diffuse loses what the specular layer above it reflected, and
362
+ // the specular gains back the energy single-scattering GGX dropped. The
363
+ // transmission is left alone, since it enters from behind the surface and
364
+ // never passes through that specular layer.
365
+ //
366
+ // The environment specular stays outside that multiply: it was already
367
+ // weighted by the directional albedo `E` when gathered, which carries the
368
+ // multiple-scattering gain, so applying `W` on top would count it twice.
369
+ // UE5 keeps the same split.
370
+ //
371
+ // The albedo multiply has to cover the transmission as well, because the
372
+ // baked profile carries only the falloff shape - UE5 supplies the base
373
+ // colour in its recombine pass instead, and this engine supplies it here.
374
+ //
375
+ // The transmission then rides into `diffLum` below and so is diffused
376
+ // along with the rest. That is deliberate: UE5 passes it as the
377
+ // `ScatterableLight` argument of `LightAccumulator_AddSplit`, the same
378
+ // slot the diffuse uses.
379
+ scope.$l.diffusible = pb.mul(scope.albedo.rgb, pb.add(pb.mul(scope.diffuseLighting, scope.energyPreservation), scope.transmissionLighting));
380
+ scope.specularLighting = pb.add(pb.mul(scope.specularLighting, scope.energyConservation), scope.envSpecular);
376
381
  scope.$l.litColor = pb.add(scope.diffusible, scope.specularLighting);
377
- // The SkinSSS side buffer carries exactly the diffusible term that is
378
- // also in litColor - untinted and unscaled. That identity is what lets
379
- // the post effect subtract it and add back a diffused version, which is
380
- // energy conserving by construction and leaves the image untouched when
381
- // the effect is disabled. Tinting or scaling here would break it; those
382
- // controls live on SkinSSS instead.
382
+ // SceneColor.a = diffuse luminance, UE5's spec/diff separation mechanism:
383
+ // the SSS passes recover the diffusible fraction as
384
+ // `saturate(SceneColor.a / luma(SceneColor.rgb))`, so the alpha must carry
385
+ // the *diffuse* luminance, not the total.
386
+ scope.$l.diffLum = pb.dot(scope.diffusible, pb.vec3(0.2126, 0.7152, 0.0722));
387
+ // Surface data for the SSS passes: rgb = world normal, a = subsurface
388
+ // opacity.
389
+ //
390
+ // SceneColor.a cannot serve as the mask: every opaque material writes 1
391
+ // there, so gating on it would make the diffusion treat the background and
392
+ // the eyes as skin.
393
+ //
394
+ // The normal rides along so the diffusion can weight taps by how far the
395
+ // surface has turned away. Carrying it here rather than reading SceneNormal
396
+ // makes scattering independent of whether that optional MRT is enabled.
383
397
  //
384
- // Base and additive light passes both write their own contribution; the
385
- // additive blend (RGB one/one, alpha zero/one) accumulates RGB and keeps
386
- // the mask written by the base pass in alpha.
387
- scope.$l.skinSSS = pb.vec4(pb.mul(scope.diffusible, scope.zSkinScatterEncodeScale), scope.skinMask);
398
+ // The alpha is UE5's Opacity input unmodified - a continuous 0..1 mask
399
+ // where 0 is no scattering and 1 is full. The profile id travels
400
+ // separately, out of the depth prepass, since one channel cannot carry a
401
+ // table row and a weight at once.
402
+ scope.$l.postSSSMask = pb.vec4(pb.add(pb.mul(scope.normal, 0.5), pb.vec3(0.5)), scope.skinMask);
388
403
  if (this.drawContext.materialFlags & (MaterialVaryingFlags.SCENE_STORE_ROUGHNESS | MaterialVaryingFlags.SCENE_STORE_NORMAL)) {
389
- scope.$l.outRoughness = pb.vec4(pb.mul(scope.albedo.rgb, pb.sub(1, scope.roughness), scope.zSkinSpecularStrength), pb.mul(scope.roughness, ShaderHelper.getCameraRoughnessFactor(scope)));
390
- this.outputFragmentColor(scope, scope.$inputs.worldPos, pb.vec4(scope.litColor, scope.albedo.a), scope.outRoughness, pb.vec4(pb.add(pb.mul(scope.normal, 0.5), pb.vec3(0.5)), 1), undefined, undefined, undefined, undefined, false, scope.skinSSS);
404
+ scope.$l.outRoughness = pb.vec4(pb.mul(scope.albedo.rgb, pb.sub(1, scope.roughness)), pb.mul(scope.roughness, ShaderHelper.getCameraRoughnessFactor(scope)));
405
+ this.outputFragmentColor(scope, scope.$inputs.worldPos, pb.vec4(scope.litColor, scope.diffLum), scope.outRoughness, pb.vec4(pb.add(pb.mul(scope.normal, 0.5), pb.vec3(0.5)), 1), undefined, undefined, undefined, undefined, false, scope.skinSSSMask);
391
406
  } else {
392
- this.outputFragmentColor(scope, scope.$inputs.worldPos, pb.vec4(scope.litColor, scope.albedo.a), undefined, undefined, undefined, undefined, undefined, undefined, false, scope.skinSSS);
407
+ this.outputFragmentColor(scope, scope.$inputs.worldPos, pb.vec4(scope.litColor, scope.diffLum), undefined, undefined, undefined, undefined, undefined, undefined, false, scope.skinSSSMask);
393
408
  }
394
409
  } else {
395
410
  this.outputFragmentColor(scope, scope.$inputs.worldPos, scope.albedo);
@@ -398,24 +413,43 @@ import { RENDER_PASS_TYPE_LIGHT, LIGHT_TYPE_POINT, MaterialVaryingFlags } from '
398
413
  this.outputFragmentColor(scope, scope.$inputs.worldPos, null);
399
414
  }
400
415
  }
416
+ /**
417
+ * The profile id the depth prepass writes for this material.
418
+ *
419
+ * @remarks
420
+ * Overrides {@link MeshMaterial.getDepthPassProfileId}. The uniform is declared
421
+ * in {@link SSSMaterial.fragmentShader} for the prepass as well as the light
422
+ * pass, and bound below.
423
+ */ getDepthPassProfileId(scope) {
424
+ return scope.zSSSProfileId;
425
+ }
401
426
  applyUniformValues(bindGroup, ctx, pass) {
402
427
  super.applyUniformValues(bindGroup, ctx, pass);
403
- if (this.needFragmentColor(ctx) && ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT) {
404
- bindGroup.setValue('zSkinShininess', this._shininess);
405
- bindGroup.setValue('zSkinSpecularStrength', this._specularStrength);
406
- bindGroup.setValue('zSkinDiffuseWrap', this._diffuseWrap);
407
- bindGroup.setValue('zSkinDiffuseSoftness', this._diffuseSoftness);
408
- bindGroup.setValue('zSkinShadowTint', this._shadowTint);
409
- bindGroup.setValue('zSkinBrightening', this._brightening);
410
- const ldrSkinSSS = ctx.SkinSSSTexture && ctx.SkinSSSTexture.format === 'rgba8unorm';
411
- bindGroup.setValue('zSkinScatterEncodeScale', ldrSkinSSS ? 1 / SKIN_SSS_LDR_ENCODE_RANGE : 1);
412
- if (this.subsurfaceTexture) {
413
- bindGroup.setValue('zSkinTransmissionStrength', this._transmissionStrength);
414
- bindGroup.setValue('zSkinTransmissionPower', this._transmissionPower);
428
+ const lightPass = ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT;
429
+ const depthPassProfileId = ctx.renderPass.type === RENDER_PASS_TYPE_DEPTH && ctx.sssProfileId;
430
+ // Not gated on needFragmentColor: the prepass declares this uniform for
431
+ // every skin material, including the opaque ones that predicate excludes.
432
+ if (lightPass || depthPassProfileId) {
433
+ bindGroup.setValue('zSSSProfileId', this._profile.encodedId);
434
+ }
435
+ if (!this.needFragmentColor(ctx) || !lightPass) {
436
+ return;
437
+ }
438
+ bindGroup.setValue('zSSSRoughness', this._roughness);
439
+ bindGroup.setValue('zSSSSpecularF0', this._specularF0);
440
+ const profile = this._profile;
441
+ bindGroup.setValue('zSSSLobeParams', this._lobeParams.setXYZ(profile.roughness0, profile.roughness1, profile.lobeMix));
442
+ if (ctx.transmissionThickness) {
443
+ bindGroup.setValue('zSSSTransmissionStrength', this._transmissionStrength);
444
+ const table = SSSProfile.getTable(ctx.device);
445
+ if (table) {
446
+ bindGroup.setTexture('zSSSProfileTex', table, fetchSampler('clamp_nearest_nomip'));
447
+ this._profileTexelSize.setXY(1 / SSSProfile.tableColumns, 1 / SSSProfile.tableRows);
448
+ bindGroup.setValue('zSSSProfileTexelSize', this._profileTexelSize);
415
449
  }
416
450
  }
417
451
  }
418
452
  }
419
453
 
420
- export { SKIN_SSS_LDR_ENCODE_RANGE, SkinMaterial };
454
+ export { SKIN_SSS_LDR_ENCODE_RANGE, SSSMaterial };
421
455
  //# sourceMappingURL=skin.js.map