@zephyr3d/scene 0.9.26 → 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.
- package/README.md +60 -27
- package/dist/animation/animation_bank.js +634 -0
- package/dist/animation/animation_bank.js.map +1 -0
- package/dist/animation/animation_bank_gpu.js +256 -0
- package/dist/animation/animation_bank_gpu.js.map +1 -0
- package/dist/animation/animationset.js +146 -30
- package/dist/animation/animationset.js.map +1 -1
- package/dist/animation/cloth/gpu_cloth_component.js +406 -0
- package/dist/animation/cloth/gpu_cloth_component.js.map +1 -0
- package/dist/animation/cloth/gpu_cloth_system.js +332 -202
- package/dist/animation/cloth/gpu_cloth_system.js.map +1 -1
- package/dist/animation/gpu_animation_driver.js +289 -0
- package/dist/animation/gpu_animation_driver.js.map +1 -0
- package/dist/animation/joint_dynamics/collision.js +112 -1
- package/dist/animation/joint_dynamics/collision.js.map +1 -1
- package/dist/animation/joint_dynamics/controller.js +6 -0
- package/dist/animation/joint_dynamics/controller.js.map +1 -1
- package/dist/animation/joint_dynamics/joint_dynamics_system.js +11 -0
- package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
- package/dist/animation/joint_dynamics/solver.js +50 -12
- package/dist/animation/joint_dynamics/solver.js.map +1 -1
- package/dist/animation/joint_dynamics/types.js.map +1 -1
- package/dist/animation/skeleton.js +300 -56
- package/dist/animation/skeleton.js.map +1 -1
- package/dist/animation/skin_palette_atlas.js +252 -0
- package/dist/animation/skin_palette_atlas.js.map +1 -0
- package/dist/animation/spring/multi_chain_spring_system.js +298 -38
- package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
- package/dist/animation/spring/spring_chain.js +2 -0
- package/dist/animation/spring/spring_chain.js.map +1 -1
- package/dist/animation/spring/spring_collider.js +159 -21
- package/dist/animation/spring/spring_collider.js.map +1 -1
- package/dist/animation/spring/spring_particle.js +1 -0
- package/dist/animation/spring/spring_particle.js.map +1 -1
- package/dist/animation/spring/spring_solver.js +173 -0
- package/dist/animation/spring/spring_solver.js.map +1 -0
- package/dist/animation/spring/spring_system.js +249 -24
- package/dist/animation/spring/spring_system.js.map +1 -1
- package/dist/animation/spring_modifier.js +32 -1
- package/dist/animation/spring_modifier.js.map +1 -1
- package/dist/app/app.js +26 -0
- package/dist/app/app.js.map +1 -1
- package/dist/app/engine.js +86 -15
- package/dist/app/engine.js.map +1 -1
- package/dist/app/gamepad.js +191 -0
- package/dist/app/gamepad.js.map +1 -0
- package/dist/app/inputactions.js +271 -0
- package/dist/app/inputactions.js.map +1 -0
- package/dist/app/inputmgr.js +331 -22
- package/dist/app/inputmgr.js.map +1 -1
- package/dist/app/loading.js +138 -0
- package/dist/app/loading.js.map +1 -0
- package/dist/app/pipelinecache.js +209 -0
- package/dist/app/pipelinecache.js.map +1 -0
- package/dist/app/runtimescript.js +25 -0
- package/dist/app/runtimescript.js.map +1 -1
- package/dist/app/scriptingsystem.js +54 -0
- package/dist/app/scriptingsystem.js.map +1 -1
- package/dist/app/scriptregistry.js +7 -2
- package/dist/app/scriptregistry.js.map +1 -1
- package/dist/asset/asset_manifest.js +59 -0
- package/dist/asset/asset_manifest.js.map +1 -0
- package/dist/asset/asset_preload.js +258 -0
- package/dist/asset/asset_preload.js.map +1 -0
- package/dist/asset/assetmanager.js +117 -65
- package/dist/asset/assetmanager.js.map +1 -1
- package/dist/asset/loaders/ktx2/basis/LICENSE +201 -0
- package/dist/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
- package/dist/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
- package/dist/asset/loaders/ktx2/basis_location.js +17 -0
- package/dist/asset/loaders/ktx2/basis_location.js.map +1 -0
- package/dist/asset/loaders/ktx2/ktx2_loader.js +88 -0
- package/dist/asset/loaders/ktx2/ktx2_loader.js.map +1 -0
- package/dist/asset/loaders/ktx2/ktx2_targets.js +118 -0
- package/dist/asset/loaders/ktx2/ktx2_targets.js.map +1 -0
- package/dist/asset/loaders/ktx2/transcoder_pool.js +259 -0
- package/dist/asset/loaders/ktx2/transcoder_pool.js.map +1 -0
- package/dist/asset/mesh_lod_builder.js +104 -0
- package/dist/asset/mesh_lod_builder.js.map +1 -0
- package/dist/asset/mesh_settings.js +111 -0
- package/dist/asset/mesh_settings.js.map +1 -0
- package/dist/asset/meshopt_decoder.js +18 -0
- package/dist/asset/meshopt_decoder.js.map +1 -0
- package/dist/asset/meshopt_simplifier.js +26 -0
- package/dist/asset/meshopt_simplifier.js.map +1 -0
- package/dist/asset/model.js +177 -3
- package/dist/asset/model.js.map +1 -1
- package/dist/asset/texture_settings.js +231 -0
- package/dist/asset/texture_settings.js.map +1 -0
- package/dist/asset/zmsh_binary.js +131 -0
- package/dist/asset/zmsh_binary.js.map +1 -0
- package/dist/asset/zsdf_binary.js +111 -0
- package/dist/asset/zsdf_binary.js.map +1 -0
- package/dist/blitter/bilateralblur.js +1 -2
- package/dist/blitter/bilateralblur.js.map +1 -1
- package/dist/blitter/gaussianblur.js +1 -6
- package/dist/blitter/gaussianblur.js.map +1 -1
- package/dist/camera/base.js +31 -10
- package/dist/camera/base.js.map +1 -1
- package/dist/camera/camera.js +178 -200
- package/dist/camera/camera.js.map +1 -1
- package/dist/camera/follow.js +342 -0
- package/dist/camera/follow.js.map +1 -0
- package/dist/camera/fps.js +9 -7
- package/dist/camera/fps.js.map +1 -1
- package/dist/camera/orbit.js +46 -19
- package/dist/camera/orbit.js.map +1 -1
- package/dist/index.d.ts +30475 -19865
- package/dist/index.js +67 -12
- package/dist/index.js.map +1 -1
- package/dist/material/blinn.js +3 -0
- package/dist/material/blinn.js.map +1 -1
- package/dist/material/eye.js +1 -1
- package/dist/material/eye.js.map +1 -1
- package/dist/material/hair.js +2 -3
- package/dist/material/hair.js.map +1 -1
- package/dist/material/hairstrand.js +235 -235
- package/dist/material/hairstrand.js.map +1 -1
- package/dist/material/lambert.js +4 -1
- package/dist/material/lambert.js.map +1 -1
- package/dist/material/material.js +362 -300
- package/dist/material/material.js.map +1 -1
- package/dist/material/meshmaterial.js +118 -45
- package/dist/material/meshmaterial.js.map +1 -1
- package/dist/material/mixins/lightmodel/blinnphong.js +1 -1
- package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
- package/dist/material/mixins/lightmodel/lambert.js +1 -1
- package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +34 -61
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +16 -2
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js +2 -1
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
- package/dist/material/mixins/lit.js +223 -83
- package/dist/material/mixins/lit.js.map +1 -1
- package/dist/material/mixins/pbr/common.js +31 -206
- package/dist/material/mixins/pbr/common.js.map +1 -1
- package/dist/material/mtoon.js +1 -1
- package/dist/material/mtoon.js.map +1 -1
- package/dist/material/pbrmr.js +3 -0
- package/dist/material/pbrmr.js.map +1 -1
- package/dist/material/pbrsg.js +3 -0
- package/dist/material/pbrsg.js.map +1 -1
- package/dist/material/sdf_debug.js +78 -0
- package/dist/material/sdf_debug.js.map +1 -0
- package/dist/material/shader/helper.js +733 -190
- package/dist/material/shader/helper.js.map +1 -1
- package/dist/material/shader/terrain_blend.js +204 -43
- package/dist/material/shader/terrain_blend.js.map +1 -1
- package/dist/material/skin.js +331 -297
- package/dist/material/skin.js.map +1 -1
- package/dist/material/skinprofile.js +1058 -0
- package/dist/material/skinprofile.js.map +1 -0
- package/dist/material/sss.js +353 -0
- package/dist/material/sss.js.map +1 -0
- package/dist/material/sss_kernel.js +265 -0
- package/dist/material/sss_kernel.js.map +1 -0
- package/dist/material/sssprofile.js +879 -0
- package/dist/material/sssprofile.js.map +1 -0
- package/dist/material/subsurfaceprofile.js +4 -8
- package/dist/material/subsurfaceprofile.js.map +1 -1
- package/dist/material/terrain-cm.js +464 -188
- package/dist/material/terrain-cm.js.map +1 -1
- package/dist/material/unlit.js +3 -0
- package/dist/material/unlit.js.map +1 -1
- package/dist/material/water.js +2009 -201
- package/dist/material/water.js.map +1 -1
- package/dist/node_modules/meshoptimizer/LICENSE +21 -0
- package/dist/node_modules/meshoptimizer/meshopt_decoder.js +197 -0
- package/dist/node_modules/meshoptimizer/meshopt_decoder.js.map +1 -0
- package/dist/node_modules/meshoptimizer/meshopt_simplifier.js +677 -0
- package/dist/node_modules/meshoptimizer/meshopt_simplifier.js.map +1 -0
- package/dist/physics/character.js +226 -0
- package/dist/physics/character.js.map +1 -0
- package/dist/physics/collider.js +209 -0
- package/dist/physics/collider.js.map +1 -0
- package/dist/physics/events.js +79 -0
- package/dist/physics/events.js.map +1 -0
- package/dist/physics/joint.js +272 -0
- package/dist/physics/joint.js.map +1 -0
- package/dist/physics/node_physics.js +234 -0
- package/dist/physics/node_physics.js.map +1 -0
- package/dist/physics/part.js +91 -0
- package/dist/physics/part.js.map +1 -0
- package/dist/physics/rigid_body.js +379 -0
- package/dist/physics/rigid_body.js.map +1 -0
- package/dist/physics/vehicle.js +335 -0
- package/dist/physics/vehicle.js.map +1 -0
- package/dist/physics/world.js +25 -0
- package/dist/physics/world.js.map +1 -0
- package/dist/posteffect/bloom.js +49 -14
- package/dist/posteffect/bloom.js.map +1 -1
- package/dist/posteffect/motionblur.js +668 -63
- package/dist/posteffect/motionblur.js.map +1 -1
- package/dist/posteffect/posteffect.js +12 -1
- package/dist/posteffect/posteffect.js.map +1 -1
- package/dist/posteffect/postsss.js +572 -0
- package/dist/posteffect/postsss.js.map +1 -0
- package/dist/posteffect/sao.js +48 -50
- package/dist/posteffect/sao.js.map +1 -1
- package/dist/posteffect/skinsss.js +605 -355
- package/dist/posteffect/skinsss.js.map +1 -1
- package/dist/posteffect/ssgi.js +91 -162
- package/dist/posteffect/ssgi.js.map +1 -1
- package/dist/posteffect/ssr.js +3 -11
- package/dist/posteffect/ssr.js.map +1 -1
- package/dist/posteffect/sss.js +34 -15
- package/dist/posteffect/sss.js.map +1 -1
- package/dist/posteffect/taa.js +1 -0
- package/dist/posteffect/taa.js.map +1 -1
- package/dist/render/abuffer_oit.js +40 -39
- package/dist/render/abuffer_oit.js.map +1 -1
- package/dist/render/clipmap.js +328 -62
- package/dist/render/clipmap.js.map +1 -1
- package/dist/render/cluster_light.js +958 -224
- package/dist/render/cluster_light.js.map +1 -1
- package/dist/render/cull_visitor.js +26 -5
- package/dist/render/cull_visitor.js.map +1 -1
- package/dist/render/debug_views.js +37 -0
- package/dist/render/debug_views.js.map +1 -0
- package/dist/render/depthpass.js +5 -1
- package/dist/render/depthpass.js.map +1 -1
- package/dist/render/drawable_mixin.js +53 -6
- package/dist/render/drawable_mixin.js.map +1 -1
- package/dist/render/dualdepthpeeling_oit.js +53 -53
- package/dist/render/dualdepthpeeling_oit.js.map +1 -1
- package/dist/render/envlight.js +196 -113
- package/dist/render/envlight.js.map +1 -1
- package/dist/render/fbm_wavegenerator.js +8 -13
- package/dist/render/fbm_wavegenerator.js.map +1 -1
- package/dist/render/fft_wavegenerator.js +268 -136
- package/dist/render/fft_wavegenerator.js.map +1 -1
- package/dist/render/gerstner_wavegenerator.js +113 -28
- package/dist/render/gerstner_wavegenerator.js.map +1 -1
- package/dist/render/gi/probe_volume.js +1334 -0
- package/dist/render/gi/probe_volume.js.map +1 -0
- package/dist/render/gi/scene_gi.js +282 -0
- package/dist/render/gi/scene_gi.js.map +1 -0
- package/dist/render/gi/voxel_atlas.js +198 -0
- package/dist/render/gi/voxel_atlas.js.map +1 -0
- package/dist/render/gi/voxel_lighting.js +652 -0
- package/dist/render/gi/voxel_lighting.js.map +1 -0
- package/dist/render/gi/voxel_scene.js +642 -0
- package/dist/render/gi/voxel_scene.js.map +1 -0
- package/dist/render/gi/voxelize_pass.js +223 -0
- package/dist/render/gi/voxelize_pass.js.map +1 -0
- package/dist/render/globalbindgroup_allocator.js +3 -2
- package/dist/render/globalbindgroup_allocator.js.map +1 -1
- package/dist/render/gpu_picking.js +9 -36
- package/dist/render/gpu_picking.js.map +1 -1
- package/dist/render/hzb.js +80 -22
- package/dist/render/hzb.js.map +1 -1
- package/dist/render/instance_culling.js +499 -0
- package/dist/render/instance_culling.js.map +1 -0
- package/dist/render/lightpass.js +23 -9
- package/dist/render/lightpass.js.map +1 -1
- package/dist/render/lod.js +46 -0
- package/dist/render/lod.js.map +1 -0
- package/dist/render/oit.js +11 -11
- package/dist/render/oit.js.map +1 -1
- package/dist/render/primitive.js +301 -9
- package/dist/render/primitive.js.map +1 -1
- package/dist/render/primitive_readback.js +209 -0
- package/dist/render/primitive_readback.js.map +1 -0
- package/dist/render/render_queue.js +198 -11
- package/dist/render/render_queue.js.map +1 -1
- package/dist/render/renderer.js +14 -7
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/rendergraph/blackboard.js +15 -1
- package/dist/render/rendergraph/blackboard.js.map +1 -1
- package/dist/render/rendergraph/executor.js +136 -0
- package/dist/render/rendergraph/executor.js.map +1 -1
- package/dist/render/rendergraph/forward_plus_builder.js +585 -59
- package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
- package/dist/render/rendergraph/frame_resource_requirements.js +1 -0
- package/dist/render/rendergraph/frame_resource_requirements.js.map +1 -1
- package/dist/render/rendergraph/history_resources.js +8 -1
- package/dist/render/rendergraph/history_resources.js.map +1 -1
- package/dist/render/rendergraph/scene_render_context.js +31 -2
- package/dist/render/rendergraph/scene_render_context.js.map +1 -1
- package/dist/render/rendergraph/types.js.map +1 -1
- package/dist/render/renderpass.js +28 -5
- package/dist/render/renderpass.js.map +1 -1
- package/dist/render/sdf/box_allocator.js +98 -0
- package/dist/render/sdf/box_allocator.js.map +1 -0
- package/dist/render/sdf/global_sdf.js +865 -0
- package/dist/render/sdf/global_sdf.js.map +1 -0
- package/dist/render/sdf/scene_sdf.js +562 -0
- package/dist/render/sdf/scene_sdf.js.map +1 -0
- package/dist/render/sdf/sdf_atlas.js +227 -0
- package/dist/render/sdf/sdf_atlas.js.map +1 -0
- package/dist/render/sdf/sdf_programs.js +42 -0
- package/dist/render/sdf/sdf_programs.js.map +1 -0
- package/dist/render/sdf/sdf_view.js +199 -0
- package/dist/render/sdf/sdf_view.js.map +1 -0
- package/dist/render/sdf/waterline.js +610 -0
- package/dist/render/sdf/waterline.js.map +1 -0
- package/dist/render/shadow_mask_pass.js +142 -25
- package/dist/render/shadow_mask_pass.js.map +1 -1
- package/dist/render/shore_wave_generator.js +1193 -0
- package/dist/render/shore_wave_generator.js.map +1 -0
- package/dist/render/sky.js +516 -86
- package/dist/render/sky.js.map +1 -1
- package/dist/render/transmission_thickness_pass.js +477 -0
- package/dist/render/transmission_thickness_pass.js.map +1 -0
- package/dist/render/underwater.js +524 -0
- package/dist/render/underwater.js.map +1 -0
- package/dist/render/virtualtexture/virtual_texture.js +1214 -0
- package/dist/render/virtualtexture/virtual_texture.js.map +1 -0
- package/dist/render/volumetric_cloud/cloud_common.js +472 -0
- package/dist/render/volumetric_cloud/cloud_common.js.map +1 -0
- package/dist/render/volumetric_cloud/cloud_noise.js +189 -0
- package/dist/render/volumetric_cloud/cloud_noise.js.map +1 -0
- package/dist/render/volumetric_cloud/debug_view.js +131 -0
- package/dist/render/volumetric_cloud/debug_view.js.map +1 -0
- package/dist/render/volumetric_cloud/resources.js +380 -0
- package/dist/render/volumetric_cloud/resources.js.map +1 -0
- package/dist/render/volumetric_cloud/sky_cache.js +380 -0
- package/dist/render/volumetric_cloud/sky_cache.js.map +1 -0
- package/dist/render/volumetric_cloud/volumetric_cloud.js +318 -0
- package/dist/render/volumetric_cloud/volumetric_cloud.js.map +1 -0
- package/dist/render/water_caustics.js +1195 -0
- package/dist/render/water_caustics.js.map +1 -0
- package/dist/render/water_foam.js +627 -0
- package/dist/render/water_foam.js.map +1 -0
- package/dist/render/water_interaction.js +1795 -0
- package/dist/render/water_interaction.js.map +1 -0
- package/dist/render/weightedblended_oit.js +36 -36
- package/dist/render/weightedblended_oit.js.map +1 -1
- package/dist/scene/basesprite.js +9 -4
- package/dist/scene/basesprite.js.map +1 -1
- package/dist/scene/batchgroup.js +108 -12
- package/dist/scene/batchgroup.js.map +1 -1
- package/dist/scene/environment.js +28 -7
- package/dist/scene/environment.js.map +1 -1
- package/dist/scene/foliage.js +1020 -0
- package/dist/scene/foliage.js.map +1 -0
- package/dist/scene/foliage_buckets.js +147 -0
- package/dist/scene/foliage_buckets.js.map +1 -0
- package/dist/scene/foliage_mask.js +358 -0
- package/dist/scene/foliage_mask.js.map +1 -0
- package/dist/scene/hair.js +15 -4
- package/dist/scene/hair.js.map +1 -1
- package/dist/scene/light.js +179 -13
- package/dist/scene/light.js.map +1 -1
- package/dist/scene/mesh.js +327 -45
- package/dist/scene/mesh.js.map +1 -1
- package/dist/scene/meshdrawable.js +6 -0
- package/dist/scene/meshdrawable.js.map +1 -1
- package/dist/scene/msdftext.js +9 -3
- package/dist/scene/msdftext.js.map +1 -1
- package/dist/scene/msdftextsprite.js +9 -3
- package/dist/scene/msdftextsprite.js.map +1 -1
- package/dist/scene/particlesys.js +8 -2
- package/dist/scene/particlesys.js.map +1 -1
- package/dist/scene/physics_settings.js +157 -0
- package/dist/scene/physics_settings.js.map +1 -0
- package/dist/scene/scene.js +190 -44
- package/dist/scene/scene.js.map +1 -1
- package/dist/scene/scene_node.js +147 -6
- package/dist/scene/scene_node.js.map +1 -1
- package/dist/scene/terrain-cm/bladegrassmaterial.js +735 -0
- package/dist/scene/terrain-cm/bladegrassmaterial.js.map +1 -0
- package/dist/scene/terrain-cm/grass.js +881 -42
- package/dist/scene/terrain-cm/grass.js.map +1 -1
- package/dist/scene/terrain-cm/grass_gpu.js +929 -0
- package/dist/scene/terrain-cm/grass_gpu.js.map +1 -0
- package/dist/scene/terrain-cm/grassmaterial.js +180 -9
- package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
- package/dist/scene/terrain-cm/terrain-cm.js +465 -36
- package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
- package/dist/scene/terrain-cm/terrain_gi_albedo.js +257 -0
- package/dist/scene/terrain-cm/terrain_gi_albedo.js.map +1 -0
- package/dist/scene/terrain-cm/terrain_rvt.js +395 -0
- package/dist/scene/terrain-cm/terrain_rvt.js.map +1 -0
- package/dist/scene/update_queue.js +54 -0
- package/dist/scene/update_queue.js.map +1 -0
- package/dist/scene/water.js +997 -96
- package/dist/scene/water.js.map +1 -1
- package/dist/scene/water_surface_sampler.js +324 -0
- package/dist/scene/water_surface_sampler.js.map +1 -0
- package/dist/scene/wind.js +317 -0
- package/dist/scene/wind.js.map +1 -0
- package/dist/shaders/atmosphere.js +388 -166
- package/dist/shaders/atmosphere.js.map +1 -1
- package/dist/shaders/fog.js +100 -32
- package/dist/shaders/fog.js.map +1 -1
- package/dist/shaders/gi.js +365 -0
- package/dist/shaders/gi.js.map +1 -0
- package/dist/shaders/ltc_rect.js +202 -0
- package/dist/shaders/ltc_rect.js.map +1 -0
- package/dist/shaders/misc.js +1 -10
- package/dist/shaders/misc.js.map +1 -1
- package/dist/shaders/noise.js +5 -1
- package/dist/shaders/noise.js.map +1 -1
- package/dist/shaders/probes.js +309 -0
- package/dist/shaders/probes.js.map +1 -0
- package/dist/shaders/sdf.js +537 -0
- package/dist/shaders/sdf.js.map +1 -0
- package/dist/shaders/shadow.js +104 -70
- package/dist/shaders/shadow.js.map +1 -1
- package/dist/shaders/skin_brdf.js +243 -0
- package/dist/shaders/skin_brdf.js.map +1 -0
- package/dist/shaders/ssr.js +6 -159
- package/dist/shaders/ssr.js.map +1 -1
- package/dist/shaders/temporal.js +38 -27
- package/dist/shaders/temporal.js.map +1 -1
- package/dist/shaders/water.js +40 -87
- package/dist/shaders/water.js.map +1 -1
- package/dist/shaders/water_medium.js +42 -0
- package/dist/shaders/water_medium.js.map +1 -0
- package/dist/shadow/dom.js +110 -19
- package/dist/shadow/dom.js.map +1 -1
- package/dist/shadow/esm.js +3 -3
- package/dist/shadow/esm.js.map +1 -1
- package/dist/shadow/pcf_opt.js +15 -48
- package/dist/shadow/pcf_opt.js.map +1 -1
- package/dist/shadow/pcf_pd.js +15 -53
- package/dist/shadow/pcf_pd.js.map +1 -1
- package/dist/shadow/pcss.js +34 -9
- package/dist/shadow/pcss.js.map +1 -1
- package/dist/shadow/shadow_impl.js +3 -8
- package/dist/shadow/shadow_impl.js.map +1 -1
- package/dist/shadow/shadow_region.js +87 -49
- package/dist/shadow/shadow_region.js.map +1 -1
- package/dist/shadow/shadowmapper.js +93 -53
- package/dist/shadow/shadowmapper.js.map +1 -1
- package/dist/shadow/ssm.js +18 -72
- package/dist/shadow/ssm.js.map +1 -1
- package/dist/shadow/vsm.js +3 -3
- package/dist/shadow/vsm.js.map +1 -1
- package/dist/shapes/shape.js +44 -1
- package/dist/shapes/shape.js.map +1 -1
- package/dist/utility/buoyancy.js +287 -0
- package/dist/utility/buoyancy.js.map +1 -0
- package/dist/utility/physical.js +10 -1
- package/dist/utility/physical.js.map +1 -1
- package/dist/utility/pmrem.js +17 -43
- package/dist/utility/pmrem.js.map +1 -1
- package/dist/utility/sdf/bake.js +435 -0
- package/dist/utility/sdf/bake.js.map +1 -0
- package/dist/utility/sdf/bvh.js +576 -0
- package/dist/utility/sdf/bvh.js.map +1 -0
- package/dist/utility/serialization/manager.js +36 -6
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +77 -7
- package/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/utility/serialization/scene/batch.js +11 -4
- package/dist/utility/serialization/scene/batch.js.map +1 -1
- package/dist/utility/serialization/scene/camera.js +140 -279
- package/dist/utility/serialization/scene/camera.js.map +1 -1
- package/dist/utility/serialization/scene/cloth.js +25 -0
- package/dist/utility/serialization/scene/cloth.js.map +1 -0
- package/dist/utility/serialization/scene/common.js +14 -6
- package/dist/utility/serialization/scene/common.js.map +1 -1
- package/dist/utility/serialization/scene/foliage.js +821 -0
- package/dist/utility/serialization/scene/foliage.js.map +1 -0
- package/dist/utility/serialization/scene/hair.js +11 -4
- package/dist/utility/serialization/scene/hair.js.map +1 -1
- package/dist/utility/serialization/scene/light.js +115 -7
- package/dist/utility/serialization/scene/light.js.map +1 -1
- package/dist/utility/serialization/scene/material.js +388 -220
- package/dist/utility/serialization/scene/material.js.map +1 -1
- package/dist/utility/serialization/scene/mesh.js +69 -4
- package/dist/utility/serialization/scene/mesh.js.map +1 -1
- package/dist/utility/serialization/scene/node.js +140 -5
- package/dist/utility/serialization/scene/node.js.map +1 -1
- package/dist/utility/serialization/scene/node_physics.js +1318 -0
- package/dist/utility/serialization/scene/node_physics.js.map +1 -0
- package/dist/utility/serialization/scene/particle.js +11 -4
- package/dist/utility/serialization/scene/particle.js.map +1 -1
- package/dist/utility/serialization/scene/physics.js +120 -0
- package/dist/utility/serialization/scene/physics.js.map +1 -0
- package/dist/utility/serialization/scene/primitive.js +124 -10
- package/dist/utility/serialization/scene/primitive.js.map +1 -1
- package/dist/utility/serialization/scene/scene.js +1687 -51
- package/dist/utility/serialization/scene/scene.js.map +1 -1
- package/dist/utility/serialization/scene/sprite.js +11 -4
- package/dist/utility/serialization/scene/sprite.js.map +1 -1
- package/dist/utility/serialization/scene/terrain.js +163 -33
- package/dist/utility/serialization/scene/terrain.js.map +1 -1
- package/dist/utility/serialization/scene/text.js +11 -4
- package/dist/utility/serialization/scene/text.js.map +1 -1
- package/dist/utility/serialization/scene/water.js +2473 -114
- package/dist/utility/serialization/scene/water.js.map +1 -1
- package/dist/utility/serialization/types.js.map +1 -1
- package/dist/utility/shprojector.js +97 -109
- package/dist/utility/shprojector.js.map +1 -1
- package/dist/utility/textures/foam.js +170 -0
- package/dist/utility/textures/foam.js.map +1 -0
- package/dist/utility/textures/ggxlut.js +17 -43
- package/dist/utility/textures/ggxlut.js.map +1 -1
- package/dist/utility/textures/ltcdata.js +4 -4
- package/dist/utility/textures/ltcdata.js.map +1 -1
- package/dist/utility/textures/ltclut.js +16 -6
- package/dist/utility/textures/ltclut.js.map +1 -1
- package/dist/utility/unsupported.js +24 -0
- package/dist/utility/unsupported.js.map +1 -0
- package/dist/values.js +13 -1
- package/dist/values.js.map +1 -1
- package/package.json +25 -12
- package/src/asset/loaders/ktx2/basis/LICENSE +201 -0
- package/src/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
- package/src/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
- package/src/asset/meshoptimizer/LICENSE +21 -0
|
@@ -0,0 +1,1058 @@
|
|
|
1
|
+
import { Vector3 } from '@zephyr3d/base';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Number of scalar parameter columns stored per profile row.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Mirrors the row layout UE5 uses in its `SSProfiles` texture, read back from
|
|
8
|
+
* `UEDigitalHuman.rdc` (SSS::PassOne_Burley lines 143-157).
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/ const SKIN_PROFILE_PARAM_COLUMNS = 6;
|
|
12
|
+
/**
|
|
13
|
+
* Number of texels the baked transmission profile occupies.
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* `BSSS_TRANSMISSION_PROFILE_SIZE` in UE5 (`SubsurfaceProfileCommon.ush:27`).
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
*/ const SKIN_TRANSMISSION_LUT_SIZE = 32;
|
|
20
|
+
/**
|
|
21
|
+
* Total columns per profile row: the scalar parameters followed by the baked
|
|
22
|
+
* transmission profile, exactly as UE5 lays out an `SSProfiles` row.
|
|
23
|
+
*
|
|
24
|
+
* @internal
|
|
25
|
+
*/ const SKIN_PROFILE_COLUMNS = SKIN_PROFILE_PARAM_COLUMNS + SKIN_TRANSMISSION_LUT_SIZE;
|
|
26
|
+
/** Maximum number of live skin profiles, matching the 8-bit profile id channel. @internal */ const SKIN_PROFILE_CAPACITY = 256;
|
|
27
|
+
/**
|
|
28
|
+
* Gate on {@link SkinProfile}'s constructor.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* Module-private, so only {@link SkinProfile.createOwned} can pass it. See the
|
|
32
|
+
* constructor for why a `private` modifier alone is not enough.
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/ const CREATE_TOKEN = Symbol('SkinProfile.create');
|
|
36
|
+
/**
|
|
37
|
+
* Largest optical depth the transmission profile is defined over.
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* `SSSS_MAX_TRANSMISSION_PROFILE_DISTANCE` in UE5. It appears in two places that
|
|
41
|
+
* have to agree, which is why it lives here rather than next to either of them:
|
|
42
|
+
* the thickness pass encodes its optical depth as `1 - opticalDepth / MAX`, and
|
|
43
|
+
* the transmission BxDF decodes that and divides by `MAX` again to index the
|
|
44
|
+
* baked profile below.
|
|
45
|
+
*
|
|
46
|
+
* @internal
|
|
47
|
+
*/ const SKIN_MAX_TRANSMISSION_OPTICAL_DEPTH = 5;
|
|
48
|
+
/**
|
|
49
|
+
* Floor the averaged optical depth is clamped to before encoding.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* UE5's `clamp(..., 0.15, 5)` in `CalculateOpticalDepth`.
|
|
53
|
+
*
|
|
54
|
+
* @internal
|
|
55
|
+
*/ const SKIN_TRANSMISSION_OPTICAL_DEPTH_FLOOR = 0.15;
|
|
56
|
+
/**
|
|
57
|
+
* Constant added to the optical depth after clamping.
|
|
58
|
+
*
|
|
59
|
+
* @remarks
|
|
60
|
+
* UE5's trailing `+ 0.25` in `CalculateOpticalDepth`.
|
|
61
|
+
*
|
|
62
|
+
* @internal
|
|
63
|
+
*/ const SKIN_TRANSMISSION_OPTICAL_DEPTH_BIAS = 0.25;
|
|
64
|
+
/**
|
|
65
|
+
* Largest value the thickness pass can write, and with it the sentinel that
|
|
66
|
+
* separates a real measurement from "no light wrote this".
|
|
67
|
+
*
|
|
68
|
+
* @remarks
|
|
69
|
+
* The encoding is `1 - opticalDepth / MAX` and the optical depth never falls
|
|
70
|
+
* below `FLOOR + BIAS`, so the pass tops out at 0.92 and the cleared value of 1
|
|
71
|
+
* is unreachable — which makes it usable as "no data".
|
|
72
|
+
*
|
|
73
|
+
* The distinction matters because 1 decodes to zero optical depth, the profile's
|
|
74
|
+
* *strongest* entry: read as a measurement it would light every non-transmitting
|
|
75
|
+
* light's pixels at full transmission.
|
|
76
|
+
*
|
|
77
|
+
* @internal
|
|
78
|
+
*/ const SKIN_TRANSMISSION_NO_DATA_ENCODING = 1 - (SKIN_TRANSMISSION_OPTICAL_DEPTH_FLOOR + SKIN_TRANSMISSION_OPTICAL_DEPTH_BIAS) / SKIN_MAX_TRANSMISSION_OPTICAL_DEPTH;
|
|
79
|
+
/**
|
|
80
|
+
* Distance, in profile millimetres, the baked transmission profile spans.
|
|
81
|
+
*
|
|
82
|
+
* @remarks
|
|
83
|
+
* `MaxTransmissionProfileDistance * CmToMm = 5 * 10` in
|
|
84
|
+
* `ComputeTransmissionProfileBurley`.
|
|
85
|
+
*
|
|
86
|
+
* The 5 here and the one in {@link SKIN_MAX_TRANSMISSION_OPTICAL_DEPTH} are the
|
|
87
|
+
* same number by construction: UE5's world unit is the centimetre, so an optical
|
|
88
|
+
* depth of 1 at unit extinction *is* one centimetre, and this axis is that span
|
|
89
|
+
* in the millimetres the mean free paths use. The shader indexes the table with
|
|
90
|
+
* optical depth directly — self-consistent only once that is accounted for.
|
|
91
|
+
*
|
|
92
|
+
* The thickness pass must therefore be calibrated against this axis;
|
|
93
|
+
* {@link SKIN_OPTICAL_DEPTH_PER_WORLD_UNIT} is that calibration, and changing
|
|
94
|
+
* this constant without it slides the whole profile along the thickness axis.
|
|
95
|
+
*
|
|
96
|
+
* @internal
|
|
97
|
+
*/ const TRANSMISSION_LUT_MAX_DISTANCE_MM = 50;
|
|
98
|
+
/**
|
|
99
|
+
* Radius the baked transmission profile is offset by, in profile millimetres.
|
|
100
|
+
*
|
|
101
|
+
* @remarks
|
|
102
|
+
* `ProfileRadiusOffset` in `BurleyNormalizedSSS.cpp:22`, 0.06 cm, times the same
|
|
103
|
+
* `CmToMm`. It keeps the zero-thickness entry off the singular `r = 0` end of
|
|
104
|
+
* the diffusion profile.
|
|
105
|
+
*
|
|
106
|
+
* @internal
|
|
107
|
+
*/ const TRANSMISSION_LUT_RADIUS_OFFSET_MM = 0.6;
|
|
108
|
+
/**
|
|
109
|
+
* Profile-space millimetres per world unit, before the profile's own world unit
|
|
110
|
+
* scale is applied.
|
|
111
|
+
*
|
|
112
|
+
* @remarks
|
|
113
|
+
* This engine is metric throughout, so a world unit is a metre and the profile
|
|
114
|
+
* distances are millimetres. UE5 instead carries a `BURLEY_CM_2_MM = 10` because
|
|
115
|
+
* its world unit is the centimetre; that factor has no analogue here and must
|
|
116
|
+
* not be transcribed (see the diffusion pass, which likewise omits it).
|
|
117
|
+
*
|
|
118
|
+
* @internal
|
|
119
|
+
*/ const WORLD_UNITS_TO_PROFILE_MM = 1000;
|
|
120
|
+
/**
|
|
121
|
+
* Optical depth accumulated per world unit of light path, before the profile's
|
|
122
|
+
* `extinctionScale`.
|
|
123
|
+
*
|
|
124
|
+
* @remarks
|
|
125
|
+
* Derived rather than chosen, so the thickness pass and the baked profile cannot
|
|
126
|
+
* drift apart. The BxDF indexes entry `opticalDepth / MAX * (size - 1)` and entry
|
|
127
|
+
* `i` was baked for `i / size * LUT_MAX_MM` profile millimetres, so one unit of
|
|
128
|
+
* optical depth is `LUT_MAX_MM * (size - 1) / size / MAX` millimetres.
|
|
129
|
+
*
|
|
130
|
+
* `worldUnitScale` deliberately does not appear: it divides both the table's axis
|
|
131
|
+
* and the path's conversion into profile space, so it cancels. UE5 likewise keeps
|
|
132
|
+
* it out of `CalculateOpticalDepth`.
|
|
133
|
+
*
|
|
134
|
+
* The `(size - 1) / size` is a deliberate 3% departure from UE5, whose index and
|
|
135
|
+
* axis disagree by that factor. Correcting it makes a path of `t` metres index
|
|
136
|
+
* exactly the entry baked for `t` millimetres.
|
|
137
|
+
*
|
|
138
|
+
* @internal
|
|
139
|
+
*/ const SKIN_OPTICAL_DEPTH_PER_WORLD_UNIT = WORLD_UNITS_TO_PROFILE_MM * SKIN_MAX_TRANSMISSION_OPTICAL_DEPTH / (TRANSMISSION_LUT_MAX_DISTANCE_MM * ((SKIN_TRANSMISSION_LUT_SIZE - 1) / SKIN_TRANSMISSION_LUT_SIZE));
|
|
140
|
+
/**
|
|
141
|
+
* `GetSearchLightDiffuseScalingFactor`, the default variant.
|
|
142
|
+
*
|
|
143
|
+
* @remarks
|
|
144
|
+
* `s = 3.5 + 100 (A - 0.33)^4`. The same curve the diffusion evaluates on the
|
|
145
|
+
* GPU (`posteffect/skinsss.ts`, `scalingFactor`); the transmission profile is
|
|
146
|
+
* baked on the CPU and needs its own copy.
|
|
147
|
+
*
|
|
148
|
+
* @internal
|
|
149
|
+
*/ function searchLightDiffuseScalingFactor(albedo) {
|
|
150
|
+
const v = albedo - 0.33;
|
|
151
|
+
return 3.5 + 100 * v * v * v * v;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Preset parameters.
|
|
155
|
+
*
|
|
156
|
+
* @remarks
|
|
157
|
+
* `skin` reproduces UE5's `FSubsurfaceProfileStruct` defaults exactly, converted
|
|
158
|
+
* into this engine's metre-based scene units:
|
|
159
|
+
*
|
|
160
|
+
* | UE5 parameter | UE5 default | here |
|
|
161
|
+
* | -------------------- | ------------------------------- | --------------------------- |
|
|
162
|
+
* | SurfaceAlbedo | (0.91058, 0.338275, 0.2718) | `surfaceAlbedo`, unchanged |
|
|
163
|
+
* | MeanFreePathColor | (1, 0.1983/2.229, 0.1607/2.229) | `meanFreePath`, unchanged |
|
|
164
|
+
* | MeanFreePathDistance | 1.2 × 2.229 = 2.6748 cm | `meanFreePathDistance` in m |
|
|
165
|
+
* | WorldUnitScale | 0.1 cm | folded into the distance |
|
|
166
|
+
* | BoundaryColorBleed | white | white |
|
|
167
|
+
*
|
|
168
|
+
* The red-shifted mean free path (roughly 1 : 0.089 : 0.072) and the equally
|
|
169
|
+
* red-shifted albedo are together what make the diffusion read as skin rather
|
|
170
|
+
* than as a neutral blur — the first thing to check when scattering looks washed
|
|
171
|
+
* out. UE5 ships only `skin`; the rest are this engine's own, pitched around the
|
|
172
|
+
* same regime to stay comparable.
|
|
173
|
+
*
|
|
174
|
+
* `boundaryColorBleed` is white throughout, as in UE5: it tints taps belonging to
|
|
175
|
+
* a *different* profile, so anything darker attenuates every profile boundary.
|
|
176
|
+
*
|
|
177
|
+
* @internal
|
|
178
|
+
*/ const SKIN_PROFILE_TEMPLATES = {
|
|
179
|
+
skin: {
|
|
180
|
+
surfaceAlbedo: [
|
|
181
|
+
0.91058,
|
|
182
|
+
0.338275,
|
|
183
|
+
0.2718
|
|
184
|
+
],
|
|
185
|
+
meanFreePath: [
|
|
186
|
+
1.0,
|
|
187
|
+
0.0889636,
|
|
188
|
+
0.0720951
|
|
189
|
+
],
|
|
190
|
+
meanFreePathDistance: 0.026748,
|
|
191
|
+
worldUnitScale: 1,
|
|
192
|
+
scatterScale: 1,
|
|
193
|
+
boundaryColorBleed: [
|
|
194
|
+
1,
|
|
195
|
+
1,
|
|
196
|
+
1
|
|
197
|
+
],
|
|
198
|
+
transmissionTint: [
|
|
199
|
+
1.0,
|
|
200
|
+
0.42,
|
|
201
|
+
0.3
|
|
202
|
+
],
|
|
203
|
+
extinctionScale: 1,
|
|
204
|
+
normalScale: 0.08,
|
|
205
|
+
scatteringDistribution: 0.93,
|
|
206
|
+
ior: 1.55,
|
|
207
|
+
roughness0: 0.75,
|
|
208
|
+
roughness1: 1.3,
|
|
209
|
+
lobeMix: 0.85
|
|
210
|
+
},
|
|
211
|
+
skin_pale: {
|
|
212
|
+
surfaceAlbedo: [
|
|
213
|
+
0.93,
|
|
214
|
+
0.4,
|
|
215
|
+
0.33
|
|
216
|
+
],
|
|
217
|
+
meanFreePath: [
|
|
218
|
+
1.0,
|
|
219
|
+
0.1,
|
|
220
|
+
0.083
|
|
221
|
+
],
|
|
222
|
+
meanFreePathDistance: 0.0294,
|
|
223
|
+
worldUnitScale: 1,
|
|
224
|
+
scatterScale: 1,
|
|
225
|
+
boundaryColorBleed: [
|
|
226
|
+
1,
|
|
227
|
+
1,
|
|
228
|
+
1
|
|
229
|
+
],
|
|
230
|
+
transmissionTint: [
|
|
231
|
+
1.0,
|
|
232
|
+
0.48,
|
|
233
|
+
0.38
|
|
234
|
+
],
|
|
235
|
+
extinctionScale: 1,
|
|
236
|
+
normalScale: 0.08,
|
|
237
|
+
scatteringDistribution: 0.93,
|
|
238
|
+
ior: 1.55,
|
|
239
|
+
roughness0: 0.72,
|
|
240
|
+
roughness1: 1.25,
|
|
241
|
+
lobeMix: 0.85
|
|
242
|
+
},
|
|
243
|
+
skin_tan: {
|
|
244
|
+
surfaceAlbedo: [
|
|
245
|
+
0.88,
|
|
246
|
+
0.3,
|
|
247
|
+
0.235
|
|
248
|
+
],
|
|
249
|
+
meanFreePath: [
|
|
250
|
+
1.0,
|
|
251
|
+
0.082,
|
|
252
|
+
0.066
|
|
253
|
+
],
|
|
254
|
+
meanFreePathDistance: 0.0254,
|
|
255
|
+
worldUnitScale: 1,
|
|
256
|
+
scatterScale: 1,
|
|
257
|
+
boundaryColorBleed: [
|
|
258
|
+
1,
|
|
259
|
+
1,
|
|
260
|
+
1
|
|
261
|
+
],
|
|
262
|
+
transmissionTint: [
|
|
263
|
+
1.0,
|
|
264
|
+
0.38,
|
|
265
|
+
0.26
|
|
266
|
+
],
|
|
267
|
+
extinctionScale: 1,
|
|
268
|
+
normalScale: 0.08,
|
|
269
|
+
scatteringDistribution: 0.93,
|
|
270
|
+
ior: 1.55,
|
|
271
|
+
roughness0: 0.76,
|
|
272
|
+
roughness1: 1.32,
|
|
273
|
+
lobeMix: 0.85
|
|
274
|
+
},
|
|
275
|
+
skin_dark: {
|
|
276
|
+
surfaceAlbedo: [
|
|
277
|
+
0.8,
|
|
278
|
+
0.24,
|
|
279
|
+
0.185
|
|
280
|
+
],
|
|
281
|
+
meanFreePath: [
|
|
282
|
+
1.0,
|
|
283
|
+
0.072,
|
|
284
|
+
0.057
|
|
285
|
+
],
|
|
286
|
+
meanFreePathDistance: 0.0227,
|
|
287
|
+
worldUnitScale: 1,
|
|
288
|
+
scatterScale: 1,
|
|
289
|
+
boundaryColorBleed: [
|
|
290
|
+
1,
|
|
291
|
+
1,
|
|
292
|
+
1
|
|
293
|
+
],
|
|
294
|
+
transmissionTint: [
|
|
295
|
+
1.0,
|
|
296
|
+
0.3,
|
|
297
|
+
0.2
|
|
298
|
+
],
|
|
299
|
+
extinctionScale: 1,
|
|
300
|
+
normalScale: 0.08,
|
|
301
|
+
scatteringDistribution: 0.93,
|
|
302
|
+
ior: 1.55,
|
|
303
|
+
roughness0: 0.78,
|
|
304
|
+
roughness1: 1.35,
|
|
305
|
+
lobeMix: 0.85
|
|
306
|
+
},
|
|
307
|
+
wax: {
|
|
308
|
+
surfaceAlbedo: [
|
|
309
|
+
0.95,
|
|
310
|
+
0.9,
|
|
311
|
+
0.8
|
|
312
|
+
],
|
|
313
|
+
meanFreePath: [
|
|
314
|
+
1.0,
|
|
315
|
+
0.85,
|
|
316
|
+
0.68
|
|
317
|
+
],
|
|
318
|
+
meanFreePathDistance: 0.05,
|
|
319
|
+
worldUnitScale: 1,
|
|
320
|
+
scatterScale: 1,
|
|
321
|
+
boundaryColorBleed: [
|
|
322
|
+
1,
|
|
323
|
+
1,
|
|
324
|
+
1
|
|
325
|
+
],
|
|
326
|
+
transmissionTint: [
|
|
327
|
+
1.0,
|
|
328
|
+
0.88,
|
|
329
|
+
0.74
|
|
330
|
+
],
|
|
331
|
+
extinctionScale: 1,
|
|
332
|
+
normalScale: 0.05,
|
|
333
|
+
scatteringDistribution: 0.85,
|
|
334
|
+
ior: 1.45,
|
|
335
|
+
roughness0: 0.85,
|
|
336
|
+
roughness1: 1.4,
|
|
337
|
+
lobeMix: 0.8
|
|
338
|
+
},
|
|
339
|
+
jade: {
|
|
340
|
+
surfaceAlbedo: [
|
|
341
|
+
0.75,
|
|
342
|
+
0.93,
|
|
343
|
+
0.86
|
|
344
|
+
],
|
|
345
|
+
meanFreePath: [
|
|
346
|
+
0.6,
|
|
347
|
+
1.0,
|
|
348
|
+
0.85
|
|
349
|
+
],
|
|
350
|
+
meanFreePathDistance: 0.045,
|
|
351
|
+
worldUnitScale: 1,
|
|
352
|
+
scatterScale: 1,
|
|
353
|
+
boundaryColorBleed: [
|
|
354
|
+
1,
|
|
355
|
+
1,
|
|
356
|
+
1
|
|
357
|
+
],
|
|
358
|
+
transmissionTint: [
|
|
359
|
+
0.68,
|
|
360
|
+
0.95,
|
|
361
|
+
0.88
|
|
362
|
+
],
|
|
363
|
+
extinctionScale: 1,
|
|
364
|
+
normalScale: 0.05,
|
|
365
|
+
scatteringDistribution: 0.7,
|
|
366
|
+
ior: 1.6,
|
|
367
|
+
roughness0: 0.6,
|
|
368
|
+
roughness1: 1.1,
|
|
369
|
+
lobeMix: 0.75
|
|
370
|
+
},
|
|
371
|
+
marble: {
|
|
372
|
+
surfaceAlbedo: [
|
|
373
|
+
0.95,
|
|
374
|
+
0.94,
|
|
375
|
+
0.92
|
|
376
|
+
],
|
|
377
|
+
meanFreePath: [
|
|
378
|
+
1.0,
|
|
379
|
+
0.96,
|
|
380
|
+
0.92
|
|
381
|
+
],
|
|
382
|
+
meanFreePathDistance: 0.035,
|
|
383
|
+
worldUnitScale: 1,
|
|
384
|
+
scatterScale: 1,
|
|
385
|
+
boundaryColorBleed: [
|
|
386
|
+
1,
|
|
387
|
+
1,
|
|
388
|
+
1
|
|
389
|
+
],
|
|
390
|
+
transmissionTint: [
|
|
391
|
+
0.96,
|
|
392
|
+
0.95,
|
|
393
|
+
0.94
|
|
394
|
+
],
|
|
395
|
+
extinctionScale: 1,
|
|
396
|
+
normalScale: 0.04,
|
|
397
|
+
scatteringDistribution: 0.6,
|
|
398
|
+
ior: 1.55,
|
|
399
|
+
roughness0: 0.55,
|
|
400
|
+
roughness1: 1.05,
|
|
401
|
+
lobeMix: 0.7
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
/**
|
|
405
|
+
* Subsurface profile for {@link SSSMaterial}, holding the parameters UE5's
|
|
406
|
+
* Burley diffusion is driven by.
|
|
407
|
+
*
|
|
408
|
+
* @remarks
|
|
409
|
+
* Profiles are packed into a shared GPU table keyed by {@link SkinProfile.id},
|
|
410
|
+
* and materials write that id per pixel. This lets several profiles — face, ears,
|
|
411
|
+
* lips — diffuse independently in a single screen-space pass, which is how UE5
|
|
412
|
+
* drives its subsurface scattering.
|
|
413
|
+
*
|
|
414
|
+
* This is a separate type from the older `SubsurfaceProfile`, which continues to
|
|
415
|
+
* serve `PBRMetallicRoughnessMaterial` and the generic SSS post effect. The two
|
|
416
|
+
* are independent and may be used side by side.
|
|
417
|
+
*
|
|
418
|
+
* @public
|
|
419
|
+
*/ class SkinProfile {
|
|
420
|
+
static _profiles = new Array(SKIN_PROFILE_CAPACITY).fill(null);
|
|
421
|
+
static _table = null;
|
|
422
|
+
/** Device {@link SkinProfile._table} belongs to, so a device swap can be spotted. */ static _tableDevice = null;
|
|
423
|
+
static _tableData = null;
|
|
424
|
+
static _tableDirty = true;
|
|
425
|
+
static _defaultProfile = null;
|
|
426
|
+
_id;
|
|
427
|
+
_surfaceAlbedo;
|
|
428
|
+
_meanFreePath;
|
|
429
|
+
_boundaryColorBleed;
|
|
430
|
+
_transmissionTint;
|
|
431
|
+
_preset;
|
|
432
|
+
_meanFreePathDistance;
|
|
433
|
+
_worldUnitScale;
|
|
434
|
+
_scatterScale;
|
|
435
|
+
_extinctionScale;
|
|
436
|
+
_normalScale;
|
|
437
|
+
_scatteringDistribution;
|
|
438
|
+
_ior;
|
|
439
|
+
_roughness0;
|
|
440
|
+
_roughness1;
|
|
441
|
+
_lobeMix;
|
|
442
|
+
_disposed;
|
|
443
|
+
_changeListeners;
|
|
444
|
+
/**
|
|
445
|
+
* Not constructible from outside. Use {@link SSSMaterial.subsurfaceProfile},
|
|
446
|
+
* which owns one for the material's lifetime.
|
|
447
|
+
*
|
|
448
|
+
* @remarks
|
|
449
|
+
* The token check is not redundant with `private`: profiles hold a row of a
|
|
450
|
+
* 256-entry GPU table that only {@link SkinProfile.dispose} returns, and the
|
|
451
|
+
* editor ships as prebuilt JavaScript that drives this class through
|
|
452
|
+
* serialization metadata, where TypeScript's visibility rules do not apply.
|
|
453
|
+
*
|
|
454
|
+
* @internal
|
|
455
|
+
*/ constructor(token, preset = 'skin'){
|
|
456
|
+
if (token !== CREATE_TOKEN) {
|
|
457
|
+
throw new Error('SkinProfile is not constructible directly; it is owned by the SSSMaterial that created it.');
|
|
458
|
+
}
|
|
459
|
+
this._surfaceAlbedo = new Vector3();
|
|
460
|
+
this._meanFreePath = new Vector3();
|
|
461
|
+
this._boundaryColorBleed = new Vector3();
|
|
462
|
+
this._transmissionTint = new Vector3();
|
|
463
|
+
this._preset = preset;
|
|
464
|
+
this._meanFreePathDistance = 0;
|
|
465
|
+
this._worldUnitScale = 1;
|
|
466
|
+
this._scatterScale = 1;
|
|
467
|
+
this._extinctionScale = 1;
|
|
468
|
+
this._normalScale = 0.08;
|
|
469
|
+
this._scatteringDistribution = 0.93;
|
|
470
|
+
this._ior = 1.55;
|
|
471
|
+
this._roughness0 = 0.75;
|
|
472
|
+
this._roughness1 = 1.3;
|
|
473
|
+
this._lobeMix = 0.85;
|
|
474
|
+
this._disposed = false;
|
|
475
|
+
this._changeListeners = new Set();
|
|
476
|
+
this._id = SkinProfile.allocateId(this);
|
|
477
|
+
this.applyPreset(preset);
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Registers a callback invoked whenever this profile's parameters change.
|
|
481
|
+
*
|
|
482
|
+
* @param listener - The callback to add.
|
|
483
|
+
*
|
|
484
|
+
* @public
|
|
485
|
+
*/ addChangeListener(listener) {
|
|
486
|
+
this._changeListeners.add(listener);
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Removes a previously registered change listener.
|
|
490
|
+
*
|
|
491
|
+
* @param listener - The callback to remove.
|
|
492
|
+
*
|
|
493
|
+
* @public
|
|
494
|
+
*/ removeChangeListener(listener) {
|
|
495
|
+
this._changeListeners.delete(listener);
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Creates a profile for a material to own.
|
|
499
|
+
*
|
|
500
|
+
* @remarks
|
|
501
|
+
* The only way to obtain one. The caller takes on releasing its table row — in
|
|
502
|
+
* practice {@link SSSMaterial}, which disposes the profile with itself.
|
|
503
|
+
*
|
|
504
|
+
* @param preset - Preset to start from. Defaults to `'skin'`.
|
|
505
|
+
* @returns The new profile.
|
|
506
|
+
*
|
|
507
|
+
* @internal
|
|
508
|
+
*/ static createOwned(preset = 'skin') {
|
|
509
|
+
return new SkinProfile(CREATE_TOKEN, preset);
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* The shared default skin profile.
|
|
513
|
+
*
|
|
514
|
+
* @remarks
|
|
515
|
+
* A fallback for consumers with no material to ask — the diffusion uses it for
|
|
516
|
+
* pixels whose id is not in the table. Created lazily, since it holds a table
|
|
517
|
+
* row for the lifetime of the process.
|
|
518
|
+
*
|
|
519
|
+
* @public
|
|
520
|
+
*/ static getDefault() {
|
|
521
|
+
if (!this._defaultProfile) {
|
|
522
|
+
this._defaultProfile = new SkinProfile(CREATE_TOKEN, 'skin');
|
|
523
|
+
}
|
|
524
|
+
return this._defaultProfile;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Looks up a profile by its packed id.
|
|
528
|
+
*
|
|
529
|
+
* @param id - The profile id, as written to the per-pixel profile channel.
|
|
530
|
+
* @returns The profile, or `null` when the id is unused.
|
|
531
|
+
*
|
|
532
|
+
* @public
|
|
533
|
+
*/ static getById(id) {
|
|
534
|
+
return id > 0 && id < SKIN_PROFILE_CAPACITY ? this._profiles[id] : null;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* The packed parameter table, rebuilt when any profile has changed.
|
|
538
|
+
*
|
|
539
|
+
* @remarks
|
|
540
|
+
* Rows are indexed by profile id, columns by parameter group. The skin
|
|
541
|
+
* diffusion passes sample this to recover per-pixel scattering parameters.
|
|
542
|
+
*
|
|
543
|
+
* @param device - Device used to create and upload the table.
|
|
544
|
+
* @returns The table texture, or `null` when it could not be created.
|
|
545
|
+
*
|
|
546
|
+
* @public
|
|
547
|
+
*/ static getTable(device) {
|
|
548
|
+
// The table is static and outlives any one device. A texture belonging to a
|
|
549
|
+
// dead device silently swallows every `update`, which strands the last values
|
|
550
|
+
// uploaded before the swap on screen until the page is reloaded.
|
|
551
|
+
if (this._table && (this._tableDevice !== device || this._table.disposed)) {
|
|
552
|
+
this._table = null;
|
|
553
|
+
this._tableData = null;
|
|
554
|
+
}
|
|
555
|
+
if (!this._table) {
|
|
556
|
+
this._table = device.createTexture2D('rgba32f', SKIN_PROFILE_COLUMNS, SKIN_PROFILE_CAPACITY, {
|
|
557
|
+
mipmapping: false,
|
|
558
|
+
samplerOptions: {
|
|
559
|
+
minFilter: 'nearest',
|
|
560
|
+
magFilter: 'nearest',
|
|
561
|
+
mipFilter: 'none'
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
this._tableDevice = device;
|
|
565
|
+
this._tableData = new Float32Array(SKIN_PROFILE_COLUMNS * SKIN_PROFILE_CAPACITY * 4);
|
|
566
|
+
this._tableDirty = true;
|
|
567
|
+
}
|
|
568
|
+
if (this._tableDirty && this._table && this._tableData) {
|
|
569
|
+
this.packTable(this._tableData);
|
|
570
|
+
this._table.update(this._tableData, 0, 0, SKIN_PROFILE_COLUMNS, SKIN_PROFILE_CAPACITY);
|
|
571
|
+
this._tableDirty = false;
|
|
572
|
+
}
|
|
573
|
+
return this._table;
|
|
574
|
+
}
|
|
575
|
+
/** Number of parameter columns in the packed table. @public */ static get tableColumns() {
|
|
576
|
+
return SKIN_PROFILE_COLUMNS;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Column the baked transmission profile starts at.
|
|
580
|
+
*
|
|
581
|
+
* @remarks
|
|
582
|
+
* UE5's `BSSS_TRANSMISSION_PROFILE_OFFSET`. Entry `i` lives at column
|
|
583
|
+
* `transmissionLutOffset + i`.
|
|
584
|
+
*
|
|
585
|
+
* @public
|
|
586
|
+
*/ static get transmissionLutOffset() {
|
|
587
|
+
return SKIN_PROFILE_PARAM_COLUMNS;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Column holding `(worldUnitScale, scatterScale, 0, 0)`.
|
|
591
|
+
*
|
|
592
|
+
* @remarks
|
|
593
|
+
* Read by the transmission thickness pass, which needs the world unit scale to
|
|
594
|
+
* size its normal shrink against the asset.
|
|
595
|
+
*
|
|
596
|
+
* @public
|
|
597
|
+
*/ static get scalingParamColumn() {
|
|
598
|
+
return 0;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Column holding `(extinctionScale, normalScale, scatteringDistribution,
|
|
602
|
+
* 1 / ior)`.
|
|
603
|
+
*
|
|
604
|
+
* @remarks
|
|
605
|
+
* UE5's `SSSS_TRANSMISSION_OFFSET`, unpacked by
|
|
606
|
+
* `GetTransmissionProfileParams`.
|
|
607
|
+
*
|
|
608
|
+
* @public
|
|
609
|
+
*/ static get transmissionParamColumn() {
|
|
610
|
+
return 4;
|
|
611
|
+
}
|
|
612
|
+
/** Number of entries in the baked transmission profile. @public */ static get transmissionLutSize() {
|
|
613
|
+
return SKIN_TRANSMISSION_LUT_SIZE;
|
|
614
|
+
}
|
|
615
|
+
/** Number of rows in the packed table. @public */ static get tableRows() {
|
|
616
|
+
return SKIN_PROFILE_CAPACITY;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* The id this profile occupies in the packed table.
|
|
620
|
+
*
|
|
621
|
+
* @remarks
|
|
622
|
+
* Ids start at 1; 0 means "not skin" in the per-pixel profile channel. The
|
|
623
|
+
* material encodes this as `id / 255`.
|
|
624
|
+
*
|
|
625
|
+
* @public
|
|
626
|
+
*/ get id() {
|
|
627
|
+
return this._id;
|
|
628
|
+
}
|
|
629
|
+
/** Normalized id as written to an 8-bit per-pixel channel. @public */ get encodedId() {
|
|
630
|
+
return this._id / 255;
|
|
631
|
+
}
|
|
632
|
+
/** The preset this profile was last initialized from. @public */ get preset() {
|
|
633
|
+
return this._preset;
|
|
634
|
+
}
|
|
635
|
+
set preset(val) {
|
|
636
|
+
if (val !== this._preset) {
|
|
637
|
+
this.applyPreset(val);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Per-channel scattering albedo.
|
|
642
|
+
*
|
|
643
|
+
* @remarks
|
|
644
|
+
* Drives Burley's shaping term `s = (albedo - 0.33)^4 * 100 + 3.5`, so values
|
|
645
|
+
* near 0.33 give the widest diffusion and values at either extreme tighten it.
|
|
646
|
+
*
|
|
647
|
+
* @public
|
|
648
|
+
*/ get surfaceAlbedo() {
|
|
649
|
+
return this._surfaceAlbedo;
|
|
650
|
+
}
|
|
651
|
+
set surfaceAlbedo(val) {
|
|
652
|
+
if (!val.equalsTo(this._surfaceAlbedo)) {
|
|
653
|
+
this._surfaceAlbedo.set(val);
|
|
654
|
+
this.notifyChanged();
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Per-channel diffuse mean free path, as a ratio.
|
|
659
|
+
*
|
|
660
|
+
* @remarks
|
|
661
|
+
* Scaled by {@link SkinProfile.meanFreePathDistance} to reach world units. The
|
|
662
|
+
* red channel is normally much longer than blue, which is what makes thin
|
|
663
|
+
* geometry such as an ear rim glow red.
|
|
664
|
+
*
|
|
665
|
+
* @public
|
|
666
|
+
*/ get meanFreePath() {
|
|
667
|
+
return this._meanFreePath;
|
|
668
|
+
}
|
|
669
|
+
set meanFreePath(val) {
|
|
670
|
+
if (!val.equalsTo(this._meanFreePath)) {
|
|
671
|
+
this._meanFreePath.set(val);
|
|
672
|
+
this.notifyChanged();
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
/** Mean free path of the widest channel, in profile space. @public */ get meanFreePathDistance() {
|
|
676
|
+
return this._meanFreePathDistance;
|
|
677
|
+
}
|
|
678
|
+
set meanFreePathDistance(val) {
|
|
679
|
+
const next = Math.max(0, val ?? 0);
|
|
680
|
+
if (next !== this._meanFreePathDistance) {
|
|
681
|
+
this._meanFreePathDistance = next;
|
|
682
|
+
this.notifyChanged();
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Profile-space to world-unit conversion, for scenes not authored in metres.
|
|
687
|
+
*
|
|
688
|
+
* @remarks
|
|
689
|
+
* Applied once, when the diffusion converts a scatter radius into a screen
|
|
690
|
+
* offset — the same place UE5 applies it, in `CalculateBurleyScale`. It is
|
|
691
|
+
* deliberately absent from {@link SkinProfile.getScatterDistance}.
|
|
692
|
+
*
|
|
693
|
+
* @public
|
|
694
|
+
*/ get worldUnitScale() {
|
|
695
|
+
return this._worldUnitScale;
|
|
696
|
+
}
|
|
697
|
+
set worldUnitScale(val) {
|
|
698
|
+
const next = Math.max(0.01, val ?? 1);
|
|
699
|
+
if (next !== this._worldUnitScale) {
|
|
700
|
+
this._worldUnitScale = next;
|
|
701
|
+
this.notifyChanged();
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
/** Overall multiplier on the diffusion width. @public */ get scatterScale() {
|
|
705
|
+
return this._scatterScale;
|
|
706
|
+
}
|
|
707
|
+
set scatterScale(val) {
|
|
708
|
+
const next = Math.max(0, val ?? 1);
|
|
709
|
+
if (next !== this._scatterScale) {
|
|
710
|
+
this._scatterScale = next;
|
|
711
|
+
this.notifyChanged();
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Per-channel tint applied where two different profiles meet.
|
|
716
|
+
*
|
|
717
|
+
* @remarks
|
|
718
|
+
* Taps that belong to another profile are attenuated by this instead of being
|
|
719
|
+
* rejected outright, so a face/lip boundary softens rather than forming a seam.
|
|
720
|
+
*
|
|
721
|
+
* @public
|
|
722
|
+
*/ get boundaryColorBleed() {
|
|
723
|
+
return this._boundaryColorBleed;
|
|
724
|
+
}
|
|
725
|
+
set boundaryColorBleed(val) {
|
|
726
|
+
if (!val.equalsTo(this._boundaryColorBleed)) {
|
|
727
|
+
this._boundaryColorBleed.set(val);
|
|
728
|
+
this.notifyChanged();
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
/** Tint applied to light transmitted through thin geometry. @public */ get transmissionTint() {
|
|
732
|
+
return this._transmissionTint;
|
|
733
|
+
}
|
|
734
|
+
set transmissionTint(val) {
|
|
735
|
+
if (!val.equalsTo(this._transmissionTint)) {
|
|
736
|
+
this._transmissionTint.set(val);
|
|
737
|
+
this.notifyChanged();
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
/** Extinction multiplier for transmission attenuation. @public */ get extinctionScale() {
|
|
741
|
+
return this._extinctionScale;
|
|
742
|
+
}
|
|
743
|
+
set extinctionScale(val) {
|
|
744
|
+
const next = Math.max(0, val ?? 1);
|
|
745
|
+
if (next !== this._extinctionScale) {
|
|
746
|
+
this._extinctionScale = next;
|
|
747
|
+
this.notifyChanged();
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/** How much surface normal detail survives the diffusion. @public */ get normalScale() {
|
|
751
|
+
return this._normalScale;
|
|
752
|
+
}
|
|
753
|
+
set normalScale(val) {
|
|
754
|
+
const next = Math.max(0, val ?? 0);
|
|
755
|
+
if (next !== this._normalScale) {
|
|
756
|
+
this._normalScale = next;
|
|
757
|
+
this.notifyChanged();
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Henyey-Greenstein asymmetry of the transmitted light, in `[-1, 1]`.
|
|
762
|
+
*
|
|
763
|
+
* @remarks
|
|
764
|
+
* UE5's `ScatteringDistribution`, default 0.93 for skin. Positive values throw
|
|
765
|
+
* the transmitted light forward, concentrating a backlit ear's glow where the
|
|
766
|
+
* light shines through rather than spreading it as a uniform wash.
|
|
767
|
+
*
|
|
768
|
+
* Only the transmission BxDF uses this; the screen-space diffusion is isotropic
|
|
769
|
+
* and ignores it. Stored raw, unlike UE5's `[0, 1]` remap for an 8-bit texture.
|
|
770
|
+
*
|
|
771
|
+
* @public
|
|
772
|
+
*/ get scatteringDistribution() {
|
|
773
|
+
return this._scatteringDistribution;
|
|
774
|
+
}
|
|
775
|
+
set scatteringDistribution(val) {
|
|
776
|
+
const next = Math.min(1, Math.max(-1, val ?? 0));
|
|
777
|
+
if (next !== this._scatteringDistribution) {
|
|
778
|
+
this._scatteringDistribution = next;
|
|
779
|
+
this.notifyChanged();
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Index of refraction used to bend the view ray before the phase function.
|
|
784
|
+
*
|
|
785
|
+
* @remarks
|
|
786
|
+
* UE5's `IOR`, default 1.55 for skin, stored in the table as `1 / ior` since
|
|
787
|
+
* that is the form `refract` takes. This drives transmission only — the
|
|
788
|
+
* specular Fresnel stays on the dielectric `F0 = 0.08 * Specular` mapping, as
|
|
789
|
+
* {@link SSSMaterial.specularF0} notes.
|
|
790
|
+
*
|
|
791
|
+
* @public
|
|
792
|
+
*/ get ior() {
|
|
793
|
+
return this._ior;
|
|
794
|
+
}
|
|
795
|
+
set ior(val) {
|
|
796
|
+
const next = Math.min(3, Math.max(1, val ?? 1.55));
|
|
797
|
+
if (next !== this._ior) {
|
|
798
|
+
this._ior = next;
|
|
799
|
+
this.notifyChanged();
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Multiplier on the material roughness for the narrow specular lobe.
|
|
804
|
+
*
|
|
805
|
+
* @remarks
|
|
806
|
+
* A direct multiplier, so 1 leaves the material roughness alone and skin's
|
|
807
|
+
* default 0.75 tightens the narrow lobe. The 0.5..2 range matches UE5's, whose
|
|
808
|
+
* halve-on-pack encoding has no counterpart here.
|
|
809
|
+
*
|
|
810
|
+
* @public
|
|
811
|
+
*/ get roughness0() {
|
|
812
|
+
return this._roughness0;
|
|
813
|
+
}
|
|
814
|
+
set roughness0(val) {
|
|
815
|
+
const next = Math.min(2, Math.max(0.5, val ?? 0.75));
|
|
816
|
+
if (next !== this._roughness0) {
|
|
817
|
+
this._roughness0 = next;
|
|
818
|
+
this.notifyChanged();
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Multiplier on the material roughness for the wide specular lobe.
|
|
823
|
+
*
|
|
824
|
+
* @remarks
|
|
825
|
+
* Same scaling as {@link SkinProfile.roughness0}; skin's default 1.3 broadens
|
|
826
|
+
* the second lobe, giving it a soft sheen alongside the tighter highlight.
|
|
827
|
+
*
|
|
828
|
+
* @public
|
|
829
|
+
*/ get roughness1() {
|
|
830
|
+
return this._roughness1;
|
|
831
|
+
}
|
|
832
|
+
set roughness1(val) {
|
|
833
|
+
const next = Math.min(2, Math.max(0.5, val ?? 1.3));
|
|
834
|
+
if (next !== this._roughness1) {
|
|
835
|
+
this._roughness1 = next;
|
|
836
|
+
this.notifyChanged();
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
/** Blend between the narrow and wide specular lobes. @public */ get lobeMix() {
|
|
840
|
+
return this._lobeMix;
|
|
841
|
+
}
|
|
842
|
+
set lobeMix(val) {
|
|
843
|
+
const next = Math.min(0.9, Math.max(0.1, val ?? 0.85));
|
|
844
|
+
if (next !== this._lobeMix) {
|
|
845
|
+
this._lobeMix = next;
|
|
846
|
+
this.notifyChanged();
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Per-channel diffusion distance, in profile space.
|
|
851
|
+
*
|
|
852
|
+
* @remarks
|
|
853
|
+
* Deliberately *not* scaled by {@link SkinProfile.worldUnitScale}, which UE5
|
|
854
|
+
* applies later in `CalculateBurleyScale`; applying it in both places would
|
|
855
|
+
* make the diffusion scale with its square. The result must therefore be read
|
|
856
|
+
* together with that factor to reach world units.
|
|
857
|
+
*
|
|
858
|
+
* @returns Mean free path scaled by the profile's distance and scatter scale.
|
|
859
|
+
*
|
|
860
|
+
* @public
|
|
861
|
+
*/ getScatterDistance() {
|
|
862
|
+
const s = this._meanFreePathDistance * this._scatterScale;
|
|
863
|
+
return new Vector3(this._meanFreePath.x * s, this._meanFreePath.y * s, this._meanFreePath.z * s);
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Overwrites every parameter from another profile.
|
|
867
|
+
*
|
|
868
|
+
* @remarks
|
|
869
|
+
* The only way to transfer a look, since profiles cannot be shared or cloned.
|
|
870
|
+
* The table row is untouched: it identifies the profile, not its contents.
|
|
871
|
+
*
|
|
872
|
+
* @param other - Profile to copy the parameters from.
|
|
873
|
+
*
|
|
874
|
+
* @public
|
|
875
|
+
*/ copyFrom(other) {
|
|
876
|
+
this._preset = other._preset;
|
|
877
|
+
this._surfaceAlbedo.set(other._surfaceAlbedo);
|
|
878
|
+
this._meanFreePath.set(other._meanFreePath);
|
|
879
|
+
this._boundaryColorBleed.set(other._boundaryColorBleed);
|
|
880
|
+
this._transmissionTint.set(other._transmissionTint);
|
|
881
|
+
this._meanFreePathDistance = other._meanFreePathDistance;
|
|
882
|
+
this._worldUnitScale = other._worldUnitScale;
|
|
883
|
+
this._scatterScale = other._scatterScale;
|
|
884
|
+
this._extinctionScale = other._extinctionScale;
|
|
885
|
+
this._normalScale = other._normalScale;
|
|
886
|
+
this._scatteringDistribution = other._scatteringDistribution;
|
|
887
|
+
this._ior = other._ior;
|
|
888
|
+
this._roughness0 = other._roughness0;
|
|
889
|
+
this._roughness1 = other._roughness1;
|
|
890
|
+
this._lobeMix = other._lobeMix;
|
|
891
|
+
this.notifyChanged();
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Releases this profile's table slot.
|
|
895
|
+
*
|
|
896
|
+
* @public
|
|
897
|
+
*/ dispose() {
|
|
898
|
+
if (this._disposed) {
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
this._disposed = true;
|
|
902
|
+
if (SkinProfile._profiles[this._id] === this) {
|
|
903
|
+
SkinProfile._profiles[this._id] = null;
|
|
904
|
+
SkinProfile.markDirty();
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
applyPreset(preset) {
|
|
908
|
+
const t = SKIN_PROFILE_TEMPLATES[preset] ?? SKIN_PROFILE_TEMPLATES.skin;
|
|
909
|
+
this._preset = preset;
|
|
910
|
+
this._surfaceAlbedo.setXYZ(t.surfaceAlbedo[0], t.surfaceAlbedo[1], t.surfaceAlbedo[2]);
|
|
911
|
+
this._meanFreePath.setXYZ(t.meanFreePath[0], t.meanFreePath[1], t.meanFreePath[2]);
|
|
912
|
+
this._boundaryColorBleed.setXYZ(t.boundaryColorBleed[0], t.boundaryColorBleed[1], t.boundaryColorBleed[2]);
|
|
913
|
+
this._transmissionTint.setXYZ(t.transmissionTint[0], t.transmissionTint[1], t.transmissionTint[2]);
|
|
914
|
+
this._meanFreePathDistance = t.meanFreePathDistance;
|
|
915
|
+
this._worldUnitScale = t.worldUnitScale;
|
|
916
|
+
this._scatterScale = t.scatterScale;
|
|
917
|
+
this._extinctionScale = t.extinctionScale;
|
|
918
|
+
this._normalScale = t.normalScale;
|
|
919
|
+
this._scatteringDistribution = t.scatteringDistribution;
|
|
920
|
+
this._ior = t.ior;
|
|
921
|
+
this._roughness0 = t.roughness0;
|
|
922
|
+
this._roughness1 = t.roughness1;
|
|
923
|
+
this._lobeMix = t.lobeMix;
|
|
924
|
+
this.notifyChanged();
|
|
925
|
+
}
|
|
926
|
+
static allocateId(profile) {
|
|
927
|
+
for(let i = 1; i < SKIN_PROFILE_CAPACITY; i++){
|
|
928
|
+
if (!this._profiles[i]) {
|
|
929
|
+
this._profiles[i] = profile;
|
|
930
|
+
this.markDirty();
|
|
931
|
+
return i;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
throw new Error('SkinProfile limit exceeded');
|
|
935
|
+
}
|
|
936
|
+
static markDirty() {
|
|
937
|
+
this._tableDirty = true;
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Flags the packed table for a rebuild and notifies materials using this
|
|
941
|
+
* profile so they can refresh their uniforms.
|
|
942
|
+
*/ notifyChanged() {
|
|
943
|
+
SkinProfile.markDirty();
|
|
944
|
+
this._changeListeners.forEach((listener)=>listener());
|
|
945
|
+
}
|
|
946
|
+
static packTable(out) {
|
|
947
|
+
out.fill(0);
|
|
948
|
+
const stride = SKIN_PROFILE_COLUMNS * 4;
|
|
949
|
+
for(let id = 1; id < SKIN_PROFILE_CAPACITY; id++){
|
|
950
|
+
const p = this._profiles[id];
|
|
951
|
+
if (!p) {
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
const row = id * stride;
|
|
955
|
+
const write = (column, x, y, z, w)=>{
|
|
956
|
+
const o = row + column * 4;
|
|
957
|
+
out[o] = x;
|
|
958
|
+
out[o + 1] = y;
|
|
959
|
+
out[o + 2] = z;
|
|
960
|
+
out[o + 3] = w;
|
|
961
|
+
};
|
|
962
|
+
// The diffusion draws its radii from one representative channel, kept in
|
|
963
|
+
// the `w` of both rows as UE5 does, and evaluates all three kernels at
|
|
964
|
+
// those radii. The widest channel represents, so the sample distribution
|
|
965
|
+
// covers every channel's tail.
|
|
966
|
+
const d = p.getScatterDistance();
|
|
967
|
+
const widest = Math.max(d.x, d.y, d.z);
|
|
968
|
+
const albedoForSampling = widest > 0 ? [
|
|
969
|
+
p._surfaceAlbedo.x,
|
|
970
|
+
p._surfaceAlbedo.y,
|
|
971
|
+
p._surfaceAlbedo.z
|
|
972
|
+
][[
|
|
973
|
+
d.x,
|
|
974
|
+
d.y,
|
|
975
|
+
d.z
|
|
976
|
+
].indexOf(widest)] : p._surfaceAlbedo.x;
|
|
977
|
+
write(0, p._worldUnitScale, p._scatterScale, 0, 0);
|
|
978
|
+
write(1, p._surfaceAlbedo.x, p._surfaceAlbedo.y, p._surfaceAlbedo.z, albedoForSampling);
|
|
979
|
+
write(2, d.x, d.y, d.z, widest);
|
|
980
|
+
write(3, p._boundaryColorBleed.x, p._boundaryColorBleed.y, p._boundaryColorBleed.z, 0);
|
|
981
|
+
write(4, p._extinctionScale, p._normalScale, p._scatteringDistribution, 1 / Math.max(p._ior, 1e-4));
|
|
982
|
+
write(5, p._roughness0, p._roughness1, p._lobeMix, 0);
|
|
983
|
+
// The LUT columns are consecutive within the row, so the baked profile
|
|
984
|
+
// drops straight in after the scalar parameters.
|
|
985
|
+
p.writeTransmissionProfile(out, row + SKIN_PROFILE_PARAM_COLUMNS * 4);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Bakes this profile's transmission table.
|
|
990
|
+
*
|
|
991
|
+
* @remarks
|
|
992
|
+
* `ComputeTransmissionProfileBurley` (`BurleyNormalizedSSS.cpp:171`),
|
|
993
|
+
* transcribed. Each entry is the Burley diffusion profile integrated from that
|
|
994
|
+
* radius out to infinity,
|
|
995
|
+
*
|
|
996
|
+
* ```
|
|
997
|
+
* T(r) = 0.25 A (exp(-s r / L) + 3 exp(-s r / 3L))
|
|
998
|
+
* ```
|
|
999
|
+
*
|
|
1000
|
+
* evaluated per channel at its own `s` and `L`, times the transmission tint.
|
|
1001
|
+
* The surface albedo is absent — UE5 passes white for `A` — so this carries
|
|
1002
|
+
* only the *shape* of the falloff; the base colour is applied where the
|
|
1003
|
+
* transmission joins the diffuse.
|
|
1004
|
+
*
|
|
1005
|
+
* The alpha is `exp(-distance x extinctionScale)`, UE5's separate "SSSS shadow"
|
|
1006
|
+
* curve. The BxDF reads `.rgb` only, but storing it costs nothing.
|
|
1007
|
+
*
|
|
1008
|
+
* @param out - Destination, `4 x transmissionLutSize` floats from `offset`.
|
|
1009
|
+
* @param offset - Index of the first float to write.
|
|
1010
|
+
*
|
|
1011
|
+
* @public
|
|
1012
|
+
*/ writeTransmissionProfile(out, offset = 0) {
|
|
1013
|
+
// The world unit scale applies to this table's *distance axis*, not to the
|
|
1014
|
+
// mean free paths, which UE5 likewise leaves unscaled.
|
|
1015
|
+
const invUnitScale = 1 / Math.max(this._worldUnitScale, 1e-4);
|
|
1016
|
+
const d = this.getScatterDistance();
|
|
1017
|
+
const l = [
|
|
1018
|
+
d.x * WORLD_UNITS_TO_PROFILE_MM,
|
|
1019
|
+
d.y * WORLD_UNITS_TO_PROFILE_MM,
|
|
1020
|
+
d.z * WORLD_UNITS_TO_PROFILE_MM
|
|
1021
|
+
];
|
|
1022
|
+
const s = [
|
|
1023
|
+
searchLightDiffuseScalingFactor(this._surfaceAlbedo.x),
|
|
1024
|
+
searchLightDiffuseScalingFactor(this._surfaceAlbedo.y),
|
|
1025
|
+
searchLightDiffuseScalingFactor(this._surfaceAlbedo.z)
|
|
1026
|
+
];
|
|
1027
|
+
const tint = [
|
|
1028
|
+
this._transmissionTint.x,
|
|
1029
|
+
this._transmissionTint.y,
|
|
1030
|
+
this._transmissionTint.z
|
|
1031
|
+
];
|
|
1032
|
+
const offsetMM = TRANSMISSION_LUT_RADIUS_OFFSET_MM * invUnitScale;
|
|
1033
|
+
for(let i = 0; i < SKIN_TRANSMISSION_LUT_SIZE; i++){
|
|
1034
|
+
const o = offset + i * 4;
|
|
1035
|
+
// UE5's `bMakeLastPixelBlack`: 50 mm leaves a red tail still visible after
|
|
1036
|
+
// tone mapping, so forcing the last entry black is what makes anything
|
|
1037
|
+
// thicker than the table stop transmitting outright.
|
|
1038
|
+
if (i === SKIN_TRANSMISSION_LUT_SIZE - 1) {
|
|
1039
|
+
out[o] = 0;
|
|
1040
|
+
out[o + 1] = 0;
|
|
1041
|
+
out[o + 2] = 0;
|
|
1042
|
+
out[o + 3] = 0;
|
|
1043
|
+
break;
|
|
1044
|
+
}
|
|
1045
|
+
// Note the divisor is the table size, not `size - 1`: the last entry is
|
|
1046
|
+
// blacked out anyway, so UE5 spends the axis on the entries that survive.
|
|
1047
|
+
const distanceMM = i / SKIN_TRANSMISSION_LUT_SIZE * TRANSMISSION_LUT_MAX_DISTANCE_MM * invUnitScale;
|
|
1048
|
+
const r = distanceMM + offsetMM;
|
|
1049
|
+
for(let c = 0; c < 3; c++){
|
|
1050
|
+
out[o + c] = l[c] > 0 ? 0.25 * (Math.exp(-s[c] * r / l[c]) + 3 * Math.exp(-s[c] * r / (3 * l[c]))) * tint[c] : 0;
|
|
1051
|
+
}
|
|
1052
|
+
out[o + 3] = Math.exp(-distanceMM * this._extinctionScale);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
export { SKIN_MAX_TRANSMISSION_OPTICAL_DEPTH, SKIN_OPTICAL_DEPTH_PER_WORLD_UNIT, SKIN_TRANSMISSION_NO_DATA_ENCODING, SKIN_TRANSMISSION_OPTICAL_DEPTH_BIAS, SKIN_TRANSMISSION_OPTICAL_DEPTH_FLOOR, SkinProfile };
|
|
1058
|
+
//# sourceMappingURL=skinprofile.js.map
|