@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
package/dist/render/sky.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { linearToGamma } from '../shaders/misc.js';
|
|
2
2
|
import { smoothNoise3D } from '../shaders/noise.js';
|
|
3
3
|
import { getDevice, getEngine } from '../app/api.js';
|
|
4
|
-
import { getDefaultAtmosphereParams, getAerialPerspectiveLut, renderAtmosphereLUTs, getAtmosphereParamsStruct, createTransmittanceLutProgram, createMultiScatteringLutProgram, createSkyViewLutProgram, createAPLutProgram, skyBox, atmosphereLUTRendered, getTransmittanceLut, getSkyViewLut } from '../shaders/atmosphere.js';
|
|
5
|
-
import { Matrix4x4, Disposable, DRef, Vector4,
|
|
4
|
+
import { getDefaultAtmosphereParams, getAerialPerspectiveLut, renderAtmosphereLUTs, MIN_OBSERVER_ALTITUDE, getMultiScatteringLut, getAtmosphereParamsStruct, createTransmittanceLutProgram, createMultiScatteringLutProgram, createSkyViewLutProgram, createAPLutProgram, skyBox, rayIntersectSphere, transmittanceToSky, atmosphereLUTRendered, getTransmittanceLut, getSkyViewLut } from '../shaders/atmosphere.js';
|
|
5
|
+
import { Matrix4x4, Disposable, DRef, Vector4, Vector3, Vector2, CubeFace, objectKeys, DEPTH_FARTHEST, DEPTH_COMPARE_DEFAULT, DEPTH_COMPARE_FARTHER } from '@zephyr3d/base';
|
|
6
6
|
import { Primitive } from './primitive.js';
|
|
7
|
+
import '../node_modules/meshoptimizer/meshopt_simplifier.js';
|
|
7
8
|
import { BoxShape } from '../shapes/box.js';
|
|
8
9
|
import '../shapes/capsule.js';
|
|
9
10
|
import '../shapes/cylinder.js';
|
|
@@ -20,14 +21,22 @@ import { uniformSphereSamples, Fog } from '../values.js';
|
|
|
20
21
|
import { getDefaultHeightFogParams, MAX_FOG_HEIGHT, getHeightFogParamsStruct, calculateFog } from '../shaders/fog.js';
|
|
21
22
|
import { drawFullscreenQuad } from './fullscreenquad.js';
|
|
22
23
|
import { panoramaToCubemap } from '../utility/panorama.js';
|
|
24
|
+
import { VolumetricCloud } from './volumetric_cloud/volumetric_cloud.js';
|
|
25
|
+
import { VolumetricCloudResources } from './volumetric_cloud/resources.js';
|
|
26
|
+
import { drawVolumetricCloudDebug } from './volumetric_cloud/debug_view.js';
|
|
27
|
+
import { VolumetricCloudSkyCache } from './volumetric_cloud/sky_cache.js';
|
|
28
|
+
import { cloudCacheEncode } from './volumetric_cloud/cloud_common.js';
|
|
23
29
|
import '../camera/camera.js';
|
|
24
30
|
import { PerspectiveCamera } from '../camera/perspectivecamera.js';
|
|
31
|
+
import '../camera/follow.js';
|
|
25
32
|
|
|
26
33
|
const fogTypeMap = {
|
|
27
34
|
height_fog: Fog.FOG_TYPE_HEIGHT,
|
|
28
35
|
none: Fog.FOG_TYPE_NONE
|
|
29
36
|
};
|
|
30
37
|
const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
38
|
+
const SKY_OUTPUT_MAX = 64512 * 0.5;
|
|
39
|
+
/** Volumetric clouds give way to procedural ones this far below the cloud base, in meters */ const VOLUMETRIC_CLOUD_CEILING_MARGIN = 500;
|
|
31
40
|
/**
|
|
32
41
|
* The sky renderer
|
|
33
42
|
* @public
|
|
@@ -97,10 +106,29 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
97
106
|
_radianceMapWidth;
|
|
98
107
|
_atmosphereParams;
|
|
99
108
|
_atmosphereExposure;
|
|
109
|
+
_lowerHemisphereIsBlack;
|
|
110
|
+
_physicalSunDisk;
|
|
111
|
+
/** Observer local up (planet center to observer) of the current frame, in world space. */ _observerUp;
|
|
112
|
+
/** Observer altitude and local up the IBL was last baked at; altitude < 0 when never baked. */ _bakedObserverAltitude;
|
|
113
|
+
_bakedObserverUp;
|
|
114
|
+
_rebakeAltitudeRatio;
|
|
115
|
+
_rebakeAngle;
|
|
100
116
|
_fogType;
|
|
101
117
|
_heightFogParams;
|
|
102
118
|
_cloudy;
|
|
103
119
|
_cloudIntensity;
|
|
120
|
+
_cloudMode;
|
|
121
|
+
_volumetricCloud;
|
|
122
|
+
_volumetricCloudResources;
|
|
123
|
+
_volumetricCloudCache;
|
|
124
|
+
/** Volumetric clouds drawn this frame: requested, supported and the observer below the layer */ _volumetricActive;
|
|
125
|
+
_bindgroupSkyVolumetric;
|
|
126
|
+
static _programSkyVolumetric = null;
|
|
127
|
+
static _volumetricAltitudeWarned = false;
|
|
128
|
+
_cloudDebugView;
|
|
129
|
+
_cloudDebugSlice;
|
|
130
|
+
_cloudDebugChannel;
|
|
131
|
+
static _volumetricFallbackWarned = false;
|
|
104
132
|
_debugAerialPerspective;
|
|
105
133
|
_wind;
|
|
106
134
|
_skyboxRotation;
|
|
@@ -125,6 +153,7 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
125
153
|
* @internal Ratio converting the fog shader's atmosphere-derived LUT samples from the fixed bake
|
|
126
154
|
* exposure to the live one (1 in legacy).
|
|
127
155
|
*/ _fogPreExposure;
|
|
156
|
+
/** @internal Cubemap latched in update() for the height fog sky light term. */ _fogSkyLightCubemap;
|
|
128
157
|
/**
|
|
129
158
|
* Creates an instance of SkyRenderer
|
|
130
159
|
*/ constructor(){
|
|
@@ -147,15 +176,33 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
147
176
|
this._irradianceFrameBuffer = new DRef();
|
|
148
177
|
this._atmosphereParams = getDefaultAtmosphereParams();
|
|
149
178
|
this._atmosphereExposure = 1;
|
|
179
|
+
this._lowerHemisphereIsBlack = true;
|
|
180
|
+
this._physicalSunDisk = false;
|
|
181
|
+
this._observerUp = Vector3.axisPY();
|
|
182
|
+
this._bakedObserverAltitude = -1;
|
|
183
|
+
this._bakedObserverUp = Vector3.axisPY();
|
|
184
|
+
this._rebakeAltitudeRatio = 0.05;
|
|
185
|
+
this._rebakeAngle = 0.5;
|
|
150
186
|
this._debugAerialPerspective = 0;
|
|
151
187
|
this._fogType = 'height_fog';
|
|
152
188
|
this._heightFogParams = getDefaultHeightFogParams();
|
|
153
189
|
this._cloudy = 0.45;
|
|
154
190
|
this._cloudIntensity = 15;
|
|
191
|
+
this._cloudMode = 'procedural';
|
|
192
|
+
this._volumetricCloud = new VolumetricCloud();
|
|
193
|
+
this._volumetricCloudResources = new VolumetricCloudResources();
|
|
194
|
+
this._volumetricCloudCache = new VolumetricCloudSkyCache();
|
|
195
|
+
this._volumetricActive = false;
|
|
196
|
+
this._bindgroupSkyVolumetric = new DRef();
|
|
197
|
+
this._cloudDebugView = 'none';
|
|
198
|
+
this._cloudDebugSlice = 0;
|
|
199
|
+
this._cloudDebugChannel = 0;
|
|
155
200
|
this._wind = new Vector2(0, 0);
|
|
156
201
|
this._skyboxRotation = new Vector3(0, 0, 0);
|
|
157
202
|
this._skyWorldMatrix = defaultSkyWorldMatrix;
|
|
158
|
-
|
|
203
|
+
// Copied: without a sun light _getSunDir returns the shared ShaderHelper.defaultSunDir, which
|
|
204
|
+
// update() would otherwise overwrite with the last scene's sun direction.
|
|
205
|
+
this._lastSunDir = new Vector3(SkyRenderer._getSunDir(null));
|
|
159
206
|
this._lastSunColor = SkyRenderer._getSunColor(null);
|
|
160
207
|
this._panoramaAsset = '';
|
|
161
208
|
this._shProjector = new CubemapSHProjector(10000);
|
|
@@ -169,6 +216,7 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
169
216
|
this._format = null;
|
|
170
217
|
this._fogLuminanceScale = 1;
|
|
171
218
|
this._fogPreExposure = 1;
|
|
219
|
+
this._fogSkyLightCubemap = null;
|
|
172
220
|
}
|
|
173
221
|
/** @internal */ getHash(_ctx) {
|
|
174
222
|
return `${this.skyType}:${this.fogType}`;
|
|
@@ -272,7 +320,10 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
272
320
|
this.invalidate();
|
|
273
321
|
}
|
|
274
322
|
}
|
|
275
|
-
/**
|
|
323
|
+
/**
|
|
324
|
+
* Distance covered by the aerial perspective LUT, in atmosphere meters (world distance times
|
|
325
|
+
* {@link SkyRenderer.cameraHeightScale}). Beyond it aerial perspective no longer increases.
|
|
326
|
+
*/ get aerialPerspectiveDistance() {
|
|
276
327
|
return this._atmosphereParams.apDistance;
|
|
277
328
|
}
|
|
278
329
|
set aerialPerspectiveDistance(val) {
|
|
@@ -281,6 +332,40 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
281
332
|
this.invalidate();
|
|
282
333
|
}
|
|
283
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Whether the environment lighting baked from the scattering sky sees black below the horizon.
|
|
337
|
+
*/ get lowerHemisphereIsBlack() {
|
|
338
|
+
return this._lowerHemisphereIsBlack;
|
|
339
|
+
}
|
|
340
|
+
set lowerHemisphereIsBlack(val) {
|
|
341
|
+
if (val !== this._lowerHemisphereIsBlack) {
|
|
342
|
+
this._lowerHemisphereIsBlack = val;
|
|
343
|
+
this.invalidate();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Strength of the aerial perspective: scales the distance the atmosphere is integrated over
|
|
348
|
+
* between the camera and the scene, so distant objects get hazier (>1) or clearer (<1). The sky
|
|
349
|
+
* itself is unaffected.
|
|
350
|
+
*/ get aerialPerspectiveViewDistanceScale() {
|
|
351
|
+
return this._atmosphereParams.apViewDistanceScale;
|
|
352
|
+
}
|
|
353
|
+
set aerialPerspectiveViewDistanceScale(val) {
|
|
354
|
+
val = Math.max(0, val);
|
|
355
|
+
if (val !== this._atmosphereParams.apViewDistanceScale) {
|
|
356
|
+
this._atmosphereParams.apViewDistanceScale = val;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Distance from the camera where aerial perspective starts, in the same units as
|
|
361
|
+
* {@link SkyRenderer.aerialPerspectiveDistance} (atmosphere meters). Nearer surfaces get no
|
|
362
|
+
* atmospheric haze.
|
|
363
|
+
*/ get aerialPerspectiveStartDepth() {
|
|
364
|
+
return this._atmosphereParams.apStartDepth;
|
|
365
|
+
}
|
|
366
|
+
set aerialPerspectiveStartDepth(val) {
|
|
367
|
+
this._atmosphereParams.apStartDepth = Math.max(0, val);
|
|
368
|
+
}
|
|
284
369
|
/** Atmosphere exposure */ get atmosphereExposure() {
|
|
285
370
|
return this._atmosphereExposure;
|
|
286
371
|
}
|
|
@@ -290,7 +375,52 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
290
375
|
this.invalidate();
|
|
291
376
|
}
|
|
292
377
|
}
|
|
293
|
-
/**
|
|
378
|
+
/**
|
|
379
|
+
* Color of the virtual planet ground of the scattering sky.
|
|
380
|
+
*
|
|
381
|
+
* @remarks
|
|
382
|
+
* Shown below the horizon where no scene geometry covers the sky, lit by the sun through the
|
|
383
|
+
* atmosphere, and bounced back into the sky as part of multiple scattering.
|
|
384
|
+
*/ get groundAlbedo() {
|
|
385
|
+
return this._atmosphereParams.groundAlbedo;
|
|
386
|
+
}
|
|
387
|
+
set groundAlbedo(val) {
|
|
388
|
+
if (!val.equalsTo(this._atmosphereParams.groundAlbedo)) {
|
|
389
|
+
this._atmosphereParams.groundAlbedo.set(val);
|
|
390
|
+
this.invalidate();
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Relative change of the observer altitude that re-bakes the environment lighting of the
|
|
395
|
+
* scattering sky, e.g. 0.05 re-bakes after climbing or descending 5% (and at least 10 m).
|
|
396
|
+
*
|
|
397
|
+
* @remarks
|
|
398
|
+
* The on-screen sky and aerial perspective follow the camera every frame; only the cached IBL
|
|
399
|
+
* bake, which is expensive, waits for this threshold or {@link SkyRenderer.atmosphereRebakeAngle}.
|
|
400
|
+
*/ get atmosphereRebakeAltitudeRatio() {
|
|
401
|
+
return this._rebakeAltitudeRatio;
|
|
402
|
+
}
|
|
403
|
+
set atmosphereRebakeAltitudeRatio(val) {
|
|
404
|
+
this._rebakeAltitudeRatio = Math.max(0, val);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Angle in degrees the local vertical may turn, by travelling across the curved planet, before
|
|
408
|
+
* the environment lighting of the scattering sky is re-baked. 0.5 degrees is about 55 km on Earth.
|
|
409
|
+
*/ get atmosphereRebakeAngle() {
|
|
410
|
+
return this._rebakeAngle;
|
|
411
|
+
}
|
|
412
|
+
set atmosphereRebakeAngle(val) {
|
|
413
|
+
this._rebakeAngle = Math.max(0, val);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Atmosphere meters per world unit.
|
|
417
|
+
*
|
|
418
|
+
* @remarks
|
|
419
|
+
* The planet top sits at the world origin with its center straight below.
|
|
420
|
+
* The camera position, scaled by this factor, places the observer in the
|
|
421
|
+
* atmosphere: its altitude, and for large horizontal distances where on the
|
|
422
|
+
* curved planet it stands. Aerial perspective distances are scaled the same way.
|
|
423
|
+
*/ get cameraHeightScale() {
|
|
294
424
|
return this._atmosphereParams.cameraHeightScale;
|
|
295
425
|
}
|
|
296
426
|
set cameraHeightScale(val) {
|
|
@@ -390,6 +520,38 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
390
520
|
}
|
|
391
521
|
}
|
|
392
522
|
/**
|
|
523
|
+
* How much distant height fog takes the color of the environment behind it (0..1).
|
|
524
|
+
*/ get heightFogSkyLightStrength() {
|
|
525
|
+
return this._heightFogParams.skyLightStrength;
|
|
526
|
+
}
|
|
527
|
+
set heightFogSkyLightStrength(val) {
|
|
528
|
+
if (val !== this._heightFogParams.skyLightStrength) {
|
|
529
|
+
this._heightFogParams.skyLightStrength = val;
|
|
530
|
+
this.invalidate();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Blurriness of the environment seen through the fog, as a roughness selecting the radiance map
|
|
535
|
+
* mip.
|
|
536
|
+
*/ get heightFogSkyLightRoughness() {
|
|
537
|
+
return this._heightFogParams.skyLightRoughness;
|
|
538
|
+
}
|
|
539
|
+
set heightFogSkyLightRoughness(val) {
|
|
540
|
+
if (val !== this._heightFogParams.skyLightRoughness) {
|
|
541
|
+
this._heightFogParams.skyLightRoughness = val;
|
|
542
|
+
this.invalidate();
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* @internal
|
|
547
|
+
* Cubemap the height fog samples for {@link SkyRenderer.heightFogSkyLightStrength}: the scene's
|
|
548
|
+
* IBL radiance map, or the sky's own radiance map as a placeholder binding when there is none
|
|
549
|
+
* (the fog then sees a zero scale and skips it).
|
|
550
|
+
*/ getFogSkyLightCubemap(ctx) {
|
|
551
|
+
const light = ctx.scene.env.light;
|
|
552
|
+
return light.type === 'ibl' && light.radianceMap || this.radianceMap;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
393
555
|
* Light density of the sky.
|
|
394
556
|
*
|
|
395
557
|
* @remarks
|
|
@@ -416,6 +578,98 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
416
578
|
}
|
|
417
579
|
}
|
|
418
580
|
/**
|
|
581
|
+
* How the clouds of the scatter sky are drawn.
|
|
582
|
+
*
|
|
583
|
+
* @remarks
|
|
584
|
+
* `volumetric` needs a WebGPU device; on other devices the procedural clouds are drawn instead,
|
|
585
|
+
* see {@link SkyRenderer.effectiveCloudMode}.
|
|
586
|
+
*/ get cloudMode() {
|
|
587
|
+
return this._cloudMode;
|
|
588
|
+
}
|
|
589
|
+
set cloudMode(val) {
|
|
590
|
+
if (val !== this._cloudMode) {
|
|
591
|
+
this._cloudMode = val;
|
|
592
|
+
this.invalidate();
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
/** The cloud mode actually drawn: `volumetric` falls back to `procedural` off WebGPU. */ get effectiveCloudMode() {
|
|
596
|
+
if (this._cloudMode === 'volumetric' && getDevice().type !== 'webgpu') {
|
|
597
|
+
if (!SkyRenderer._volumetricFallbackWarned) {
|
|
598
|
+
SkyRenderer._volumetricFallbackWarned = true;
|
|
599
|
+
console.warn('Volumetric clouds require WebGPU, drawing procedural clouds instead');
|
|
600
|
+
}
|
|
601
|
+
return 'procedural';
|
|
602
|
+
}
|
|
603
|
+
return this._cloudMode;
|
|
604
|
+
}
|
|
605
|
+
/** Parameters of the volumetric clouds. */ get volumetricCloud() {
|
|
606
|
+
return this._volumetricCloud;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Base shape noise of the volumetric clouds, as a 2D atlas of the slices of a cubic volume,
|
|
610
|
+
* or null to generate it.
|
|
611
|
+
*
|
|
612
|
+
* @remarks
|
|
613
|
+
* An N^3 volume is stored as N slices of N x N texels laid out row by row, left to right, so
|
|
614
|
+
* that the atlas holds width * height = N^3 texels, e.g. 128^3 as 2048 x 1024 (16 x 8 slices).
|
|
615
|
+
* Channels: R Perlin-Worley, GBA Worley fbm of increasing frequency.
|
|
616
|
+
*/ get cloudBaseNoise() {
|
|
617
|
+
return this._volumetricCloudResources.baseNoiseSource;
|
|
618
|
+
}
|
|
619
|
+
set cloudBaseNoise(tex) {
|
|
620
|
+
this._volumetricCloudResources.baseNoiseSource = tex;
|
|
621
|
+
this._volumetricCloudCache.invalidate();
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Detail noise of the volumetric clouds as a slice atlas (see {@link SkyRenderer.cloudBaseNoise}),
|
|
625
|
+
* or null to generate it. Channels: RGB Worley fbm of increasing frequency.
|
|
626
|
+
*/ get cloudDetailNoise() {
|
|
627
|
+
return this._volumetricCloudResources.detailNoiseSource;
|
|
628
|
+
}
|
|
629
|
+
set cloudDetailNoise(tex) {
|
|
630
|
+
this._volumetricCloudResources.detailNoiseSource = tex;
|
|
631
|
+
this._volumetricCloudCache.invalidate();
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Weather map (the cloud map of Nubis 2017) of the volumetric clouds, or null to generate it.
|
|
635
|
+
* Channels: R coverage of the connected formation, G coverage of the isolated formation (clouds
|
|
636
|
+
* grow where it is highest; Coverage lowers the threshold, rather than prescribing sky area),
|
|
637
|
+
* B cloud type (0 stratus, 0.5 stratocumulus, 1 cumulus).
|
|
638
|
+
*/ get cloudWeatherMap() {
|
|
639
|
+
return this._volumetricCloudResources.weatherMapSource;
|
|
640
|
+
}
|
|
641
|
+
set cloudWeatherMap(tex) {
|
|
642
|
+
this._volumetricCloudResources.weatherMapSource = tex;
|
|
643
|
+
this._volumetricCloudCache.invalidate();
|
|
644
|
+
}
|
|
645
|
+
/** Draws a volumetric cloud resource over the sky, for inspection. */ get cloudDebugView() {
|
|
646
|
+
return this._cloudDebugView;
|
|
647
|
+
}
|
|
648
|
+
set cloudDebugView(val) {
|
|
649
|
+
this._cloudDebugView = val;
|
|
650
|
+
}
|
|
651
|
+
/** @internal Whether {@link SkyRenderer.drawCloudDebugView} has anything to draw this frame. */ get cloudDebugViewActive() {
|
|
652
|
+
return this._cloudDebugView !== 'none' && this._skyType === 'scatter' && this.effectiveCloudMode === 'volumetric';
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* @internal Draws {@link SkyRenderer.cloudDebugView} over the current render target. Called on
|
|
656
|
+
* the presented image, after tone mapping, so the texels show their stored values.
|
|
657
|
+
*/ drawCloudDebugView() {
|
|
658
|
+
drawVolumetricCloudDebug(this._volumetricCloudResources, this._cloudDebugView, this._cloudDebugSlice, this._cloudDebugChannel, this._volumetricCloudCache.densityParams);
|
|
659
|
+
}
|
|
660
|
+
/** Slice, 0 to 1: noise volume depth, density XY depth, or density XZ altitude. */ get cloudDebugSlice() {
|
|
661
|
+
return this._cloudDebugSlice;
|
|
662
|
+
}
|
|
663
|
+
set cloudDebugSlice(val) {
|
|
664
|
+
this._cloudDebugSlice = Math.min(1, Math.max(0, val));
|
|
665
|
+
}
|
|
666
|
+
/** Channel shown by {@link SkyRenderer.cloudDebugView}: 0 RGB, 1-4 R, G, B or A as grey. */ get cloudDebugChannel() {
|
|
667
|
+
return this._cloudDebugChannel;
|
|
668
|
+
}
|
|
669
|
+
set cloudDebugChannel(val) {
|
|
670
|
+
this._cloudDebugChannel = Math.min(4, Math.max(0, Math.round(val)));
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
419
673
|
* Wind velocity
|
|
420
674
|
*
|
|
421
675
|
* @remarks
|
|
@@ -556,6 +810,7 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
556
810
|
return this._skyDistantLightLut.get().getColorAttachments()[0];
|
|
557
811
|
}
|
|
558
812
|
update(ctx) {
|
|
813
|
+
const wantVolumetric = this._skyType === 'scatter' && this.effectiveCloudMode === 'volumetric';
|
|
559
814
|
// Latch the fog scales for this frame. The bake path (updateBakedSkyMap) uses the sky camera
|
|
560
815
|
// whose scene is null, so it cannot read lightingMode itself; both bake and main pass run after
|
|
561
816
|
// update(), so caching them here keeps them consistent.
|
|
@@ -567,15 +822,25 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
567
822
|
// take the plain camera pre-exposure.
|
|
568
823
|
// Legacy leaves both at 1 so its authored colors are untouched.
|
|
569
824
|
this._fogPreExposure = SkyRenderer.getBakeToPreExposedScale(ctx);
|
|
825
|
+
this._physicalSunDisk = ctx.scene.lightingMode === 'physical';
|
|
826
|
+
// Sky light for height fog: radiance map samples times the same scale the IBL applies to them.
|
|
827
|
+
const envLight = ctx.scene.env.light;
|
|
828
|
+
const fogSkyLightMap = envLight.type === 'ibl' ? envLight.radianceMap : null;
|
|
829
|
+
// Same scale as ShaderHelper.getEnvLightLuminance(), which cannot be used here: it reads ctx.env,
|
|
830
|
+
// and update() runs before the render graph sets it.
|
|
831
|
+
this._heightFogParams.skyLightScale = fogSkyLightMap ? SkyRenderer.getBakeToPreExposedScale(ctx) * (envLight.strength ?? 0) : 0;
|
|
832
|
+
this._heightFogParams.skyLightMaxLod = fogSkyLightMap ? fogSkyLightMap.mipLevelCount - 1 : 0;
|
|
833
|
+
this._fogSkyLightCubemap = this.getFogSkyLightCubemap(ctx);
|
|
570
834
|
this._fogLuminanceScale = ctx.scene.lightingMode === 'physical' ? ctx.scene.env.light.intensity * ShaderHelper.getPreExposure(ctx) : 1;
|
|
571
835
|
const useScatter = this._skyType === 'scatter';
|
|
572
836
|
if (useScatter || !atmosphereLUTRendered()) {
|
|
573
837
|
this.renderAtmosphereLUTs(ctx);
|
|
574
838
|
}
|
|
839
|
+
this._updateVolumetricClouds(ctx, wantVolumetric);
|
|
575
840
|
const oldSunLight = ctx.sunLight && useScatter ? ctx.sunLight.color : null;
|
|
576
841
|
const sunDir = SkyRenderer._getSunDir(ctx.sunLight);
|
|
577
842
|
const sunColor = SkyRenderer._getSunColor(ctx.sunLight);
|
|
578
|
-
if (this._skyType === 'scatter' && (this._wind.x !== 0 || this._wind.y !== 0)) {
|
|
843
|
+
if (this._skyType === 'scatter' && !this._volumetricActive && (this._wind.x !== 0 || this._wind.y !== 0)) {
|
|
579
844
|
this._bakedSkyboxDirty = true;
|
|
580
845
|
}
|
|
581
846
|
if (!this._skyDistantLightLut.get()) {
|
|
@@ -593,13 +858,23 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
593
858
|
this._lastSunColor.set(sunColor);
|
|
594
859
|
this._bakedSkyboxDirty = true;
|
|
595
860
|
}
|
|
861
|
+
// The scattering sky seen from the observer changes as the camera climbs or travels across the
|
|
862
|
+
// planet. Re-baking the IBL every frame would be far too expensive, so it waits for thresholds.
|
|
863
|
+
if (useScatter) {
|
|
864
|
+
const altitude = this._atmosphereParams.observerAltitude;
|
|
865
|
+
const baked = this._bakedObserverAltitude;
|
|
866
|
+
if (baked < 0 || Math.abs(altitude - baked) > Math.max(10, this._rebakeAltitudeRatio * baked) || Vector3.dot(this._observerUp, this._bakedObserverUp) < Math.cos(this._rebakeAngle * Math.PI / 180)) {
|
|
867
|
+
this._bakedObserverAltitude = altitude;
|
|
868
|
+
this._bakedObserverUp.set(this._observerUp);
|
|
869
|
+
this._bakedSkyboxDirty = true;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
596
872
|
if (this._bakedSkyboxDirty) {
|
|
597
873
|
this._bakedSkyboxDirty = false;
|
|
598
|
-
// updateBakedSkyMap derives the IBL
|
|
599
|
-
//
|
|
600
|
-
//
|
|
874
|
+
// updateBakedSkyMap derives both the IBL and the distant-sky LUT from the fog-free cubemap,
|
|
875
|
+
// before compositing fog. Deriving the distant-sky LUT from the fogged cubemap would feed the
|
|
876
|
+
// fog color back into its own ambient term.
|
|
601
877
|
this.updateBakedSkyMap(ctx);
|
|
602
|
-
this.renderSkyDistantLut(ctx, this._bakedSkyboxTexture.get());
|
|
603
878
|
}
|
|
604
879
|
// Atmosphere extinction times cloud occlusion. The cloud term intentionally applies only to
|
|
605
880
|
// the scene's directional light, not to the sky bake: the clouds in the cubemap are lit by
|
|
@@ -614,11 +889,9 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
614
889
|
const cameraY = Math.min(this._heightFogParams.parameter2.y + MAX_FOG_HEIGHT, ctx.camera.getWorldPosition().y);
|
|
615
890
|
const p = Math.max(-125, Math.min(126, -this._heightFogParams.parameter1.w * (cameraY - this._heightFogParams.parameter2.y)));
|
|
616
891
|
this._heightFogParams.parameter3.z = this._heightFogParams.parameter2.x * Math.pow(2, p);
|
|
617
|
-
// The legacy directional fog term expects a display-relative 0..1 light color. It cannot be
|
|
618
|
-
// mixed directly with photometric illuminance; physical fog instead receives its radiance
|
|
619
|
-
// from the distant-sky integration below.
|
|
620
892
|
if (ctx.scene.lightingMode === 'physical') {
|
|
621
|
-
|
|
893
|
+
const sunIlluminance = ctx.sunLight ? ctx.sunLight.intensity : 0;
|
|
894
|
+
this._heightFogParams.lightColor.set(Vector3.scale(newSunLight, sunIlluminance * ShaderHelper.getPreExposure(ctx) / (4 * Math.PI)));
|
|
622
895
|
} else {
|
|
623
896
|
this._heightFogParams.lightColor.set(newSunLight);
|
|
624
897
|
}
|
|
@@ -626,19 +899,78 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
626
899
|
}
|
|
627
900
|
return oldSunLight;
|
|
628
901
|
}
|
|
902
|
+
/**
|
|
903
|
+
* Advances the volumetric cloud sky cache, after the atmosphere parameters of the frame are set.
|
|
904
|
+
* The sky cache only holds what an observer below the layer sees; above that the procedural
|
|
905
|
+
* clouds are drawn instead.
|
|
906
|
+
*/ _updateVolumetricClouds(ctx, wanted) {
|
|
907
|
+
let active = wanted;
|
|
908
|
+
if (active) {
|
|
909
|
+
const altitude = this._atmosphereParams.observerAltitude;
|
|
910
|
+
if (altitude > this._volumetricCloud.layerBottomAltitude - VOLUMETRIC_CLOUD_CEILING_MARGIN) {
|
|
911
|
+
active = false;
|
|
912
|
+
if (!SkyRenderer._volumetricAltitudeWarned) {
|
|
913
|
+
SkyRenderer._volumetricAltitudeWarned = true;
|
|
914
|
+
console.warn('Volumetric clouds are only drawn below the cloud layer, drawing procedural clouds instead');
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
if (active) {
|
|
919
|
+
this._volumetricCloudResources.update(this._volumetricCloud.noiseSeed);
|
|
920
|
+
this._volumetricCloudCache.update(this._volumetricCloud, this._volumetricCloudResources, this._atmosphereParams, ctx.camera.getWorldPosition(), ctx.device.frameInfo.elapsedOverall * 0.001);
|
|
921
|
+
active = !!this._volumetricCloudCache.toMap;
|
|
922
|
+
if (this._volumetricCloudCache.roundCompleted) {
|
|
923
|
+
// The environment lighting follows the clouds once per round
|
|
924
|
+
this._bakedSkyboxDirty = true;
|
|
925
|
+
}
|
|
926
|
+
} else if (this._volumetricActive) {
|
|
927
|
+
// Resumes with a full rebuild rather than a fade from stale maps
|
|
928
|
+
this._volumetricCloudCache.invalidate();
|
|
929
|
+
}
|
|
930
|
+
if (active !== this._volumetricActive) {
|
|
931
|
+
this._volumetricActive = active;
|
|
932
|
+
this._bakedSkyboxDirty = true;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
629
935
|
renderAtmosphereLUTs(ctx) {
|
|
630
936
|
this._atmosphereParams.lightDir.set(SkyRenderer._getSunDir(ctx.sunLight));
|
|
631
937
|
this._atmosphereParams.lightColor.set(SkyRenderer._getSunColor(ctx.sunLight));
|
|
632
|
-
// Physical: the sun input is photometric (lux), normalized onto the model's authored reference
|
|
633
|
-
// and stored at the fixed PHYSICAL_BAKE_EXPOSURE rather than the live camera exposure. That
|
|
634
|
-
// keeps the cached IBL bake exposure-independent while staying inside the environment cubemap's
|
|
635
|
-
// limited float range -- raw luminance would overflow it to Inf. Consumers rescale to the live
|
|
636
|
-
// exposure via getBakeToPreExposedScale().
|
|
637
938
|
this._atmosphereParams.lightColor.w *= this._atmosphereExposure * (ctx.scene.lightingMode === 'physical' ? SkyRenderer.PHYSICAL_ATMOSPHERE_LUMINANCE_SCALE * SkyRenderer.PHYSICAL_BAKE_EXPOSURE : 1);
|
|
638
939
|
this._atmosphereParams.cameraAspect = ctx.camera.getAspect();
|
|
639
|
-
this._atmosphereParams.
|
|
940
|
+
this._atmosphereParams.cameraTanHalfFovy = ctx.camera.isPerspective() ? ctx.camera.getTanHalfFovy() : 1;
|
|
941
|
+
const referential = this._updateObserver(ctx.camera.getWorldPosition());
|
|
942
|
+
Matrix4x4.multiply(referential, ctx.camera.worldMatrix, this._atmosphereParams.cameraWorldMatrix);
|
|
943
|
+
referential.transformVectorAffine(this._atmosphereParams.lightDir, this._atmosphereParams.lightDir);
|
|
640
944
|
renderAtmosphereLUTs(this._atmosphereParams);
|
|
641
945
|
}
|
|
946
|
+
/**
|
|
947
|
+
* Places the observer for the camera at `cameraPos` and returns the world to observer-local
|
|
948
|
+
* rotation, also stored in the atmosphere params.
|
|
949
|
+
*/ _updateObserver(cameraPos) {
|
|
950
|
+
const params = this._atmosphereParams;
|
|
951
|
+
const scale = params.cameraHeightScale;
|
|
952
|
+
const R = params.plantRadius;
|
|
953
|
+
const px = cameraPos.x * scale;
|
|
954
|
+
const py = R + cameraPos.y * scale;
|
|
955
|
+
const pz = cameraPos.z * scale;
|
|
956
|
+
const len = Math.hypot(px, py, pz);
|
|
957
|
+
params.observerAltitude = Math.max(len - R, MIN_OBSERVER_ALTITUDE);
|
|
958
|
+
const up = len > 0 ? new Vector3(px / len, py / len, pz / len) : Vector3.axisPY();
|
|
959
|
+
this._observerUp.set(up);
|
|
960
|
+
// Duff et al. would do, but projecting a fixed axis keeps the identity at the origin.
|
|
961
|
+
const ref = Math.abs(up.x) < 0.999 ? Vector3.axisPX() : Vector3.axisPZ();
|
|
962
|
+
const localX = Vector3.sub(ref, Vector3.scale(up, Vector3.dot(ref, up))).inplaceNormalize();
|
|
963
|
+
const localZ = Vector3.cross(localX, up).inplaceNormalize();
|
|
964
|
+
// Rows of the world to local rotation are the local axes. Matrix4x4 is column-major: its
|
|
965
|
+
// setColXYZW(i, ...) writes the strided elements transformVectorAffine and shader mat * vec
|
|
966
|
+
// treat as row i.
|
|
967
|
+
const m = params.skyViewReferential;
|
|
968
|
+
m.setColXYZW(0, localX.x, localX.y, localX.z, 0);
|
|
969
|
+
m.setColXYZW(1, up.x, up.y, up.z, 0);
|
|
970
|
+
m.setColXYZW(2, localZ.x, localZ.y, localZ.z, 0);
|
|
971
|
+
m.setColXYZW(3, 0, 0, 0, 1);
|
|
972
|
+
return m;
|
|
973
|
+
}
|
|
642
974
|
renderSkyDistantLut(ctx, skybox) {
|
|
643
975
|
this._prepareSkyBox(ctx.device);
|
|
644
976
|
ctx.device.pushDeviceStates();
|
|
@@ -680,7 +1012,7 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
680
1012
|
// Direct sunlight is represented by the scene's directional sun light. Keep the analytic
|
|
681
1013
|
// sun disk out of both diffuse and specular IBL to avoid baking that direct contribution a
|
|
682
1014
|
// second time. Atmospheric scattering and clouds remain in the cubemap.
|
|
683
|
-
this._renderSky(camera, false, false, this._getSkyBakeLuminanceScale(ctx));
|
|
1015
|
+
this._renderSky(camera, false, false, this._getSkyBakeLuminanceScale(ctx), this._lowerHemisphereIsBlack);
|
|
684
1016
|
}
|
|
685
1017
|
device.popDeviceStates();
|
|
686
1018
|
this.renderSkyDistantLut(ctx, tex);
|
|
@@ -737,6 +1069,7 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
737
1069
|
}
|
|
738
1070
|
bindgroup.setTexture('skyDistantLightLut', this._skyDistantLightLut.get().getColorAttachments()[0], fetchSampler('clamp_nearest_nomip'));
|
|
739
1071
|
bindgroup.setTexture('apLut', getAerialPerspectiveLut(), fetchSampler('clamp_linear_nomip'));
|
|
1072
|
+
bindgroup.setTexture('skyLightCubemap', this._fogSkyLightCubemap ?? this.radianceMap, fetchSampler('clamp_linear'));
|
|
740
1073
|
bindgroup.setValue('rt', device.getFramebuffer() ? 1 : 0);
|
|
741
1074
|
bindgroup.setValue('invProjViewMatrix', camera.invViewProjectionMatrix);
|
|
742
1075
|
bindgroup.setValue('cameraNearFar', new Vector2(camera.getNearPlane(), camera.getFarPlane()));
|
|
@@ -745,7 +1078,7 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
745
1078
|
bindgroup.setValue('withAerialPerspective', this.skyType === 'scatter' ? 1 : 0);
|
|
746
1079
|
bindgroup.setValue('fogType', this.mappedFogType);
|
|
747
1080
|
bindgroup.setValue('atmosphereParams', this._atmosphereParams);
|
|
748
|
-
bindgroup.setValue('heightFogParams', this.
|
|
1081
|
+
bindgroup.setValue('heightFogParams', this.getUploadHeightFogParams());
|
|
749
1082
|
device.setProgram(fogProgram);
|
|
750
1083
|
device.setBindGroup(0, bindgroup);
|
|
751
1084
|
device.setVertexLayout(SkyRenderer._vertexLayout);
|
|
@@ -758,7 +1091,7 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
758
1091
|
* physical space when physical lighting is active, plus the pre-exposure the shader applies to its
|
|
759
1092
|
* atmosphere-derived LUT samples. Legacy returns the stored params untouched so its upload stays
|
|
760
1093
|
* byte-identical. Stored authored values are never mutated.
|
|
761
|
-
*/
|
|
1094
|
+
*/ getUploadHeightFogParams() {
|
|
762
1095
|
const scale = this._fogLuminanceScale;
|
|
763
1096
|
const p = this._heightFogParams;
|
|
764
1097
|
if (scale === 1 && this._fogPreExposure === 1) {
|
|
@@ -853,6 +1186,9 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
853
1186
|
this._irradianceSHFB.dispose();
|
|
854
1187
|
this._irradianceFrameBuffer.dispose();
|
|
855
1188
|
this._shProjector.dispose();
|
|
1189
|
+
this._volumetricCloudResources.dispose();
|
|
1190
|
+
this._volumetricCloudCache.dispose();
|
|
1191
|
+
this._bindgroupSkyVolumetric.dispose();
|
|
856
1192
|
if (this._skyDistantLightLut.get()) {
|
|
857
1193
|
this._skyDistantLightLut.get().getColorAttachments()[0].dispose();
|
|
858
1194
|
this._skyDistantLightLut.dispose();
|
|
@@ -885,13 +1221,13 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
885
1221
|
});
|
|
886
1222
|
}
|
|
887
1223
|
}
|
|
888
|
-
/** @internal */ _renderSky(camera, depthTest, includeSunDisk, luminanceScale = 1) {
|
|
1224
|
+
/** @internal */ _renderSky(camera, depthTest, includeSunDisk, luminanceScale = 1, lowerHemisphereBlack = false) {
|
|
889
1225
|
const device = getDevice();
|
|
890
1226
|
const savedRenderStates = device.getRenderStates();
|
|
891
1227
|
this._prepareSkyBox(device);
|
|
892
1228
|
if (this._skyType === 'scatter') {
|
|
893
1229
|
// The atmosphere LUTs already hold physical luminance, so only the caller's exposure applies.
|
|
894
|
-
this._drawScattering(camera, depthTest, includeSunDisk, luminanceScale);
|
|
1230
|
+
this._drawScattering(camera, depthTest, includeSunDisk, luminanceScale, lowerHemisphereBlack);
|
|
895
1231
|
} else if (this._skyType === 'skybox' && this.skyboxTexture) {
|
|
896
1232
|
this._drawSkybox(camera, depthTest, luminanceScale);
|
|
897
1233
|
} else {
|
|
@@ -998,7 +1334,8 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
998
1334
|
* clouds at low coverage barely attenuating the sun, while full cover drops to the
|
|
999
1335
|
* heavy-overcast floor {@link SkyRenderer.CLOUD_SUN_MIN_TRANSMITTANCE}.
|
|
1000
1336
|
*/ cloudSunTransmittance(_sunDir) {
|
|
1001
|
-
|
|
1337
|
+
// Volumetric clouds shade the sun per pixel through their shadow instead
|
|
1338
|
+
if (this._skyType !== 'scatter' || this._volumetricActive) {
|
|
1002
1339
|
return Vector3.one();
|
|
1003
1340
|
}
|
|
1004
1341
|
const coverage = Math.min(1, Math.max(0, this._cloudy));
|
|
@@ -1021,25 +1358,25 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
1021
1358
|
];
|
|
1022
1359
|
function rayleighSc(params, fH) {
|
|
1023
1360
|
const sigma = new Vector3(RAYLEIGH_SIGMA[0] * 1e-6, RAYLEIGH_SIGMA[1] * 1e-6, RAYLEIGH_SIGMA[2] * 1e-6);
|
|
1024
|
-
const rho_h = Math.exp(-fH / params.rayleighScatteringHeight);
|
|
1361
|
+
const rho_h = Math.exp(-Math.max(fH, 0) / params.rayleighScatteringHeight);
|
|
1025
1362
|
return Vector3.scale(sigma, rho_h);
|
|
1026
1363
|
}
|
|
1027
1364
|
function mieSc(params, fH) {
|
|
1028
1365
|
const sigma = new Vector3(MIE_SIGMA * 1e-6, MIE_SIGMA * 1e-6, MIE_SIGMA * 1e-6);
|
|
1029
|
-
const rho_h = Math.exp(-(fH / params.mieScatteringHeight)
|
|
1366
|
+
const rho_h = Math.exp(-Math.max(fH, 0) / params.mieScatteringHeight);
|
|
1030
1367
|
return Vector3.scale(sigma, rho_h);
|
|
1031
1368
|
}
|
|
1032
1369
|
function mieAb(params, fH) {
|
|
1033
1370
|
const sigma = new Vector3(MIE_ABSORPTION_SIGMA * 1e-6, MIE_ABSORPTION_SIGMA * 1e-6, MIE_ABSORPTION_SIGMA * 1e-6);
|
|
1034
|
-
const rho_h = Math.exp(-(fH / params.mieScatteringHeight)
|
|
1371
|
+
const rho_h = Math.exp(-Math.max(fH, 0) / params.mieScatteringHeight);
|
|
1035
1372
|
return Vector3.scale(sigma, rho_h);
|
|
1036
1373
|
}
|
|
1037
1374
|
function ozoneAb(params, fH) {
|
|
1038
1375
|
const sigma = new Vector3(OZONE_ABSORPTION_SIGMA[0] * 1e-6, OZONE_ABSORPTION_SIGMA[1] * 1e-6, OZONE_ABSORPTION_SIGMA[2] * 1e-6);
|
|
1039
|
-
const rho_h = Math.max(0, 1 - Math.abs(fH - params.ozoneCenter)
|
|
1376
|
+
const rho_h = Math.max(0, 1 - Math.abs(fH - params.ozoneCenter) / params.ozoneWidth);
|
|
1040
1377
|
return Vector3.scale(sigma, rho_h);
|
|
1041
1378
|
}
|
|
1042
|
-
const eyePos = new Vector3(0, this._atmosphereParams.plantRadius +
|
|
1379
|
+
const eyePos = new Vector3(0, this._atmosphereParams.plantRadius + MIN_OBSERVER_ALTITUDE, 0);
|
|
1043
1380
|
const lightDir = SkyRenderer._getSunDir(sunLight);
|
|
1044
1381
|
const d = this._rayIntersectSphere(this._atmosphereParams.plantRadius + this._atmosphereParams.atmosphereHeight, eyePos, lightDir);
|
|
1045
1382
|
if (d < 0) {
|
|
@@ -1058,27 +1395,38 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
1058
1395
|
}
|
|
1059
1396
|
return new Vector3(Math.exp(-sum.x), Math.exp(-sum.y), Math.exp(-sum.z));
|
|
1060
1397
|
}
|
|
1061
|
-
/** @internal */ _drawScattering(camera, depthTest, includeSunDisk, luminanceScale) {
|
|
1398
|
+
/** @internal */ _drawScattering(camera, depthTest, includeSunDisk, luminanceScale, lowerHemisphereBlack) {
|
|
1062
1399
|
const device = getDevice();
|
|
1063
1400
|
const tLut = getTransmittanceLut();
|
|
1064
1401
|
const skyLut = getSkyViewLut();
|
|
1065
1402
|
//const apLut = ScatteringLut.getAerialPerspectiveLut(alpha, 8000);
|
|
1066
|
-
const
|
|
1067
|
-
const
|
|
1403
|
+
const volumetric = this._volumetricActive;
|
|
1404
|
+
const program = volumetric ? SkyRenderer._programSkyVolumetric : SkyRenderer._programSky.scatter;
|
|
1405
|
+
const bindgroup = volumetric ? this._bindgroupSkyVolumetric.get() : this._bindgroupSky.scatter.get();
|
|
1406
|
+
if (volumetric) {
|
|
1407
|
+
const cache = this._volumetricCloudCache;
|
|
1408
|
+
bindgroup.setTexture('cloudFrom', cache.fromMap, fetchSampler('clamp_linear_nomip'));
|
|
1409
|
+
bindgroup.setTexture('cloudTo', cache.toMap, fetchSampler('clamp_linear_nomip'));
|
|
1410
|
+
bindgroup.setValue('cloudBlend', new Vector4(cache.blend, cache.fromDelta, cache.toDelta, 0));
|
|
1411
|
+
}
|
|
1068
1412
|
bindgroup.setValue('flip', device.getFramebuffer() && device.type === 'webgpu' ? new Vector4(1, -1, 1, 1) : new Vector4(1, 1, 1, 1));
|
|
1069
1413
|
bindgroup.setValue('params', this._atmosphereParams);
|
|
1070
1414
|
bindgroup.setValue('viewProjMatrix', camera.viewProjectionMatrix);
|
|
1071
1415
|
bindgroup.setValue('worldMatrix', this._skyWorldMatrix);
|
|
1072
1416
|
bindgroup.setValue('cameraPos', camera.getWorldPosition());
|
|
1073
1417
|
bindgroup.setValue('srgbOut', device.getFramebuffer() ? 0 : 1);
|
|
1074
|
-
bindgroup.setValue('includeSunDisk', includeSunDisk ? 1 : 0);
|
|
1418
|
+
bindgroup.setValue('includeSunDisk', includeSunDisk ? this._physicalSunDisk ? 2 : 1 : 0);
|
|
1419
|
+
bindgroup.setValue('lowerHemisphereBlack', lowerHemisphereBlack ? 1 : 0);
|
|
1075
1420
|
bindgroup.setValue('luminanceScale', luminanceScale);
|
|
1076
1421
|
bindgroup.setTexture('tLut', tLut, fetchSampler('clamp_linear_nomip'));
|
|
1077
1422
|
bindgroup.setTexture('skyLut', skyLut, fetchSampler('clamp_linear_nomip'));
|
|
1078
|
-
bindgroup.
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1423
|
+
bindgroup.setTexture('msLut', getMultiScatteringLut(), fetchSampler('clamp_linear_nomip'));
|
|
1424
|
+
if (!volumetric) {
|
|
1425
|
+
bindgroup.setValue('cloudy', this._cloudy);
|
|
1426
|
+
bindgroup.setValue('cloudIntensity', this._cloudIntensity);
|
|
1427
|
+
bindgroup.setValue('time', device.frameInfo.elapsedOverall * 0.001);
|
|
1428
|
+
bindgroup.setValue('velocity', this._wind);
|
|
1429
|
+
}
|
|
1082
1430
|
device.setProgram(program);
|
|
1083
1431
|
device.setBindGroup(0, bindgroup);
|
|
1084
1432
|
device.setRenderStates(depthTest ? SkyRenderer._renderStatesSky : SkyRenderer._renderStatesSkyNoDepthTest);
|
|
@@ -1182,7 +1530,13 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
1182
1530
|
this._bindgroupSky.image = new DRef(device.createBindGroup(SkyRenderer._programSky.image.bindGroupLayouts[0]));
|
|
1183
1531
|
}
|
|
1184
1532
|
if (!SkyRenderer._programSky.scatter) {
|
|
1185
|
-
SkyRenderer._programSky.scatter = SkyRenderer._createScatterProgram(device,
|
|
1533
|
+
SkyRenderer._programSky.scatter = SkyRenderer._createScatterProgram(device, 'procedural');
|
|
1534
|
+
}
|
|
1535
|
+
if (!SkyRenderer._programSkyVolumetric) {
|
|
1536
|
+
SkyRenderer._programSkyVolumetric = SkyRenderer._createScatterProgram(device, 'volumetric');
|
|
1537
|
+
}
|
|
1538
|
+
if (!this._bindgroupSkyVolumetric.get()) {
|
|
1539
|
+
this._bindgroupSkyVolumetric.set(device.createBindGroup(SkyRenderer._programSkyVolumetric.bindGroupLayouts[0]));
|
|
1186
1540
|
}
|
|
1187
1541
|
if (!this._bindgroupSky.scatter) {
|
|
1188
1542
|
this._bindgroupSky.scatter = new DRef(device.createBindGroup(SkyRenderer._programSky.scatter.bindGroupLayouts[0]));
|
|
@@ -1322,6 +1676,7 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
1322
1676
|
}
|
|
1323
1677
|
this.apLut = pb.tex2D().uniform(0);
|
|
1324
1678
|
this.skyDistantLightLut = pb.tex2D().uniform(0);
|
|
1679
|
+
this.skyLightCubemap = pb.texCube().uniform(0);
|
|
1325
1680
|
this.invProjViewMatrix = pb.mat4().uniform(0);
|
|
1326
1681
|
this.cameraNearFar = pb.vec2().uniform(0);
|
|
1327
1682
|
this.cameraPosition = pb.vec3().uniform(0);
|
|
@@ -1337,7 +1692,7 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
1337
1692
|
this.$l.hPos = pb.mul(this.invProjViewMatrix, this.clipSpacePos);
|
|
1338
1693
|
this.$l.worldPos = pb.div(this.$l.hPos, this.$l.hPos.w).xyz;
|
|
1339
1694
|
this.$l.isSky = ShaderHelper.isFarthestDepth(this, this.$l.depthValue);
|
|
1340
|
-
this.$l.color = calculateFog(this, this.withAerialPerspective, this.fogType, this.atmosphereParams, this.heightFogParams, this.$inputs.uv, this.isSky, this.cameraPosition, this.worldPos, 0, this.apLut, this.skyDistantLightLut);
|
|
1695
|
+
this.$l.color = calculateFog(this, this.withAerialPerspective, this.fogType, this.atmosphereParams, this.heightFogParams, this.$inputs.uv, this.isSky, this.cameraPosition, this.worldPos, 0, this.apLut, this.skyDistantLightLut, this.skyLightCubemap);
|
|
1341
1696
|
this.$if(pb.equal(this.srgbOut, 0), function() {
|
|
1342
1697
|
this.$outputs.outColor = this.color;
|
|
1343
1698
|
}).$else(function() {
|
|
@@ -1371,7 +1726,9 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
1371
1726
|
this.APLutProgram = createAPLutProgram(device);
|
|
1372
1727
|
}
|
|
1373
1728
|
}
|
|
1374
|
-
static _createScatterProgram(device,
|
|
1729
|
+
static _createScatterProgram(device, cloudMode) {
|
|
1730
|
+
const cloud = cloudMode === 'procedural';
|
|
1731
|
+
const volumetric = cloudMode === 'volumetric';
|
|
1375
1732
|
const program = device.buildRenderProgram({
|
|
1376
1733
|
vertex (pb) {
|
|
1377
1734
|
this.$inputs.pos = pb.vec3().attrib('position');
|
|
@@ -1389,8 +1746,16 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
1389
1746
|
this.$outputs.outColor = pb.vec4();
|
|
1390
1747
|
this.tLut = pb.tex2D().uniform(0);
|
|
1391
1748
|
this.skyLut = pb.tex2D().uniform(0);
|
|
1749
|
+
this.msLut = pb.tex2D().uniform(0);
|
|
1392
1750
|
this.params = getAtmosphereParamsStruct(pb)().uniform(0);
|
|
1393
1751
|
this.includeSunDisk = pb.int().uniform(0);
|
|
1752
|
+
this.lowerHemisphereBlack = pb.int().uniform(0);
|
|
1753
|
+
if (volumetric) {
|
|
1754
|
+
this.cloudFrom = pb.tex2D().uniform(0);
|
|
1755
|
+
this.cloudTo = pb.tex2D().uniform(0);
|
|
1756
|
+
// (blend, delta of cloudFrom, delta of cloudTo, 0)
|
|
1757
|
+
this.cloudBlend = pb.vec4().uniform(0);
|
|
1758
|
+
}
|
|
1394
1759
|
if (cloud) {
|
|
1395
1760
|
this.cloudy = pb.float().uniform(0);
|
|
1396
1761
|
this.cloudIntensity = pb.float().uniform(0);
|
|
@@ -1399,67 +1764,132 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
1399
1764
|
}
|
|
1400
1765
|
this.srgbOut = pb.int().uniform(0);
|
|
1401
1766
|
this.luminanceScale = pb.float().uniform(0);
|
|
1767
|
+
// Six-octave fbm. `footprint` is the pixel size in p units: an octave whose features get close
|
|
1768
|
+
// to it is faded to its mean (0.5) instead of aliasing into speckles, e.g. seen from space.
|
|
1402
1769
|
pb.func('noise', [
|
|
1403
1770
|
pb.vec3('p'),
|
|
1404
|
-
pb.float('t')
|
|
1771
|
+
pb.float('t'),
|
|
1772
|
+
pb.float('footprint')
|
|
1405
1773
|
], function() {
|
|
1406
|
-
this.p2 = pb.mul(this.p, 0.25);
|
|
1407
|
-
this.
|
|
1408
|
-
this.
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1774
|
+
this.$l.p2 = pb.mul(this.p, 0.25);
|
|
1775
|
+
this.$l.freq = pb.float(0.25);
|
|
1776
|
+
this.$l.f = pb.float(0);
|
|
1777
|
+
const octaves = [
|
|
1778
|
+
{
|
|
1779
|
+
mul: 1,
|
|
1780
|
+
weight: 0.5,
|
|
1781
|
+
dt: 0
|
|
1782
|
+
},
|
|
1783
|
+
{
|
|
1784
|
+
mul: 3.02,
|
|
1785
|
+
weight: 0.25,
|
|
1786
|
+
dt: -0.02
|
|
1787
|
+
},
|
|
1788
|
+
{
|
|
1789
|
+
mul: 3.03,
|
|
1790
|
+
weight: 0.125,
|
|
1791
|
+
dt: 0.01
|
|
1792
|
+
},
|
|
1793
|
+
{
|
|
1794
|
+
mul: 3.02,
|
|
1795
|
+
weight: 0.0625,
|
|
1796
|
+
dt: 0
|
|
1797
|
+
},
|
|
1798
|
+
{
|
|
1799
|
+
mul: 3.01,
|
|
1800
|
+
weight: 0.03125,
|
|
1801
|
+
dt: 0
|
|
1802
|
+
},
|
|
1803
|
+
{
|
|
1804
|
+
mul: 3.01,
|
|
1805
|
+
weight: 0.015625,
|
|
1806
|
+
dt: 0
|
|
1807
|
+
}
|
|
1808
|
+
];
|
|
1809
|
+
for (const o of octaves){
|
|
1810
|
+
if (o.mul !== 1) {
|
|
1811
|
+
this.p2 = pb.mul(this.p2, o.mul);
|
|
1812
|
+
this.freq = pb.mul(this.freq, o.mul);
|
|
1813
|
+
}
|
|
1814
|
+
if (o.dt !== 0) {
|
|
1815
|
+
this.p2.y = pb.add(this.p2.y, pb.mul(this.t, o.dt));
|
|
1816
|
+
}
|
|
1817
|
+
this.$l.fade = pb.sub(1, pb.smoothStep(0.25, 0.75, pb.mul(this.footprint, this.freq)));
|
|
1818
|
+
this.f = pb.add(this.f, pb.mul(pb.mix(0.5, smoothNoise3D(this, this.p2), this.fade), o.weight));
|
|
1819
|
+
}
|
|
1420
1820
|
this.$return(this.f);
|
|
1421
1821
|
});
|
|
1422
1822
|
pb.main(function() {
|
|
1423
|
-
//
|
|
1424
|
-
this.$l.
|
|
1425
|
-
this.$l.skyColor = skyBox(this, this.params, this.sunColor, this.$inputs.worldDirection, pb.float(0.01), this.includeSunDisk, this.tLut, this.skyLut).rgb;
|
|
1426
|
-
this.$l.rayDir = pb.normalize(this.$inputs.worldDirection);
|
|
1823
|
+
// Everything below runs in the observer's local frame (up = +Y), see skyViewReferential.
|
|
1824
|
+
this.$l.rayDir = pb.normalize(pb.mul(this.params.skyViewReferential, pb.vec4(this.$inputs.worldDirection, 0)).xyz);
|
|
1427
1825
|
this.$l.sunDir = this.params.lightDir;
|
|
1428
|
-
//
|
|
1429
|
-
this.$l.
|
|
1430
|
-
//
|
|
1826
|
+
// Angular size of a pixel, taken here in uniform control flow (WGSL requires it).
|
|
1827
|
+
this.$l.pixelAngle = pb.length(pb.fwidth(this.rayDir));
|
|
1828
|
+
// The sun disk's anti-aliasing wants the pixel pitch itself; the fwidth sum above runs up
|
|
1829
|
+
// to twice that along diagonals, which is fine for fading noise but would blur the rim.
|
|
1830
|
+
this.$l.sunPixelAngle = pb.max(pb.length(pb.dpdx(this.rayDir)), pb.length(pb.dpdy(this.rayDir)));
|
|
1831
|
+
this.$l.sunColor = pb.vec4();
|
|
1832
|
+
this.$l.skyColor = skyBox(this, this.params, this.sunColor, this.rayDir, pb.float(0.01), this.includeSunDisk, this.sunPixelAngle, pb.div(SKY_OUTPUT_MAX, pb.max(this.luminanceScale, 1e-8)), this.tLut, this.skyLut, this.msLut).rgb;
|
|
1833
|
+
// Procedural cloud layer: a spherical shell 3 km above the ground, so it is seen from
|
|
1834
|
+
// below, from above and wrapped around the planet from space. Near the world origin the
|
|
1835
|
+
// shell point matches the flat layer this used to be.
|
|
1431
1836
|
if (cloud) {
|
|
1432
|
-
this.$l.
|
|
1433
|
-
this.$
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1837
|
+
this.$l.eyePos = pb.vec3(0, pb.add(this.params.plantRadius, this.params.observerAltitude), 0);
|
|
1838
|
+
this.$l.cloudRadius = pb.add(this.params.plantRadius, 3000);
|
|
1839
|
+
this.$l.tCloud = rayIntersectSphere(this, pb.vec3(0), this.cloudRadius, this.eyePos, this.rayDir);
|
|
1840
|
+
this.$l.tGround = rayIntersectSphere(this, pb.vec3(0), this.params.plantRadius, this.eyePos, this.rayDir);
|
|
1841
|
+
// Below the shell the ray leaves through it unless the ground is in the way; above it the
|
|
1842
|
+
// shell is always hit before the ground.
|
|
1843
|
+
this.$l.hasCloud = pb.and(pb.greaterThan(this.tCloud, 0), pb.or(pb.greaterThanEqual(this.params.observerAltitude, 3000), pb.or(pb.lessThanEqual(this.tGround, 0), pb.lessThan(this.tCloud, this.tGround))));
|
|
1844
|
+
this.$l.noiseValue = pb.float(0);
|
|
1845
|
+
this.$l.cloudColor = pb.vec3(0);
|
|
1846
|
+
this.$l.vfactor = pb.float(0);
|
|
1847
|
+
this.$if(this.hasCloud, function() {
|
|
1848
|
+
this.$l.hitPos = pb.add(this.eyePos, pb.mul(this.rayDir, this.tCloud));
|
|
1849
|
+
this.$l.hitUp = pb.normalize(this.hitPos);
|
|
1850
|
+
// Anchor the clouds to the planet, not to the observer: back to the world-aligned planet
|
|
1851
|
+
// frame (the transpose of the rotation), relative to the planet top at the world origin.
|
|
1852
|
+
this.$l.planetHit = pb.mul(pb.vec4(this.hitPos, 0), this.params.skyViewReferential).xyz;
|
|
1853
|
+
this.$l.cloudPoint = pb.sub(this.planetHit, pb.vec3(0, this.params.plantRadius, 0));
|
|
1438
1854
|
this.speed = pb.mul(pb.vec3(this.velocity.x, 0, this.velocity.y), this.time);
|
|
1439
1855
|
this.$l.noiseScale = pb.float(4e-4);
|
|
1440
|
-
this.noiseValue = this.noise(pb.mul(pb.add(this.cloudPoint, this.speed), this.noiseScale), this.time);
|
|
1856
|
+
this.noiseValue = this.noise(pb.mul(pb.add(this.cloudPoint, this.speed), this.noiseScale), this.time, pb.mul(this.tCloud, this.pixelAngle, this.noiseScale));
|
|
1441
1857
|
this.noiseValue = pb.add(this.noiseValue, this.cloudy);
|
|
1442
1858
|
this.noiseValue = pb.smoothStep(1, pb.add(1, this.cloudy), this.noiseValue);
|
|
1859
|
+
// Ad hoc lighting, generalized from the flat layer: sun elevation at the cloud times
|
|
1860
|
+
// the viewing obliquity, lit from either side. The sun reaches the cloud through the
|
|
1861
|
+
// atmosphere above it (planet shadowed on the night side).
|
|
1862
|
+
this.$l.cosView = pb.abs(pb.dot(this.rayDir, this.hitUp));
|
|
1863
|
+
this.$l.sunIntensity = pb.sqrt(pb.max(0, pb.mul(pb.dot(this.hitUp, this.sunDir), this.cosView)));
|
|
1864
|
+
this.$l.sunAtCloud = transmittanceToSky(this, this.params, this.hitPos, this.sunDir, this.tLut);
|
|
1865
|
+
this.cloudColor = pb.mul(this.params.lightColor.rgb, this.sunAtCloud, this.sunIntensity, pb.mul(this.noiseValue, this.cloudIntensity));
|
|
1866
|
+
// Fade out at grazing angles, where the layer aliases
|
|
1867
|
+
this.vfactor = pb.clamp(pb.div(pb.sub(this.cosView, 0.01), pb.sub(0.03, 0.01)), 0, 1);
|
|
1443
1868
|
});
|
|
1444
|
-
// use sun color as cloud color
|
|
1445
|
-
/*
|
|
1446
|
-
this.$l.sunColor = pb.mul(
|
|
1447
|
-
pb.textureSampleLevel(this.skyLut, viewDirToUV(this, this.sunDir), 0),
|
|
1448
|
-
this.sunIntensity
|
|
1449
|
-
);
|
|
1450
|
-
*/ this.$l.cloudColor = pb.mul(this.sunColor.rgb, this.sunIntensity, pb.mul(this.noiseValue, this.cloudIntensity));
|
|
1451
|
-
}
|
|
1452
|
-
// Compute sky color
|
|
1453
|
-
if (cloud) {
|
|
1454
|
-
// blend
|
|
1455
|
-
this.$l.vfactor = pb.clamp(pb.div(pb.sub(this.rayDir.y, 0.01), pb.sub(0.03, 0.01)), 0, 1);
|
|
1456
1869
|
this.$l.factor = pb.clamp(pb.mul(this.noiseValue, this.vfactor), 0, 1);
|
|
1457
1870
|
this.$l.color = pb.mix(this.skyColor, this.cloudColor, this.factor);
|
|
1871
|
+
} else if (volumetric) {
|
|
1872
|
+
// Cached clouds: (luminance, transmittance) over the sky, blended between the two
|
|
1873
|
+
// latest completed maps. Below -delta a map holds no clouds.
|
|
1874
|
+
this.$l.color = this.skyColor;
|
|
1875
|
+
this.$l.elevation = pb.asin(pb.clamp(this.rayDir.y, -1, 1));
|
|
1876
|
+
this.$l.cFrom = pb.vec4(0, 0, 0, 1);
|
|
1877
|
+
this.$l.cTo = pb.vec4(0, 0, 0, 1);
|
|
1878
|
+
this.$if(pb.greaterThanEqual(this.elevation, pb.neg(this.cloudBlend.y)), function() {
|
|
1879
|
+
this.cFrom = pb.textureSampleLevel(this.cloudFrom, cloudCacheEncode(this, this.rayDir, this.cloudBlend.y), 0);
|
|
1880
|
+
});
|
|
1881
|
+
this.$if(pb.greaterThanEqual(this.elevation, pb.neg(this.cloudBlend.z)), function() {
|
|
1882
|
+
this.cTo = pb.textureSampleLevel(this.cloudTo, cloudCacheEncode(this, this.rayDir, this.cloudBlend.z), 0);
|
|
1883
|
+
});
|
|
1884
|
+
this.$l.clouds = pb.mix(this.cFrom, this.cTo, this.cloudBlend.x);
|
|
1885
|
+
this.color = pb.add(pb.mul(this.color, this.clouds.a), this.clouds.rgb);
|
|
1458
1886
|
} else {
|
|
1459
1887
|
this.$l.color = this.skyColor;
|
|
1460
1888
|
}
|
|
1461
|
-
|
|
1462
|
-
|
|
1889
|
+
this.$if(pb.and(pb.notEqual(this.lowerHemisphereBlack, 0), pb.lessThan(this.rayDir.y, 0)), function() {
|
|
1890
|
+
this.color = pb.vec3(0);
|
|
1891
|
+
});
|
|
1892
|
+
this.color = pb.min(pb.mul(this.color, this.luminanceScale), pb.vec3(SKY_OUTPUT_MAX));
|
|
1463
1893
|
this.$if(pb.equal(this.srgbOut, 0), function() {
|
|
1464
1894
|
this.$outputs.outColor = pb.vec4(this.color, 1);
|
|
1465
1895
|
}).$else(function() {
|
|
@@ -1468,7 +1898,7 @@ const defaultSkyWorldMatrix = Matrix4x4.identity();
|
|
|
1468
1898
|
});
|
|
1469
1899
|
}
|
|
1470
1900
|
});
|
|
1471
|
-
program.name = cloud ? '@SkyScatterCloud' : '@SkyScatter';
|
|
1901
|
+
program.name = cloud ? '@SkyScatterCloud' : volumetric ? '@SkyScatterVolumetricCloud' : '@SkyScatter';
|
|
1472
1902
|
return program;
|
|
1473
1903
|
}
|
|
1474
1904
|
}
|