@zephyr3d/scene 0.9.25 → 0.9.27
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/dist/animation/animationset.js +92 -30
- package/dist/animation/animationset.js.map +1 -1
- package/dist/animation/hair/gpu_hair_simulation.js +1090 -0
- package/dist/animation/hair/gpu_hair_simulation.js.map +1 -0
- package/dist/animation/joint_dynamics/controller.js +7 -5
- package/dist/animation/joint_dynamics/controller.js.map +1 -1
- package/dist/animation/joint_dynamics/convex_collider.js +320 -0
- package/dist/animation/joint_dynamics/convex_collider.js.map +1 -0
- package/dist/animation/joint_dynamics/joint_dynamics_system.js +81 -5
- package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
- package/dist/animation/morphtrack.js +3 -4
- package/dist/animation/morphtrack.js.map +1 -1
- package/dist/animation/skeleton.js +79 -25
- package/dist/animation/skeleton.js.map +1 -1
- package/dist/animation/spring/multi_chain_spring_system.js +577 -179
- package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
- package/dist/animation/spring/spring_collider.js +4 -3
- package/dist/animation/spring/spring_collider.js.map +1 -1
- package/dist/animation/spring/spring_node_pose_tracker.js +40 -0
- package/dist/animation/spring/spring_node_pose_tracker.js.map +1 -0
- package/dist/animation/spring/spring_particle.js +2 -0
- package/dist/animation/spring/spring_particle.js.map +1 -1
- package/dist/animation/spring/spring_system.js +11 -3
- package/dist/animation/spring/spring_system.js.map +1 -1
- package/dist/animation/spring_modifier.js +10 -0
- package/dist/animation/spring_modifier.js.map +1 -1
- package/dist/app/engine.js +53 -43
- package/dist/app/engine.js.map +1 -1
- package/dist/app/scriptingsystem.js +119 -6
- package/dist/app/scriptingsystem.js.map +1 -1
- package/dist/app/scriptregistry.js +3 -2
- package/dist/app/scriptregistry.js.map +1 -1
- package/dist/asset/assetmanager.js +284 -67
- package/dist/asset/assetmanager.js.map +1 -1
- package/dist/asset/loaders/zhair/zhair_format.js +288 -0
- package/dist/asset/loaders/zhair/zhair_format.js.map +1 -0
- package/dist/asset/loaders/zhair/zhair_loader.js +221 -0
- package/dist/asset/loaders/zhair/zhair_loader.js.map +1 -0
- package/dist/asset/model.js +184 -69
- package/dist/asset/model.js.map +1 -1
- package/dist/camera/camera.js +268 -44
- package/dist/camera/camera.js.map +1 -1
- package/dist/camera/perspectivecamera.js +3 -1
- package/dist/camera/perspectivecamera.js.map +1 -1
- package/dist/index.d.ts +9044 -5139
- package/dist/index.js +17 -6
- package/dist/index.js.map +1 -1
- package/dist/material/eye.js +932 -0
- package/dist/material/eye.js.map +1 -0
- package/dist/material/hair.js +917 -0
- package/dist/material/hair.js.map +1 -0
- package/dist/material/hairstrand.js +842 -0
- package/dist/material/hairstrand.js.map +1 -0
- package/dist/material/hairstrand_data.js +162 -0
- package/dist/material/hairstrand_data.js.map +1 -0
- package/dist/material/lambert.js +5 -2
- package/dist/material/lambert.js.map +1 -1
- package/dist/material/material.js +48 -10
- package/dist/material/material.js.map +1 -1
- package/dist/material/meshmaterial.js +120 -19
- package/dist/material/meshmaterial.js.map +1 -1
- package/dist/material/mixins/albedocolor.js +1 -1
- package/dist/material/mixins/albedocolor.js.map +1 -1
- package/dist/material/mixins/lightmodel/blinnphong.js +5 -3
- package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
- package/dist/material/mixins/lightmodel/lambert.js +5 -4
- package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +3 -3
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +5 -5
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js +3 -3
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
- package/dist/material/mixins/lit.js +106 -19
- package/dist/material/mixins/lit.js.map +1 -1
- package/dist/material/mixins/pbr/common.js +4 -4
- package/dist/material/mixins/pbr/common.js.map +1 -1
- package/dist/material/mixins/texture.js +48 -11
- package/dist/material/mixins/texture.js.map +1 -1
- package/dist/material/mtoon.js +6 -5
- package/dist/material/mtoon.js.map +1 -1
- package/dist/material/pbrblueprint.js +146 -28
- package/dist/material/pbrblueprint.js.map +1 -1
- package/dist/material/pbrblueprint_instance.js +501 -0
- package/dist/material/pbrblueprint_instance.js.map +1 -0
- package/dist/material/pbrmr.js +15 -16
- package/dist/material/pbrmr.js.map +1 -1
- package/dist/material/shader/helper.js +734 -23
- package/dist/material/shader/helper.js.map +1 -1
- package/dist/material/shader/terrain_blend.js +55 -0
- package/dist/material/shader/terrain_blend.js.map +1 -0
- package/dist/material/skin.js +192 -32
- package/dist/material/skin.js.map +1 -1
- package/dist/material/spriteblueprint.js +16 -1
- package/dist/material/spriteblueprint.js.map +1 -1
- package/dist/material/subsurfaceprofile.js +86 -23
- package/dist/material/subsurfaceprofile.js.map +1 -1
- package/dist/material/water.js +2059 -72
- package/dist/material/water.js.map +1 -1
- package/dist/posteffect/bloom.js +167 -7
- package/dist/posteffect/bloom.js.map +1 -1
- package/dist/posteffect/coloradjust.js +2 -1
- package/dist/posteffect/coloradjust.js.map +1 -1
- package/dist/posteffect/compositor.js.map +1 -1
- package/dist/posteffect/fxaa.js +2 -1
- package/dist/posteffect/fxaa.js.map +1 -1
- package/dist/posteffect/grayscale.js +3 -1
- package/dist/posteffect/grayscale.js.map +1 -1
- package/dist/posteffect/motionblur.js +664 -56
- 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/sao.js +46 -44
- package/dist/posteffect/sao.js.map +1 -1
- package/dist/posteffect/skinsss.js +368 -69
- package/dist/posteffect/skinsss.js.map +1 -1
- package/dist/posteffect/ssgi.js +16 -9
- package/dist/posteffect/ssgi.js.map +1 -1
- package/dist/posteffect/ssr.js +3 -0
- package/dist/posteffect/ssr.js.map +1 -1
- package/dist/posteffect/sss.js +121 -36
- package/dist/posteffect/sss.js.map +1 -1
- package/dist/posteffect/taa.js.map +1 -1
- package/dist/posteffect/tonemap.js +44 -1
- package/dist/posteffect/tonemap.js.map +1 -1
- package/dist/render/abuffer_oit.js +8 -2
- package/dist/render/abuffer_oit.js.map +1 -1
- package/dist/render/clipmap.js +156 -5
- package/dist/render/clipmap.js.map +1 -1
- package/dist/render/cull_visitor.js +20 -0
- package/dist/render/cull_visitor.js.map +1 -1
- package/dist/render/depthpass.js +36 -2
- package/dist/render/depthpass.js.map +1 -1
- package/dist/render/drawable_mixin.js +26 -1
- package/dist/render/drawable_mixin.js.map +1 -1
- package/dist/render/dualdepthpeeling_oit.js +3 -2
- package/dist/render/dualdepthpeeling_oit.js.map +1 -1
- package/dist/render/envlight.js +1 -1
- package/dist/render/envlight.js.map +1 -1
- package/dist/render/fbm_wavegenerator.js +6 -3
- package/dist/render/fbm_wavegenerator.js.map +1 -1
- package/dist/render/fft_wavegenerator.js +57 -15
- package/dist/render/fft_wavegenerator.js.map +1 -1
- package/dist/render/gerstner_wavegenerator.js +130 -20
- package/dist/render/gerstner_wavegenerator.js.map +1 -1
- package/dist/render/globalbindgroup_allocator.js +3 -0
- package/dist/render/globalbindgroup_allocator.js.map +1 -1
- package/dist/render/hzb.js +80 -22
- package/dist/render/hzb.js.map +1 -1
- package/dist/render/lightpass.js +6 -3
- package/dist/render/lightpass.js.map +1 -1
- package/dist/render/oit.js +29 -0
- package/dist/render/oit.js.map +1 -0
- package/dist/render/render_queue.js +39 -0
- package/dist/render/render_queue.js.map +1 -1
- package/dist/render/renderer.js +4 -0
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/rendergraph/blackboard.js +1 -0
- package/dist/render/rendergraph/blackboard.js.map +1 -1
- package/dist/render/rendergraph/device_pool_allocator.js.map +1 -1
- package/dist/render/rendergraph/executor.js +2 -0
- package/dist/render/rendergraph/executor.js.map +1 -1
- package/dist/render/rendergraph/forward_plus_builder.js +379 -10
- 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/rendergraph.js.map +1 -1
- package/dist/render/rendergraph/texture_affinity_cache.js +1 -1
- package/dist/render/rendergraph/texture_affinity_cache.js.map +1 -1
- package/dist/render/rendergraph/types.js.map +1 -1
- package/dist/render/shadow_mask_pass.js +73 -8
- package/dist/render/shadow_mask_pass.js.map +1 -1
- package/dist/render/sky.js +166 -126
- package/dist/render/sky.js.map +1 -1
- package/dist/render/underwater.js +624 -0
- package/dist/render/underwater.js.map +1 -0
- package/dist/render/water_caustics.js +1380 -0
- package/dist/render/water_caustics.js.map +1 -0
- package/dist/render/weightedblended_oit.js +3 -2
- package/dist/render/weightedblended_oit.js.map +1 -1
- package/dist/scene/basesprite.js +8 -0
- package/dist/scene/basesprite.js.map +1 -1
- package/dist/scene/batchgroup.js +3 -0
- package/dist/scene/batchgroup.js.map +1 -1
- package/dist/scene/environment.js +4 -1
- package/dist/scene/environment.js.map +1 -1
- package/dist/scene/hair.js +963 -0
- package/dist/scene/hair.js.map +1 -0
- package/dist/scene/light.js +5 -1
- package/dist/scene/light.js.map +1 -1
- package/dist/scene/mesh.js +481 -47
- 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 +3 -0
- package/dist/scene/msdftext.js.map +1 -1
- package/dist/scene/msdftextsprite.js +3 -0
- package/dist/scene/msdftextsprite.js.map +1 -1
- package/dist/scene/particlesys.js +6 -0
- package/dist/scene/particlesys.js.map +1 -1
- package/dist/scene/scene.js +3 -0
- package/dist/scene/scene.js.map +1 -1
- package/dist/scene/scene_node.js +6 -1
- package/dist/scene/scene_node.js.map +1 -1
- package/dist/scene/terrain-cm/grass.js +3 -0
- package/dist/scene/terrain-cm/grass.js.map +1 -1
- package/dist/scene/terrain-cm/grassmaterial.js +3 -0
- package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
- package/dist/scene/terrain-cm/terrain-cm.js +6 -0
- package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
- package/dist/scene/terrain-cm/texture_cache.js +309 -0
- package/dist/scene/terrain-cm/texture_cache.js.map +1 -0
- package/dist/scene/water.js +546 -4
- package/dist/scene/water.js.map +1 -1
- package/dist/shaders/atmosphere.js.map +1 -1
- package/dist/shaders/diffusion.js +54 -0
- package/dist/shaders/diffusion.js.map +1 -0
- package/dist/shaders/shadow.js +33 -21
- package/dist/shaders/shadow.js.map +1 -1
- package/dist/shaders/temporal.js +71 -33
- package/dist/shaders/temporal.js.map +1 -1
- package/dist/shaders/water.js +22 -19
- package/dist/shaders/water.js.map +1 -1
- package/dist/shaders/water_medium.js +56 -0
- package/dist/shaders/water_medium.js.map +1 -0
- package/dist/shadow/dom.js +546 -0
- package/dist/shadow/dom.js.map +1 -0
- package/dist/shadow/pcf_opt.js +3 -5
- package/dist/shadow/pcf_opt.js.map +1 -1
- package/dist/shadow/pcf_pd.js +1 -1
- package/dist/shadow/pcf_pd.js.map +1 -1
- package/dist/shadow/receiver_bias.js +41 -0
- package/dist/shadow/receiver_bias.js.map +1 -0
- package/dist/shadow/shader.js +20 -60
- package/dist/shadow/shader.js.map +1 -1
- package/dist/shadow/shadow_impl.js +89 -0
- package/dist/shadow/shadow_impl.js.map +1 -1
- package/dist/shadow/shadow_region.js +1 -1
- package/dist/shadow/shadow_region.js.map +1 -1
- package/dist/shadow/shadowmapper.js +150 -37
- package/dist/shadow/shadowmapper.js.map +1 -1
- package/dist/shapes/shape.js +11 -2
- package/dist/shapes/shape.js.map +1 -1
- package/dist/shapes/sphere.js +50 -9
- package/dist/shapes/sphere.js.map +1 -1
- package/dist/utility/blueprint/common/constants.js +112 -2
- package/dist/utility/blueprint/common/constants.js.map +1 -1
- package/dist/utility/blueprint/material/func.js +142 -49
- package/dist/utility/blueprint/material/func.js.map +1 -1
- package/dist/utility/blueprint/material/ir.js +490 -25
- package/dist/utility/blueprint/material/ir.js.map +1 -1
- package/dist/utility/blueprint/material/texture.js +898 -4
- package/dist/utility/blueprint/material/texture.js.map +1 -1
- package/dist/utility/serialization/manager.js +434 -101
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +401 -5
- package/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/utility/serialization/scene/batch.js +4 -0
- package/dist/utility/serialization/scene/batch.js.map +1 -1
- package/dist/utility/serialization/scene/camera.js +186 -26
- package/dist/utility/serialization/scene/camera.js.map +1 -1
- package/dist/utility/serialization/scene/common.js +26 -5
- package/dist/utility/serialization/scene/common.js.map +1 -1
- package/dist/utility/serialization/scene/hair.js +1141 -0
- package/dist/utility/serialization/scene/hair.js.map +1 -0
- package/dist/utility/serialization/scene/light.js +84 -4
- package/dist/utility/serialization/scene/light.js.map +1 -1
- package/dist/utility/serialization/scene/material.js +1867 -166
- package/dist/utility/serialization/scene/material.js.map +1 -1
- package/dist/utility/serialization/scene/mesh.js +231 -14
- package/dist/utility/serialization/scene/mesh.js.map +1 -1
- package/dist/utility/serialization/scene/node.js +141 -7
- package/dist/utility/serialization/scene/node.js.map +1 -1
- package/dist/utility/serialization/scene/particle.js +4 -0
- package/dist/utility/serialization/scene/particle.js.map +1 -1
- package/dist/utility/serialization/scene/primitive.js +18 -0
- package/dist/utility/serialization/scene/primitive.js.map +1 -1
- package/dist/utility/serialization/scene/sprite.js +4 -0
- package/dist/utility/serialization/scene/sprite.js.map +1 -1
- package/dist/utility/serialization/scene/terrain.js +4 -0
- package/dist/utility/serialization/scene/terrain.js.map +1 -1
- package/dist/utility/serialization/scene/text.js +4 -0
- package/dist/utility/serialization/scene/text.js.map +1 -1
- package/dist/utility/serialization/scene/water.js +1105 -21
- package/dist/utility/serialization/scene/water.js.map +1 -1
- package/dist/utility/serialization/types.js.map +1 -1
- package/dist/utility/shprojector.js +9 -8
- package/dist/utility/shprojector.js.map +1 -1
- package/dist/values.js +49 -2
- package/dist/values.js.map +1 -1
- package/package.json +3 -3
- package/dist/render/rendergraph/frame_graph_context.js +0 -41
- package/dist/render/rendergraph/frame_graph_context.js.map +0 -1
- package/dist/shaders/ssgi.js +0 -63
- package/dist/shaders/ssgi.js.map +0 -1
- package/dist/shaders/ssgi_reprojection.js +0 -118
- package/dist/shaders/ssgi_reprojection.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Vector3, Vector4, Vector2, REVERSE_Z, DEPTH_FARTHEST, DEPTH_REDUCE_CLOSER, DEPTH_REDUCE_FARTHER } from '@zephyr3d/base';
|
|
2
|
-
import { RENDER_PASS_TYPE_DEPTH, RENDER_PASS_TYPE_SHADOWMAP, RENDER_PASS_TYPE_OBJECT_COLOR, RENDER_PASS_TYPE_LIGHT, MaterialVaryingFlags, MORPH_WEIGHTS_VECTOR_COUNT, MORPH_TARGET_POSITION, MORPH_TARGET_NORMAL, MORPH_TARGET_TANGENT, MORPH_ATTRIBUTE_VECTOR_COUNT } from '../../values.js';
|
|
2
|
+
import { RENDER_PASS_TYPE_DEPTH, RENDER_PASS_TYPE_SHADOWMAP, RENDER_PASS_TYPE_OBJECT_COLOR, RENDER_PASS_TYPE_LIGHT, MaterialVaryingFlags, MAX_SKIN_EXTRA_INFLUENCE_PAIRS, MORPH_WEIGHTS_VECTOR_COUNT, MORPH_TARGET_POSITION, MORPH_TARGET_NORMAL, MORPH_TARGET_TANGENT, MORPH_ATTRIBUTE_VECTOR_COUNT, LIGHT_TYPE_DIRECTIONAL } from '../../values.js';
|
|
3
3
|
import { ProgramBuilder } from '@zephyr3d/device';
|
|
4
4
|
import { decodeNormalizedFloatFromRGBA, linearToGamma } from '../../shaders/misc.js';
|
|
5
5
|
import { getSamplerOptions, fetchSampler } from '../../utility/misc.js';
|
|
@@ -7,6 +7,7 @@ import { PHYSICAL_BAKE_EXPOSURE } from '../../utility/physical.js';
|
|
|
7
7
|
import { getDevice } from '../../app/api.js';
|
|
8
8
|
import { getDefaultAtmosphereParams, getAtmosphereParamsStruct } from '../../shaders/atmosphere.js';
|
|
9
9
|
import { getDefaultHeightFogParams, getHeightFogParamsStruct, calculateFog } from '../../shaders/fog.js';
|
|
10
|
+
import { getShadowReceiverNoL, getShadowReceiverBiasFactor } from '../../shadow/receiver_bias.js';
|
|
10
11
|
|
|
11
12
|
const UNIFORM_NAME_LIGHT_BUFFER = 'Z_UniformLightBuffer';
|
|
12
13
|
const UNIFORM_NAME_LIGHT_INDEX_TEXTURE = 'Z_UniformLightIndexTex';
|
|
@@ -16,6 +17,28 @@ const UNIFORM_NAME_SKYDISTANTLIGHT_LUT = 'Z_UniformSkyDistantLightLUT';
|
|
|
16
17
|
const UNIFORM_NAME_SHADOW_MAP = 'Z_UniformShadowMap';
|
|
17
18
|
const UNIFORM_NAME_SHADOW_MASK = 'Z_UniformShadowMask';
|
|
18
19
|
const UNIFORM_NAME_SHADOW_MASK_MODE = 'Z_UniformShadowMaskMode';
|
|
20
|
+
const UNIFORM_NAME_CAUSTIC_MAP = 'Z_UniformCausticMap';
|
|
21
|
+
const UNIFORM_NAME_CAUSTIC_HEIGHT_MAP = 'Z_UniformCausticHeightMap';
|
|
22
|
+
/**
|
|
23
|
+
* Offset added to a surface height before it is stored in the caustic height
|
|
24
|
+
* map, so that a cleared texel - no surface on that ray - is distinguishable
|
|
25
|
+
* from a surface at the rest level. A ray with no water on it must read as
|
|
26
|
+
* dry, not as submerged under a flat surface: on the down-sun side of a
|
|
27
|
+
* footprint edge, a receiver below the rest level but above a trough would
|
|
28
|
+
* otherwise be lit through water that is not there. Eight keeps troughs down
|
|
29
|
+
* to seven metres positive and leaves half floats millimetre precision.
|
|
30
|
+
* @internal
|
|
31
|
+
*/ const CAUSTIC_HEIGHT_BIAS = 8;
|
|
32
|
+
/**
|
|
33
|
+
* Water bodies that can cast caustics into one map.
|
|
34
|
+
*
|
|
35
|
+
* They share the map and the slice, so this is not a limit on how much water a
|
|
36
|
+
* scene may hold - only on how many separate surfaces the receiver will test
|
|
37
|
+
* itself against, which it does per fragment. Four covers a lake with a couple
|
|
38
|
+
* of pools beside it; beyond that the renderer keeps the largest four, which
|
|
39
|
+
* are the ones a viewer is most likely to be standing over.
|
|
40
|
+
*/ const MAX_CAUSTIC_WATERS = 4;
|
|
41
|
+
const UNIFORM_NAME_CAUSTIC_PARAMS = 'Z_UniformCausticParams';
|
|
19
42
|
const UNIFORM_NAME_LINEAR_DEPTH_MAP = 'Z_UniformLinearDepth';
|
|
20
43
|
const UNIFORM_NAME_LINEAR_DEPTH_MAP_SIZE = 'Z_UniformLinearDepthSize';
|
|
21
44
|
const UNIFORM_NAME_SCENE_COLOR_MAP = 'Z_UniformSceneColor';
|
|
@@ -32,6 +55,8 @@ const UNIFORM_NAME_INSTANCE_DATA_OFFSET = 'Z_UniformInstanceDataOffset';
|
|
|
32
55
|
const UNIFORM_NAME_BONE_MATRICES = 'Z_UniformBoneMatrices';
|
|
33
56
|
const UNIFORM_NAME_BONE_TEXTURE_SIZE = 'Z_UniformBoneTexSize';
|
|
34
57
|
const UNIFORM_NAME_BONE_INV_BIND_MATRIX = 'Z_UniformBoneInvBindMatrix';
|
|
58
|
+
const UNIFORM_NAME_SKIN_INFLUENCE_DATA = 'Z_UniformSkinInfluenceData';
|
|
59
|
+
const UNIFORM_NAME_SKIN_INFLUENCE_INFO = 'Z_UniformSkinInfluenceInfo';
|
|
35
60
|
const UNIFORM_NAME_MORPH_DATA = 'Z_UniformMorphData';
|
|
36
61
|
const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
37
62
|
/**
|
|
@@ -46,6 +71,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
46
71
|
/** @internal */ static SKIN_MATRIX_NAME = 'Z_SkinMatrix';
|
|
47
72
|
static SKIN_PREV_MATRIX_NAME = 'Z_PrevSkinMatrix';
|
|
48
73
|
static SKIN_BONE_OFFSET = 'Z_boneOffset';
|
|
74
|
+
static RESOLVED_VERTEX_NORMAL_NAME = 'Z_ResolvedVertexNormal';
|
|
49
75
|
/** @internal */ static _drawableBindGroupLayouts = {};
|
|
50
76
|
/** @internal */ static _lightUniformShadow = {
|
|
51
77
|
sunDir: new Vector3(),
|
|
@@ -101,6 +127,12 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
101
127
|
static getBoneInvBindMatrixUniformName() {
|
|
102
128
|
return UNIFORM_NAME_BONE_INV_BIND_MATRIX;
|
|
103
129
|
}
|
|
130
|
+
static getSkinInfluenceDataUniformName() {
|
|
131
|
+
return UNIFORM_NAME_SKIN_INFLUENCE_DATA;
|
|
132
|
+
}
|
|
133
|
+
static getSkinInfluenceInfoUniformName() {
|
|
134
|
+
return UNIFORM_NAME_SKIN_INFLUENCE_INFO;
|
|
135
|
+
}
|
|
104
136
|
static getMorphDataUniformName() {
|
|
105
137
|
return UNIFORM_NAME_MORPH_DATA;
|
|
106
138
|
}
|
|
@@ -183,10 +215,22 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
183
215
|
pb.vec4('directionCutoff'),
|
|
184
216
|
pb.mat4('viewMatrix'),
|
|
185
217
|
pb.vec4('depthBias'),
|
|
218
|
+
// Same slot the lighting pass exposes through getShadowImplParams. A
|
|
219
|
+
// caster normally has no use for it, but one that encodes something
|
|
220
|
+
// beyond depth - deep opacity map layers - needs the same parameters the
|
|
221
|
+
// receiver will decode with.
|
|
222
|
+
pb.vec4('implParams'),
|
|
186
223
|
pb.int('lightType')
|
|
187
224
|
]);
|
|
188
225
|
scope.camera = cameraStruct().uniform(0);
|
|
189
226
|
scope.light = lightStruct().uniform(0);
|
|
227
|
+
// Lets the shadow implementation add its own caster inputs. Only deep
|
|
228
|
+
// opacity maps use this, to read the frontmost depth its first pass wrote.
|
|
229
|
+
const shadowLight = ctx.renderPass.light;
|
|
230
|
+
const casterParams = shadowLight ? ctx.shadowMapInfo?.get(shadowLight) : null;
|
|
231
|
+
if (casterParams) {
|
|
232
|
+
casterParams.impl?.declareCasterUniforms(scope, casterParams);
|
|
233
|
+
}
|
|
190
234
|
} else if (ctx.renderPass.type === RENDER_PASS_TYPE_DEPTH || ctx.renderPass.type === RENDER_PASS_TYPE_OBJECT_COLOR) {
|
|
191
235
|
scope.camera = cameraStruct().uniform(0);
|
|
192
236
|
} else if (ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT) {
|
|
@@ -272,6 +316,9 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
272
316
|
}
|
|
273
317
|
scope[UNIFORM_NAME_SHADOW_MAP] = tex.uniform(0);
|
|
274
318
|
}
|
|
319
|
+
if (this.usesWaterCaustics(ctx)) {
|
|
320
|
+
this.declareWaterCausticUniforms(pb);
|
|
321
|
+
}
|
|
275
322
|
if (ctx.drawEnvLight) {
|
|
276
323
|
ctx.env.light.envLight.initShaderBindings(pb, ctx);
|
|
277
324
|
}
|
|
@@ -282,8 +329,18 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
282
329
|
scope[UNIFORM_NAME_LINEAR_DEPTH_MAP_SIZE] = pb.vec2().uniform(0);
|
|
283
330
|
}
|
|
284
331
|
if (ctx.sceneColorTexture) {
|
|
285
|
-
//
|
|
286
|
-
|
|
332
|
+
// Mip-filtered, because the refraction background is read at a LOD and
|
|
333
|
+
// not only at mip 0: the water picks one from how deep and how turbid
|
|
334
|
+
// the column it is looking through is. A `nomip` sampler pins mip 0
|
|
335
|
+
// whatever LOD the shader passes, so this and the copy's level count
|
|
336
|
+
// have to change together - and each backend only honours one of them.
|
|
337
|
+
// WebGPU binds the sampler declared here as a static binding the runtime
|
|
338
|
+
// cannot overwrite; WebGL falls back to the texture's own sampler, whose
|
|
339
|
+
// mip filter follows its level count.
|
|
340
|
+
//
|
|
341
|
+
// Every other reader of this texture passes an explicit LOD 0 (SSR hits,
|
|
342
|
+
// the sky blend), so none of them changes behaviour.
|
|
343
|
+
scope[UNIFORM_NAME_SCENE_COLOR_MAP] = pb.tex2D().uniform(0).withSampler(getSamplerOptions('clamp_linear'));
|
|
287
344
|
scope[UNIFORM_NAME_SCENE_COLOR_MAP_SIZE] = pb.vec2().uniform(0);
|
|
288
345
|
}
|
|
289
346
|
if (ctx.HiZTexture) {
|
|
@@ -322,6 +379,8 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
322
379
|
return null;
|
|
323
380
|
}
|
|
324
381
|
const pb = scope.$builder;
|
|
382
|
+
const isWebGL = pb.getDevice().type === 'webgl';
|
|
383
|
+
const supportsTextureLoad = !isWebGL;
|
|
325
384
|
const funcNameGetBoneMatrixFromTexture = 'Z_getBoneMatrixFromTexture';
|
|
326
385
|
pb.func(funcNameGetBoneMatrixFromTexture, [
|
|
327
386
|
pb.int('boneIndex')
|
|
@@ -354,6 +413,33 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
354
413
|
this.$l.m2 = scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(blendIndices[2]));
|
|
355
414
|
this.$l.m3 = scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(blendIndices[3]));
|
|
356
415
|
this.$l.m = pb.add(pb.mul(this.m0, blendWeights.x), pb.mul(this.m1, blendWeights.y), pb.mul(this.m2, blendWeights.z), pb.mul(this.m3, blendWeights.w));
|
|
416
|
+
this.$l.skinInfo = this[UNIFORM_NAME_SKIN_INFLUENCE_INFO];
|
|
417
|
+
this.$if(pb.greaterThan(this.skinInfo.z, 4), function() {
|
|
418
|
+
this.$l.vertexIndex = isWebGL ? pb.int(scope.$inputs.zFakeVertexID) : pb.int(scope.$builtins.vertexIndex);
|
|
419
|
+
this.$l.texWidth = pb.int(this.skinInfo.x);
|
|
420
|
+
this.$l.pairCount = pb.int(this.skinInfo.w);
|
|
421
|
+
this.$for(pb.int('pairIndex'), 0, MAX_SKIN_EXTRA_INFLUENCE_PAIRS, function() {
|
|
422
|
+
this.$if(pb.greaterThanEqual(this.pairIndex, this.pairCount), function() {
|
|
423
|
+
this.$break();
|
|
424
|
+
});
|
|
425
|
+
this.$l.pixelIndex = pb.add(pb.mul(this.vertexIndex, this.pairCount), this.pairIndex);
|
|
426
|
+
this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
|
|
427
|
+
this.$l.yIndex = pb.div(this.pixelIndex, this.texWidth);
|
|
428
|
+
if (supportsTextureLoad) {
|
|
429
|
+
this.$l.extra = pb.textureLoad(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.ivec2(this.xIndex, this.yIndex), 0);
|
|
430
|
+
} else {
|
|
431
|
+
this.$l.u = pb.div(pb.add(pb.float(this.xIndex), 0.5), this.skinInfo.x);
|
|
432
|
+
this.$l.v = pb.div(pb.add(pb.float(this.yIndex), 0.5), this.skinInfo.y);
|
|
433
|
+
this.$l.extra = pb.textureSampleLevel(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.vec2(this.u, this.v), 0);
|
|
434
|
+
}
|
|
435
|
+
this.$if(pb.greaterThan(this.extra.y, 0), function() {
|
|
436
|
+
this.m = pb.add(this.m, pb.mul(scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(this.extra.x)), this.extra.y));
|
|
437
|
+
});
|
|
438
|
+
this.$if(pb.greaterThan(this.extra.w, 0), function() {
|
|
439
|
+
this.m = pb.add(this.m, pb.mul(scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(this.extra.z)), this.extra.w));
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
});
|
|
357
443
|
this.$return(pb.mul(invBindMatrix, this.m));
|
|
358
444
|
});
|
|
359
445
|
return scope.$g[funcNameGetSkinningMatrix]();
|
|
@@ -389,7 +475,8 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
389
475
|
});
|
|
390
476
|
this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
|
|
391
477
|
this.$if(pb.notEqual(this.weight, 0), function() {
|
|
392
|
-
this.$l.
|
|
478
|
+
this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
|
|
479
|
+
this.$l.pixelIndex = pb.float(pb.add(this.offset, pb.mul(this.targetIndex, this.numVertices), this.vertexIndex));
|
|
393
480
|
this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
|
|
394
481
|
this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, this.texWidth));
|
|
395
482
|
this.$l.u = pb.div(pb.add(this.xIndex, 0.5), this.texWidth);
|
|
@@ -405,7 +492,8 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
405
492
|
this.$l.j = pb.compAnd(this.t, 3);
|
|
406
493
|
this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
|
|
407
494
|
this.$if(pb.notEqual(this.weight, 0), function() {
|
|
408
|
-
this.$l.
|
|
495
|
+
this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
|
|
496
|
+
this.$l.pixelIndex = pb.float(pb.add(this.offset, pb.mul(this.targetIndex, this.numVertices), this.vertexIndex));
|
|
409
497
|
this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
|
|
410
498
|
this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, this.texWidth));
|
|
411
499
|
this.$l.u = pb.div(pb.add(this.xIndex, 0.5), this.texWidth);
|
|
@@ -417,17 +505,166 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
417
505
|
}
|
|
418
506
|
this.$return(this.value);
|
|
419
507
|
});
|
|
420
|
-
const pos = 1 + MORPH_WEIGHTS_VECTOR_COUNT + (attrib >> 2);
|
|
508
|
+
const pos = 1 + MORPH_WEIGHTS_VECTOR_COUNT * 2 + (attrib >> 2);
|
|
421
509
|
const comp = attrib & 3;
|
|
422
510
|
const offset = scope[this.getMorphInfoUniformName()][pos][comp];
|
|
423
511
|
return scope[funcName](pb.int(offset));
|
|
424
512
|
}
|
|
513
|
+
/**
|
|
514
|
+
* Normalize a morphed direction without allowing cancellation between
|
|
515
|
+
* multiple morph targets to turn a valid base direction into NaN/noise.
|
|
516
|
+
*
|
|
517
|
+
* Morph normal/tangent data is additive. When two targets cancel the base
|
|
518
|
+
* direction, the zero-length result is not a meaningful direction, so the
|
|
519
|
+
* authored vertex direction is the least surprising fallback.
|
|
520
|
+
* @internal
|
|
521
|
+
*/ static defineNormalizeMorphedDirection(scope) {
|
|
522
|
+
const pb = scope.$builder;
|
|
523
|
+
const funcName = 'Z_normalizeMorphedDirection';
|
|
524
|
+
pb.func(funcName, [
|
|
525
|
+
pb.vec3('base'),
|
|
526
|
+
pb.vec3('delta')
|
|
527
|
+
], function() {
|
|
528
|
+
this.$l.morphed = pb.add(this.base, this.delta);
|
|
529
|
+
this.$l.lengthSquared = pb.dot(this.morphed, this.morphed);
|
|
530
|
+
this.$if(pb.greaterThan(this.lengthSquared, 1e-6), function() {
|
|
531
|
+
this.$return(pb.mul(this.morphed, pb.inverseSqrt(this.lengthSquared)));
|
|
532
|
+
});
|
|
533
|
+
this.$l.baseLengthSquared = pb.dot(this.base, this.base);
|
|
534
|
+
this.$if(pb.greaterThan(this.baseLengthSquared, 1e-6), function() {
|
|
535
|
+
this.$return(pb.mul(this.base, pb.inverseSqrt(this.baseLengthSquared)));
|
|
536
|
+
});
|
|
537
|
+
this.$return(pb.vec3(0, 0, 1));
|
|
538
|
+
});
|
|
539
|
+
return funcName;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Mixes morph directions according to the amount of positional deformation
|
|
543
|
+
* contributed by each active target. This avoids letting a target with a
|
|
544
|
+
* large authored normal delta dominate a target that actually moves the
|
|
545
|
+
* surface much farther at the current vertex.
|
|
546
|
+
*
|
|
547
|
+
* The additive result remains the fallback for assets without position
|
|
548
|
+
* morph data, and for vertices whose position deltas are all zero.
|
|
549
|
+
* @internal
|
|
550
|
+
*/ static calculateMorphDirectionByDisplacement(scope, base, attrib) {
|
|
551
|
+
const pb = scope.$builder;
|
|
552
|
+
const isWebGL1 = pb.getDevice().type === 'webgl';
|
|
553
|
+
if (pb.shaderKind !== 'vertex') {
|
|
554
|
+
throw new Error(`ShaderHelper.calculateMorphDirectionByDisplacement(): must be called at vertex stage`);
|
|
555
|
+
}
|
|
556
|
+
const that = this;
|
|
557
|
+
const funcName = 'Z_calculateMorphDirectionByDisplacement';
|
|
558
|
+
const normalizeName = this.defineNormalizeMorphedDirection(scope);
|
|
559
|
+
pb.func(funcName, [
|
|
560
|
+
pb.vec3('base'),
|
|
561
|
+
pb.int('directionOffset'),
|
|
562
|
+
pb.int('positionOffset')
|
|
563
|
+
], function() {
|
|
564
|
+
this.$l.vertexIndex = isWebGL1 ? pb.int(scope.$inputs.zFakeVertexID) : pb.int(scope.$builtins.vertexIndex);
|
|
565
|
+
const morphInfo = scope[that.getMorphInfoUniformName()];
|
|
566
|
+
this.$l.metaData = pb.ivec4(morphInfo[0]);
|
|
567
|
+
this.$l.texWidth = pb.float(this.metaData.x);
|
|
568
|
+
this.$l.texHeight = pb.float(this.metaData.y);
|
|
569
|
+
this.$l.numVertices = this.metaData.z;
|
|
570
|
+
this.$l.numTargets = this.metaData.w;
|
|
571
|
+
this.$l.directionDelta = pb.vec3(0);
|
|
572
|
+
this.$l.weightedDirection = pb.vec3(0);
|
|
573
|
+
this.$l.weightSum = pb.float(0);
|
|
574
|
+
const accumulateTarget = (targetScope)=>{
|
|
575
|
+
targetScope.$if(pb.notEqual(targetScope.weight, 0), function() {
|
|
576
|
+
this.$l.targetIndex = pb.int(targetScope['targetIndex']);
|
|
577
|
+
this.$l.positionValue = pb.vec3(0);
|
|
578
|
+
this.$l.directionValue = pb.vec3(0);
|
|
579
|
+
this.$if(pb.greaterThanEqual(targetScope['directionOffset'], 0), function() {
|
|
580
|
+
this.$l.pixelIndex = pb.float(pb.add(targetScope['directionOffset'], pb.mul(this.targetIndex, targetScope['numVertices']), targetScope['vertexIndex']));
|
|
581
|
+
this.$l.xIndex = pb.mod(this.pixelIndex, targetScope['texWidth']);
|
|
582
|
+
this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, targetScope['texWidth']));
|
|
583
|
+
this.$l.u = pb.div(pb.add(this.xIndex, 0.5), targetScope['texWidth']);
|
|
584
|
+
this.$l.v = pb.div(pb.add(this.yIndex, 0.5), targetScope['texHeight']);
|
|
585
|
+
this.directionValue = pb.textureSampleLevel(scope[that.getMorphDataUniformName()], pb.vec2(this.u, this.v), 0).xyz;
|
|
586
|
+
});
|
|
587
|
+
this.$if(pb.greaterThanEqual(targetScope['positionOffset'], 0), function() {
|
|
588
|
+
this.$l.pixelIndex = pb.float(pb.add(targetScope['positionOffset'], pb.mul(this.targetIndex, targetScope['numVertices']), targetScope['vertexIndex']));
|
|
589
|
+
this.$l.xIndex = pb.mod(this.pixelIndex, targetScope['texWidth']);
|
|
590
|
+
this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, targetScope['texWidth']));
|
|
591
|
+
this.$l.u = pb.div(pb.add(this.xIndex, 0.5), targetScope['texWidth']);
|
|
592
|
+
this.$l.v = pb.div(pb.add(this.yIndex, 0.5), targetScope['texHeight']);
|
|
593
|
+
this.positionValue = pb.textureSampleLevel(scope[that.getMorphDataUniformName()], pb.vec2(this.u, this.v), 0).xyz;
|
|
594
|
+
});
|
|
595
|
+
this.directionDelta = pb.add(this.directionDelta, pb.mul(this.directionValue, targetScope.weight));
|
|
596
|
+
this.$l.targetDirection = scope.$g[normalizeName](this.base, pb.mul(this.directionValue, targetScope.weight));
|
|
597
|
+
this.$l.displacementWeight = pb.mul(pb.abs(targetScope.weight), pb.sqrt(pb.dot(this.positionValue, this.positionValue)));
|
|
598
|
+
this.weightedDirection = pb.add(this.weightedDirection, pb.mul(this.targetDirection, this.displacementWeight));
|
|
599
|
+
this.weightSum = pb.add(this.weightSum, this.displacementWeight);
|
|
600
|
+
});
|
|
601
|
+
};
|
|
602
|
+
if (isWebGL1) {
|
|
603
|
+
this.$for(pb.int('i'), 0, MORPH_WEIGHTS_VECTOR_COUNT, function() {
|
|
604
|
+
this.$for(pb.int('j'), 0, 4, function() {
|
|
605
|
+
this.$l.index = pb.add(pb.mul(this.i, 4), this.j);
|
|
606
|
+
this.$if(pb.greaterThanEqual(this.index, this.numTargets), function() {
|
|
607
|
+
this.$break();
|
|
608
|
+
});
|
|
609
|
+
this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
|
|
610
|
+
this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
|
|
611
|
+
accumulateTarget(this);
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
} else {
|
|
615
|
+
this.$for(pb.int('t'), 0, this.numTargets, function() {
|
|
616
|
+
this.$l.i = pb.sar(this.t, 2);
|
|
617
|
+
this.$l.j = pb.compAnd(this.t, 3);
|
|
618
|
+
this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
|
|
619
|
+
this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
|
|
620
|
+
accumulateTarget(this);
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
this.$l.fallback = scope.$g[normalizeName](this.base, this.directionDelta);
|
|
624
|
+
this.$if(pb.lessThan(this.weightSum, 1e-6), function() {
|
|
625
|
+
this.$return(this.fallback);
|
|
626
|
+
});
|
|
627
|
+
this.$l.weightedLengthSquared = pb.dot(this.weightedDirection, this.weightedDirection);
|
|
628
|
+
this.$if(pb.greaterThan(this.weightedLengthSquared, 1e-6), function() {
|
|
629
|
+
this.$return(pb.mul(this.weightedDirection, pb.inverseSqrt(this.weightedLengthSquared)));
|
|
630
|
+
});
|
|
631
|
+
this.$return(this.fallback);
|
|
632
|
+
});
|
|
633
|
+
const directionPos = 1 + MORPH_WEIGHTS_VECTOR_COUNT * 2 + (attrib >> 2);
|
|
634
|
+
const directionComp = attrib & 3;
|
|
635
|
+
const positionPos = 1 + MORPH_WEIGHTS_VECTOR_COUNT * 2 + (MORPH_TARGET_POSITION >> 2);
|
|
636
|
+
const positionComp = MORPH_TARGET_POSITION & 3;
|
|
637
|
+
const morphInfo = scope[this.getMorphInfoUniformName()];
|
|
638
|
+
return scope.$g[funcName](base.xyz, pb.int(morphInfo[directionPos][directionComp]), pb.int(morphInfo[positionPos][positionComp]));
|
|
639
|
+
}
|
|
640
|
+
/** @internal */ static orthogonalizeMorphedTangent(scope, normal, tangent) {
|
|
641
|
+
const pb = scope.$builder;
|
|
642
|
+
const funcName = 'Z_orthogonalizeMorphedTangent';
|
|
643
|
+
pb.func(funcName, [
|
|
644
|
+
pb.vec3('normal'),
|
|
645
|
+
pb.vec3('tangent')
|
|
646
|
+
], function() {
|
|
647
|
+
this.$l.normalLengthSquared = pb.dot(this.normal, this.normal);
|
|
648
|
+
this.$if(pb.greaterThan(this.normalLengthSquared, 1e-6), function() {
|
|
649
|
+
this.$l.n = pb.mul(this.normal, pb.inverseSqrt(this.normalLengthSquared));
|
|
650
|
+
this.$l.projected = pb.sub(this.tangent, pb.mul(this.n, pb.dot(this.n, this.tangent)));
|
|
651
|
+
this.$l.tangentLengthSquared = pb.dot(this.projected, this.projected);
|
|
652
|
+
this.$if(pb.greaterThan(this.tangentLengthSquared, 1e-6), function() {
|
|
653
|
+
this.$return(pb.mul(this.projected, pb.inverseSqrt(this.tangentLengthSquared)));
|
|
654
|
+
});
|
|
655
|
+
});
|
|
656
|
+
this.$return(this.tangent);
|
|
657
|
+
});
|
|
658
|
+
return scope.$g[funcName](normal.xyz, tangent.xyz);
|
|
659
|
+
}
|
|
425
660
|
/** @internal */ static prepareSkinAnimation(scope) {
|
|
426
661
|
if (!this.hasSkinning(scope)) {
|
|
427
662
|
return;
|
|
428
663
|
}
|
|
429
664
|
const that = this;
|
|
430
665
|
const pb = scope.$builder;
|
|
666
|
+
const isWebGL = pb.getDevice().type === 'webgl';
|
|
667
|
+
const supportsTextureLoad = !isWebGL;
|
|
431
668
|
const funcNameGetBoneMatrixFromTexture = 'Z_getBoneMatrixFromTexture';
|
|
432
669
|
pb.func(funcNameGetBoneMatrixFromTexture, [
|
|
433
670
|
pb.float('boneIndex'),
|
|
@@ -461,6 +698,33 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
461
698
|
this.$l.m2 = scope.$g[funcNameGetBoneMatrixFromTexture](blendIndices[2], this.boneOffset);
|
|
462
699
|
this.$l.m3 = scope.$g[funcNameGetBoneMatrixFromTexture](blendIndices[3], this.boneOffset);
|
|
463
700
|
this.$l.m = pb.add(pb.mul(this.m0, blendWeights.x), pb.mul(this.m1, blendWeights.y), pb.mul(this.m2, blendWeights.z), pb.mul(this.m3, blendWeights.w));
|
|
701
|
+
this.$l.skinInfo = this[UNIFORM_NAME_SKIN_INFLUENCE_INFO];
|
|
702
|
+
this.$if(pb.greaterThan(this.skinInfo.z, 4), function() {
|
|
703
|
+
this.$l.vertexIndex = isWebGL ? pb.int(scope.$inputs.zFakeVertexID) : pb.int(scope.$builtins.vertexIndex);
|
|
704
|
+
this.$l.texWidth = pb.int(this.skinInfo.x);
|
|
705
|
+
this.$l.pairCount = pb.int(this.skinInfo.w);
|
|
706
|
+
this.$for(pb.int('pairIndex'), 0, MAX_SKIN_EXTRA_INFLUENCE_PAIRS, function() {
|
|
707
|
+
this.$if(pb.greaterThanEqual(this.pairIndex, this.pairCount), function() {
|
|
708
|
+
this.$break();
|
|
709
|
+
});
|
|
710
|
+
this.$l.pixelIndex = pb.add(pb.mul(this.vertexIndex, this.pairCount), this.pairIndex);
|
|
711
|
+
this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
|
|
712
|
+
this.$l.yIndex = pb.div(this.pixelIndex, this.texWidth);
|
|
713
|
+
if (supportsTextureLoad) {
|
|
714
|
+
this.$l.extra = pb.textureLoad(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.ivec2(this.xIndex, this.yIndex), 0);
|
|
715
|
+
} else {
|
|
716
|
+
this.$l.u = pb.div(pb.add(pb.float(this.xIndex), 0.5), this.skinInfo.x);
|
|
717
|
+
this.$l.v = pb.div(pb.add(pb.float(this.yIndex), 0.5), this.skinInfo.y);
|
|
718
|
+
this.$l.extra = pb.textureSampleLevel(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.vec2(this.u, this.v), 0);
|
|
719
|
+
}
|
|
720
|
+
this.$if(pb.greaterThan(this.extra.y, 0), function() {
|
|
721
|
+
this.m = pb.add(this.m, pb.mul(scope.$g[funcNameGetBoneMatrixFromTexture](this.extra.x, this.boneOffset), this.extra.y));
|
|
722
|
+
});
|
|
723
|
+
this.$if(pb.greaterThan(this.extra.w, 0), function() {
|
|
724
|
+
this.m = pb.add(this.m, pb.mul(scope.$g[funcNameGetBoneMatrixFromTexture](this.extra.z, this.boneOffset), this.extra.w));
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
});
|
|
464
728
|
this.$return(pb.mul(invBindMatrix, this.m));
|
|
465
729
|
});
|
|
466
730
|
const motionVector = !!this.getUnjitteredViewProjectionMatrix(scope);
|
|
@@ -535,6 +799,19 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
535
799
|
}
|
|
536
800
|
}
|
|
537
801
|
/**
|
|
802
|
+
* Gets the skinning matrix in the vertex shader, if the mesh being drawn is skinned.
|
|
803
|
+
*
|
|
804
|
+
* @remarks
|
|
805
|
+
* Lets a material apply the same skinning transform to its own uniform-supplied
|
|
806
|
+
* object-space vectors that {@link ShaderHelper.resolveVertexNormal} applies to
|
|
807
|
+
* the normal attribute, so such vectors stay attached to the deforming mesh.
|
|
808
|
+
*
|
|
809
|
+
* @param scope - Current shader scope, must be vertex stage
|
|
810
|
+
* @returns The skinning matrix of type mat4, or null when the mesh is not skinned
|
|
811
|
+
*/ static getSkinMatrix(scope) {
|
|
812
|
+
return scope[this.SKIN_MATRIX_NAME] ?? null;
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
538
815
|
* Calculates the normal vector of type vec3 in object space
|
|
539
816
|
*
|
|
540
817
|
* @param scope - Current shader scope
|
|
@@ -550,6 +827,10 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
550
827
|
if (!funcScope || !funcScope.$isMain()) {
|
|
551
828
|
throw new Error(`ShaderHelper.resolveVertexNormal(): must be called at entry function`);
|
|
552
829
|
}
|
|
830
|
+
const useVertexAttrib = !normal;
|
|
831
|
+
if (useVertexAttrib && scope[this.RESOLVED_VERTEX_NORMAL_NAME]) {
|
|
832
|
+
return scope[this.RESOLVED_VERTEX_NORMAL_NAME];
|
|
833
|
+
}
|
|
553
834
|
if (!normal) {
|
|
554
835
|
if (!scope.$getVertexAttrib('normal')) {
|
|
555
836
|
scope.$inputs.Z_normal = pb.vec3().attrib('normal');
|
|
@@ -557,13 +838,17 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
557
838
|
normal = scope.$getVertexAttrib('normal');
|
|
558
839
|
}
|
|
559
840
|
if (this.hasMorphing(scope)) {
|
|
560
|
-
normal =
|
|
841
|
+
normal = this.calculateMorphDirectionByDisplacement(scope, normal, MORPH_TARGET_NORMAL);
|
|
561
842
|
}
|
|
843
|
+
let resolvedNormal = normal;
|
|
562
844
|
if (scope[this.SKIN_MATRIX_NAME]) {
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
|
|
845
|
+
resolvedNormal = pb.mul(scope[this.SKIN_MATRIX_NAME], pb.vec4(normal, 0)).xyz;
|
|
846
|
+
}
|
|
847
|
+
if (useVertexAttrib) {
|
|
848
|
+
scope.$l[this.RESOLVED_VERTEX_NORMAL_NAME] = resolvedNormal;
|
|
849
|
+
resolvedNormal = scope[this.RESOLVED_VERTEX_NORMAL_NAME];
|
|
566
850
|
}
|
|
851
|
+
return resolvedNormal;
|
|
567
852
|
}
|
|
568
853
|
/**
|
|
569
854
|
* Calculates the tangent vector of type vec3 in object space
|
|
@@ -588,13 +873,16 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
588
873
|
tangent = scope.$getVertexAttrib('tangent');
|
|
589
874
|
}
|
|
590
875
|
if (this.hasMorphing(scope)) {
|
|
591
|
-
tangent = pb.
|
|
876
|
+
tangent = pb.vec4(this.calculateMorphDirectionByDisplacement(scope, tangent.xyz, MORPH_TARGET_TANGENT), tangent.w);
|
|
592
877
|
}
|
|
593
878
|
if (scope[this.SKIN_MATRIX_NAME]) {
|
|
594
|
-
|
|
595
|
-
}
|
|
596
|
-
|
|
879
|
+
tangent = pb.vec4(pb.mul(scope[this.SKIN_MATRIX_NAME], pb.vec4(tangent.xyz, 0)).xyz, tangent.w);
|
|
880
|
+
}
|
|
881
|
+
if (this.hasMorphing(scope) && scope.$getVertexAttrib('normal')) {
|
|
882
|
+
const resolvedNormal = scope[this.RESOLVED_VERTEX_NORMAL_NAME] ?? this.resolveVertexNormal(scope);
|
|
883
|
+
tangent = pb.vec4(this.orthogonalizeMorphedTangent(scope, resolvedNormal, tangent.xyz), tangent.w);
|
|
597
884
|
}
|
|
885
|
+
return tangent;
|
|
598
886
|
}
|
|
599
887
|
/**
|
|
600
888
|
* Gets the uniform variable of type mat4 which holds the world matrix of current object to be drawn
|
|
@@ -676,10 +964,12 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
676
964
|
scope[UNIFORM_NAME_BONE_MATRICES] = pb.tex2D().uniform(1).sampleType('unfilterable-float');
|
|
677
965
|
scope[UNIFORM_NAME_BONE_INV_BIND_MATRIX] = pb.mat4().uniform(1);
|
|
678
966
|
scope[UNIFORM_NAME_BONE_TEXTURE_SIZE] = pb.vec2().uniform(1);
|
|
967
|
+
scope[UNIFORM_NAME_SKIN_INFLUENCE_DATA] = pb.tex2D().uniform(1).sampleType('unfilterable-float');
|
|
968
|
+
scope[UNIFORM_NAME_SKIN_INFLUENCE_INFO] = pb.vec4().uniform(1);
|
|
679
969
|
}
|
|
680
970
|
if (morphing) {
|
|
681
971
|
scope[UNIFORM_NAME_MORPH_DATA] = pb.tex2D().uniform(1).sampleType('unfilterable-float');
|
|
682
|
-
scope[UNIFORM_NAME_MORPH_INFO] = pb.vec4[1 + MORPH_WEIGHTS_VECTOR_COUNT + MORPH_ATTRIBUTE_VECTOR_COUNT]().uniformBuffer(1);
|
|
972
|
+
scope[UNIFORM_NAME_MORPH_INFO] = pb.vec4[1 + MORPH_WEIGHTS_VECTOR_COUNT * 2 + MORPH_ATTRIBUTE_VECTOR_COUNT]().uniformBuffer(1);
|
|
683
973
|
}
|
|
684
974
|
}
|
|
685
975
|
/** @internal */ static prepareVertexShaderCommon(pb, ctx) {
|
|
@@ -735,8 +1025,10 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
735
1025
|
directionCutoff: light.directionAndCutoff,
|
|
736
1026
|
viewMatrix: light.viewMatrix,
|
|
737
1027
|
depthBias: shadowMapParams.depthBiasValues[0],
|
|
1028
|
+
implParams: shadowMapParams.impl.getParams(this._lightUniformShadow.implParams),
|
|
738
1029
|
lightType: light.lightType
|
|
739
1030
|
});
|
|
1031
|
+
shadowMapParams.impl?.applyCasterUniforms(bindGroup, shadowMapParams);
|
|
740
1032
|
}
|
|
741
1033
|
}
|
|
742
1034
|
/** @internal */ static setFogUniforms(bindGroup, withAerialPerspective, fogType, additive, atmosphereParams, heightFogParams, aerialPerspectiveLUT, skyDistantLightLUT) {
|
|
@@ -834,10 +1126,329 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
834
1126
|
bindGroup.setTexture(UNIFORM_NAME_SHADOW_MASK, ctx.shadowMaskTexture ?? this.getDummyShadowMask(ctx.device), fetchSampler('clamp_nearest'));
|
|
835
1127
|
}
|
|
836
1128
|
bindGroup.setTexture(UNIFORM_NAME_BAKED_SKY_MAP, ctx.scene.env.sky.getBakedSkyTexture(ctx));
|
|
1129
|
+
if (this.usesWaterCaustics(ctx)) {
|
|
1130
|
+
this.setWaterCausticUniforms(bindGroup, ctx);
|
|
1131
|
+
}
|
|
837
1132
|
if (ctx.drawEnvLight) {
|
|
838
1133
|
ctx.env.light.envLight.updateBindGroup(bindGroup, ctx);
|
|
839
1134
|
}
|
|
840
1135
|
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Whether the pass being built modulates its light with the water caustic map.
|
|
1138
|
+
*
|
|
1139
|
+
* True for every light pass of a frame that produced a map; which light the
|
|
1140
|
+
* caustics actually attach to is decided per-fragment by
|
|
1141
|
+
* {@link ShaderHelper.calculateWaterCaustic}. Both the shader variant hash and
|
|
1142
|
+
* the global bind group hash fold this in, so a program that declares the
|
|
1143
|
+
* caustic uniforms is always paired with a bind group that supplies them.
|
|
1144
|
+
*
|
|
1145
|
+
* Deliberately a function of the draw context alone, with no material input.
|
|
1146
|
+
* The caustic uniforms sit in the global bind group, whose layout is built
|
|
1147
|
+
* once per {@link DrawContext.renderPassHash} and shared by every material in
|
|
1148
|
+
* the pass, so the declarations have to be uniform across the pass. A material
|
|
1149
|
+
* that must not receive caustics - water itself - opts out of the sampling
|
|
1150
|
+
* instead, through `receivesWaterCaustics()`; the uniforms stay declared and
|
|
1151
|
+
* simply go unread.
|
|
1152
|
+
*
|
|
1153
|
+
* @param ctx - Current draw context.
|
|
1154
|
+
* @returns True when the caustic uniforms take part in this pass.
|
|
1155
|
+
* @internal
|
|
1156
|
+
*/ static usesWaterCaustics(ctx) {
|
|
1157
|
+
return !!ctx.waterCaustics && !!ctx.waterCausticLight && ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT;
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Declares the global uniforms {@link ShaderHelper.calculateWaterCaustic} reads.
|
|
1161
|
+
*
|
|
1162
|
+
* Split out of the global uniform setup so a test can build a program that
|
|
1163
|
+
* exercises the sampling code without standing up a whole light pass.
|
|
1164
|
+
*
|
|
1165
|
+
* @param pb - Program builder whose global scope receives the uniforms.
|
|
1166
|
+
* @internal
|
|
1167
|
+
*/ /**
|
|
1168
|
+
* Warps a linear caustic-map coordinate so texels concentrate near the centre.
|
|
1169
|
+
*
|
|
1170
|
+
* The map covers a fixed footprint, so its world texel size is the range
|
|
1171
|
+
* divided by the resolution and a range large enough to light the scene is
|
|
1172
|
+
* already too coarse to resolve a caustic cell. This trades the edge of the
|
|
1173
|
+
* map, which is far from the camera, for the middle, which is not:
|
|
1174
|
+
*
|
|
1175
|
+
* ```
|
|
1176
|
+
* warp(u) = u (1 + k) / (1 + k |u|)
|
|
1177
|
+
* unwarp(w) = w / ((1 + k) - k |w|)
|
|
1178
|
+
* ```
|
|
1179
|
+
*
|
|
1180
|
+
* Texel density is `1 + k` times uniform at the centre and `1 / (1 + k)` times
|
|
1181
|
+
* it at the border, and `k = 0` is exactly the identity, so the pass can apply
|
|
1182
|
+
* this unconditionally. Both ends of `[-1, 1]` are fixed points, which is what
|
|
1183
|
+
* lets the warp sit inside the existing parameterisation without moving the
|
|
1184
|
+
* footprint the receiver gates on. The first derivative is continuous at the
|
|
1185
|
+
* centre; the second flips sign there, which is not visible in a pattern this
|
|
1186
|
+
* noisy.
|
|
1187
|
+
*
|
|
1188
|
+
* Applied per axis rather than radially: the photon grid, the map and the blur
|
|
1189
|
+
* are all axis-aligned, so a separable warp keeps photon `(i,j)` on texel
|
|
1190
|
+
* `(i,j)` for calm water and leaves the map's normalisation to 1.0 intact.
|
|
1191
|
+
*
|
|
1192
|
+
* @param scope - Current shader scope.
|
|
1193
|
+
* @param ndc - Linear map coordinate in `[-1, 1]`.
|
|
1194
|
+
* @param strength - Warp strength `k`; 0 is the identity.
|
|
1195
|
+
* @returns The warped coordinate, also in `[-1, 1]`.
|
|
1196
|
+
* @internal
|
|
1197
|
+
*/ static warpCausticNDC(scope, ndc, strength) {
|
|
1198
|
+
const pb = scope.$builder;
|
|
1199
|
+
const funcName = 'Z_warpCausticNDC';
|
|
1200
|
+
pb.func(funcName, [
|
|
1201
|
+
pb.vec2('ndc'),
|
|
1202
|
+
pb.float('k')
|
|
1203
|
+
], function() {
|
|
1204
|
+
this.$return(pb.div(pb.mul(this.ndc, pb.add(this.k, 1)), pb.add(pb.vec2(1), pb.mul(pb.abs(this.ndc), this.k))));
|
|
1205
|
+
});
|
|
1206
|
+
return pb.getGlobalScope()[funcName](ndc, strength);
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Inverse of {@link ShaderHelper.warpCausticNDC}.
|
|
1210
|
+
*
|
|
1211
|
+
* The denominator is `1` at the border and `1 + k` at the centre, so it stays
|
|
1212
|
+
* positive for any input the passes feed it - all of which come from `[-1, 1]`.
|
|
1213
|
+
*
|
|
1214
|
+
* @param scope - Current shader scope.
|
|
1215
|
+
* @param warped - Warped map coordinate in `[-1, 1]`.
|
|
1216
|
+
* @param strength - Warp strength `k`; 0 is the identity.
|
|
1217
|
+
* @returns The linear coordinate the warp came from.
|
|
1218
|
+
* @internal
|
|
1219
|
+
*/ static unwarpCausticNDC(scope, warped, strength) {
|
|
1220
|
+
const pb = scope.$builder;
|
|
1221
|
+
const funcName = 'Z_unwarpCausticNDC';
|
|
1222
|
+
pb.func(funcName, [
|
|
1223
|
+
pb.vec2('warped'),
|
|
1224
|
+
pb.float('k')
|
|
1225
|
+
], function() {
|
|
1226
|
+
this.$return(pb.div(this.warped, pb.sub(pb.vec2(pb.add(this.k, 1)), pb.mul(pb.abs(this.warped), this.k))));
|
|
1227
|
+
});
|
|
1228
|
+
return pb.getGlobalScope()[funcName](warped, strength);
|
|
1229
|
+
}
|
|
1230
|
+
/** @internal */ static declareWaterCausticUniforms(pb) {
|
|
1231
|
+
const scope = pb.getGlobalScope();
|
|
1232
|
+
const causticStruct = pb.defineStruct([
|
|
1233
|
+
pb.vec4('frameX'),
|
|
1234
|
+
pb.vec4('frameY'),
|
|
1235
|
+
pb.vec4('center'),
|
|
1236
|
+
pb.vec4('lightDir'),
|
|
1237
|
+
pb.vec4('params'),
|
|
1238
|
+
pb.vec4('extinction'),
|
|
1239
|
+
// Per water body, because the slice is shared but the water is not: each
|
|
1240
|
+
// has its own surface height, footprint and medium, and none of the three
|
|
1241
|
+
// means anything until a receiver has resolved which water is above it.
|
|
1242
|
+
pb.int('waterCount'),
|
|
1243
|
+
pb.vec4[MAX_CAUSTIC_WATERS]('waterRegions'),
|
|
1244
|
+
// (sigma_t.xyz, surface height)
|
|
1245
|
+
pb.vec4[MAX_CAUSTIC_WATERS]('waterMedia')
|
|
1246
|
+
]);
|
|
1247
|
+
scope[UNIFORM_NAME_CAUSTIC_PARAMS] = causticStruct().uniform(0);
|
|
1248
|
+
// The map is single mip and read bilinearly; clamping keeps receivers
|
|
1249
|
+
// outside the covered area reading the border rather than wrapping.
|
|
1250
|
+
scope[UNIFORM_NAME_CAUSTIC_MAP] = pb.tex2D().uniform(0).withSampler(getSamplerOptions('clamp_linear_nomip'));
|
|
1251
|
+
// Displaced surface height per water slot, on the same slice as the map.
|
|
1252
|
+
// Read with the map's uv, so it shares the map's sampling.
|
|
1253
|
+
scope[UNIFORM_NAME_CAUSTIC_HEIGHT_MAP] = pb.tex2D().uniform(0).withSampler(getSamplerOptions('clamp_linear_nomip'));
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Attenuation the water caustics apply to one light at a world position.
|
|
1257
|
+
*
|
|
1258
|
+
* Returns the product of two effects: the caustic pattern focused by the waves,
|
|
1259
|
+
* and the Beer-Lambert transmittance of the water column the light travelled
|
|
1260
|
+
* through to reach the point. The result is 1 for any light that is not the
|
|
1261
|
+
* caustic light, above the surface, and outside the water region, so the caller
|
|
1262
|
+
* can multiply unconditionally.
|
|
1263
|
+
*
|
|
1264
|
+
* The caustic light is identified at runtime by comparing directions rather
|
|
1265
|
+
* than by which pass is running. Shadow-casting lights reach the opaque queue
|
|
1266
|
+
* through the clustered path when the screen-space shadow mask is on (the
|
|
1267
|
+
* default) and through a per-light additive pass when it is off; only a runtime
|
|
1268
|
+
* test covers both. Two directional lights pointing the same way would both
|
|
1269
|
+
* pick up the caustics, but they are physically the same light.
|
|
1270
|
+
*
|
|
1271
|
+
* @param scope - Current shader scope.
|
|
1272
|
+
* @param worldPos - World position of the shaded fragment.
|
|
1273
|
+
* @param lightType - The light's type constant.
|
|
1274
|
+
* @param lightDirection - Direction the light travels, i.e. `directionAndCutoff.xyz`.
|
|
1275
|
+
* @param ctx - Current draw context.
|
|
1276
|
+
* @returns A `vec3` multiplier, or null when this pass has no caustics.
|
|
1277
|
+
*/ static calculateWaterCaustic(scope, worldPos, lightType, lightDirection, ctx) {
|
|
1278
|
+
if (!this.usesWaterCaustics(ctx)) {
|
|
1279
|
+
return null;
|
|
1280
|
+
}
|
|
1281
|
+
return this.sampleWaterCaustic(scope, worldPos, lightType, lightDirection);
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* {@link ShaderHelper.calculateWaterCaustic} without the pass gate.
|
|
1285
|
+
*
|
|
1286
|
+
* The gate asks whether the *light pass* has caustics, which is the right
|
|
1287
|
+
* question for a material and the wrong one for a standalone full-screen pass
|
|
1288
|
+
* that declared the same uniforms itself. Such a pass has already decided it
|
|
1289
|
+
* has a map; this is the sampling half on its own.
|
|
1290
|
+
*
|
|
1291
|
+
* @param scope - Current shader scope.
|
|
1292
|
+
* @param worldPos - World position of the shaded fragment.
|
|
1293
|
+
* @param lightType - The light's type constant.
|
|
1294
|
+
* @param lightDirection - Direction the light travels, i.e. `directionAndCutoff.xyz`.
|
|
1295
|
+
* @param defocus - Whether to fade the pattern's contrast by how far the point
|
|
1296
|
+
* is from the map's focal depth. True for a receiver surface, which is what
|
|
1297
|
+
* that fade describes. False for a point inside the water column: see the
|
|
1298
|
+
* comment on the term itself.
|
|
1299
|
+
* @returns A `vec3` multiplier.
|
|
1300
|
+
* @internal
|
|
1301
|
+
*/ static sampleWaterCaustic(scope, worldPos, lightType, lightDirection, defocus = true) {
|
|
1302
|
+
const pb = scope.$builder;
|
|
1303
|
+
const funcName = defocus ? 'Z_calculateWaterCaustic' : 'Z_calculateWaterCausticVolume';
|
|
1304
|
+
pb.func(funcName, [
|
|
1305
|
+
pb.vec3('worldPos'),
|
|
1306
|
+
pb.int('lightType'),
|
|
1307
|
+
pb.vec3('lightDirection')
|
|
1308
|
+
], function() {
|
|
1309
|
+
this.$l.cu = this[UNIFORM_NAME_CAUSTIC_PARAMS];
|
|
1310
|
+
// Only the one light the map was built for casts caustics.
|
|
1311
|
+
this.$if(pb.or(pb.notEqual(this.lightType, LIGHT_TYPE_DIRECTIONAL), pb.lessThan(pb.dot(this.lightDirection, this.cu.lightDir.xyz), 0.999)), function() {
|
|
1312
|
+
this.$return(pb.vec3(1));
|
|
1313
|
+
});
|
|
1314
|
+
// Project into the map: frameX/frameY are orthonormal and perpendicular to
|
|
1315
|
+
// the light, so the same two dot products the splat pass used invert it.
|
|
1316
|
+
this.$l.rel = pb.sub(this.worldPos, this.cu.center.xyz);
|
|
1317
|
+
// Per-axis inverse extents: the slice is fitted to the water, so it is
|
|
1318
|
+
// only square in texels, not in meters.
|
|
1319
|
+
this.$l.mapNDC = pb.mul(pb.vec2(pb.dot(this.rel, this.cu.frameX.xyz), pb.dot(this.rel, this.cu.frameY.xyz)), pb.vec2(this.cu.frameX.w, this.cu.frameY.w));
|
|
1320
|
+
// Texels are concentrated towards the middle of the map, so the lookup
|
|
1321
|
+
// goes through the same warp the splat laid the photons out under.
|
|
1322
|
+
this.$l.warpedNDC = ShaderHelper.warpCausticNDC(this, this.mapNDC, this.cu.extinction.w);
|
|
1323
|
+
this.$l.uv = pb.add(pb.mul(this.warpedNDC, 0.5), pb.vec2(0.5));
|
|
1324
|
+
// The displaced surface height of each water slot on this fragment's own
|
|
1325
|
+
// sun ray, biased by CAUSTIC_HEIGHT_BIAS. The height map rasterises the
|
|
1326
|
+
// displaced surface into the same light-space slice as the pattern, so
|
|
1327
|
+
// the texel under this uv holds the surface the ray through this fragment
|
|
1328
|
+
// passed through - no entry point has to be estimated. Beyond the map the
|
|
1329
|
+
// sampler would repeat its border, which is some other ray's wave; the
|
|
1330
|
+
// surface is taken as flat there.
|
|
1331
|
+
this.$l.insideMap = pb.all(pb.lessThanEqual(pb.abs(this.mapNDC), pb.vec2(1)));
|
|
1332
|
+
// How far this fragment is towards the map border, and how much the map is
|
|
1333
|
+
// therefore trusted. Hoisted above the water loop because the wave height
|
|
1334
|
+
// is faded by it too, not just the pattern.
|
|
1335
|
+
//
|
|
1336
|
+
// Measured on the linear coordinate, not the warped one. The warp fixes
|
|
1337
|
+
// both ends of [-1, 1], so the border is in the same world place either
|
|
1338
|
+
// way, but the band between them is not: warping it would make its world
|
|
1339
|
+
// width depend on the warp strength, and the CPU sized it in meters.
|
|
1340
|
+
//
|
|
1341
|
+
// Distance is measured under an L4 norm rather than per-axis. A per-axis
|
|
1342
|
+
// fade is a rectangle in light space, and the sun tilts that rectangle, so
|
|
1343
|
+
// its border lands on the sea bed as straight lines - which the eye picks
|
|
1344
|
+
// out of a caustic pattern however wide the gradient is. An L2 circle has
|
|
1345
|
+
// no straight part anywhere but discards the 21% of the map outside its
|
|
1346
|
+
// inscribed disc; L4 gives up 7% instead and still curves everywhere.
|
|
1347
|
+
this.$l.ndcSq = pb.mul(this.mapNDC, this.mapNDC);
|
|
1348
|
+
this.$l.edgeDist = pb.sqrt(pb.sqrt(pb.dot(this.ndcSq, this.ndcSq)));
|
|
1349
|
+
this.$l.edge = pb.sub(1, pb.smoothStep(this.cu.params.w, 1, this.edgeDist));
|
|
1350
|
+
this.$l.waveHeights = pb.textureSampleLevel(this[UNIFORM_NAME_CAUSTIC_HEIGHT_MAP], this.uv, 0);
|
|
1351
|
+
// Find the water above this point. Several can qualify - a pool inside a
|
|
1352
|
+
// lake, water on terraces - and the one that matters is the lowest of
|
|
1353
|
+
// them, because that is the last surface the sun crossed on its way here.
|
|
1354
|
+
this.$l.depth = pb.float(-1);
|
|
1355
|
+
this.$l.medium = pb.vec3(0);
|
|
1356
|
+
this.$for(pb.int('i'), 0, MAX_CAUSTIC_WATERS, function() {
|
|
1357
|
+
this.$if(pb.greaterThanEqual(this.i, this.cu.waterCount), function() {
|
|
1358
|
+
this.$break();
|
|
1359
|
+
});
|
|
1360
|
+
this.$l.slotMedium = this.cu.waterMedia.at(this.i);
|
|
1361
|
+
// Where the sun ray that lights this point crossed that water's rest
|
|
1362
|
+
// plane, for the footprint test. The splat pass clips photons by that
|
|
1363
|
+
// same crossing, so the receiver must ask the same question: a point
|
|
1364
|
+
// under the footprint that the sun reaches from outside it saw no water
|
|
1365
|
+
// at all, and a point beyond the footprint that the sun reaches through
|
|
1366
|
+
// it did. Testing the point's own xz gets both wrong by the offset
|
|
1367
|
+
// depth / tan(elevation). Only the footprint is decided here; the depth
|
|
1368
|
+
// itself comes from the height map, which needs no crossing point.
|
|
1369
|
+
this.$l.slotFlat = pb.sub(this.slotMedium.w, this.worldPos.y);
|
|
1370
|
+
this.$l.slotPath = pb.mul(this.slotFlat, this.cu.lightDir.w);
|
|
1371
|
+
this.$l.slotEntry = pb.sub(this.worldPos.xz, pb.mul(this.cu.lightDir.xz, this.slotPath));
|
|
1372
|
+
this.$l.slotRegion = this.cu.waterRegions.at(this.i);
|
|
1373
|
+
this.$if(pb.and(pb.all(pb.greaterThanEqual(this.slotEntry, this.slotRegion.xy)), pb.all(pb.lessThanEqual(this.slotEntry, this.slotRegion.zw))), function() {
|
|
1374
|
+
// Depth under the *displaced* surface on this ray, which is what
|
|
1375
|
+
// the splat refracted the photons through. Gating on the rest plane
|
|
1376
|
+
// instead cut the caustics off at a flat line across any receiver
|
|
1377
|
+
// that pokes above it under a crest. Slot i lives in channel i.
|
|
1378
|
+
this.$l.slotMask = pb.vec4(pb.float(pb.equal(this.i, 0)), pb.float(pb.equal(this.i, 1)), pb.float(pb.equal(this.i, 2)), pb.float(pb.equal(this.i, 3)));
|
|
1379
|
+
this.$l.slotSample = pb.dot(this.waveHeights, this.slotMask);
|
|
1380
|
+
// A cleared texel inside the map means this ray never meets that
|
|
1381
|
+
// body's surface - it crosses the rest plane beyond the footprint,
|
|
1382
|
+
// or the surface is on the far side of the receiver - and the
|
|
1383
|
+
// receiver is dry as far as this body is concerned. Outside the map
|
|
1384
|
+
// nothing is known and the surface is taken as flat.
|
|
1385
|
+
this.$l.slotCovered = pb.or(pb.not(this.insideMap), pb.greaterThan(this.slotSample, 0.5));
|
|
1386
|
+
this.$if(this.slotCovered, function() {
|
|
1387
|
+
// Faded into the rest plane over the same band the pattern fades
|
|
1388
|
+
// on, rather than switched off at the border. Beyond the map the
|
|
1389
|
+
// surface is taken as flat, and exp(-sigma * depth) is convex, so
|
|
1390
|
+
// a hard switch makes the wavy side systematically brighter than
|
|
1391
|
+
// the flat side - E[exp(-sigma*w)] > 1 - and the step lands as a
|
|
1392
|
+
// mean brightness seam that grows with both the swell and the
|
|
1393
|
+
// turbidity. Ramping the wave to zero puts the two sides on the
|
|
1394
|
+
// same value at the border.
|
|
1395
|
+
this.$l.slotWave = pb.mul(pb.sub(this.slotSample, CAUSTIC_HEIGHT_BIAS), pb.float(this.insideMap), this.edge);
|
|
1396
|
+
this.$l.slotDepth = pb.sub(pb.add(this.slotMedium.w, this.slotWave), this.worldPos.y);
|
|
1397
|
+
this.$if(pb.greaterThan(this.slotDepth, 0), function() {
|
|
1398
|
+
this.$if(pb.or(pb.lessThan(this.depth, 0), pb.lessThan(this.slotDepth, this.depth)), function() {
|
|
1399
|
+
this.depth = this.slotDepth;
|
|
1400
|
+
this.medium = this.slotMedium.xyz;
|
|
1401
|
+
});
|
|
1402
|
+
});
|
|
1403
|
+
});
|
|
1404
|
+
});
|
|
1405
|
+
});
|
|
1406
|
+
this.$if(pb.lessThanEqual(this.depth, 0), function() {
|
|
1407
|
+
this.$return(pb.vec3(1));
|
|
1408
|
+
});
|
|
1409
|
+
this.$l.pathLength = pb.mul(this.depth, this.cu.lightDir.w);
|
|
1410
|
+
this.$l.transmittance = pb.exp(pb.neg(pb.mul(this.medium, this.pathLength)));
|
|
1411
|
+
this.$l.pattern = pb.textureSampleLevel(this[UNIFORM_NAME_CAUSTIC_MAP], this.uv, 0).x;
|
|
1412
|
+
if (defocus) {
|
|
1413
|
+
// Receivers away from the focal plane see a defocused, lower-contrast
|
|
1414
|
+
// pattern rather than a displaced one.
|
|
1415
|
+
this.$l.defocus = pb.div(1, pb.add(1, pb.mul(pb.abs(pb.sub(this.depth, this.cu.params.y)), this.cu.params.z)));
|
|
1416
|
+
} else {
|
|
1417
|
+
// A point inside the water column is not a receiver, and has no depth of
|
|
1418
|
+
// its own to be out of focus at - the volumetric march visits every
|
|
1419
|
+
// depth along the ray. Charging it the receiver's fade makes the light
|
|
1420
|
+
// shafts fade according to where the *sea bed* was told to be sharpest:
|
|
1421
|
+
// the open-ocean preset focuses the map at 26m for its bed, which at the
|
|
1422
|
+
// default defocus leaves a point 2m down at 26% contrast, and the shafts
|
|
1423
|
+
// dissolve into grain. The shafts instead take the pattern as the map
|
|
1424
|
+
// holds it and fade with the edge and the transmittance like everything
|
|
1425
|
+
// else in the column.
|
|
1426
|
+
this.$l.defocus = pb.float(1);
|
|
1427
|
+
}
|
|
1428
|
+
this.$l.weight = pb.mul(this.cu.params.x, this.defocus, this.edge);
|
|
1429
|
+
this.$l.caustic = pb.add(1, pb.mul(pb.sub(this.pattern, 1), this.weight));
|
|
1430
|
+
// Soften the waterline so the transmittance does not switch on abruptly.
|
|
1431
|
+
this.$l.submerged = pb.smoothStep(0, 0.05, this.depth);
|
|
1432
|
+
this.$return(pb.mix(pb.vec3(1), pb.mul(pb.vec3(this.caustic), this.transmittance), this.submerged));
|
|
1433
|
+
});
|
|
1434
|
+
return pb.getGlobalScope()[funcName](worldPos, lightType, lightDirection);
|
|
1435
|
+
}
|
|
1436
|
+
/** @internal */ static setWaterCausticUniforms(bindGroup, ctx) {
|
|
1437
|
+
const uniforms = ctx.waterCausticUniforms;
|
|
1438
|
+
bindGroup.setValue(UNIFORM_NAME_CAUSTIC_PARAMS, {
|
|
1439
|
+
frameX: uniforms.frameX,
|
|
1440
|
+
frameY: uniforms.frameY,
|
|
1441
|
+
center: uniforms.center,
|
|
1442
|
+
lightDir: uniforms.lightDir,
|
|
1443
|
+
params: uniforms.params,
|
|
1444
|
+
extinction: uniforms.extinction,
|
|
1445
|
+
waterCount: uniforms.waterCount,
|
|
1446
|
+
waterRegions: uniforms.waterRegions,
|
|
1447
|
+
waterMedia: uniforms.waterMedia
|
|
1448
|
+
});
|
|
1449
|
+
bindGroup.setTexture(UNIFORM_NAME_CAUSTIC_MAP, ctx.waterCausticTexture, fetchSampler('clamp_linear_nomip'));
|
|
1450
|
+
bindGroup.setTexture(UNIFORM_NAME_CAUSTIC_HEIGHT_MAP, ctx.waterCausticHeightTexture, fetchSampler('clamp_linear_nomip'));
|
|
1451
|
+
}
|
|
841
1452
|
/** @internal */ static setLightUniformsShadow(bindGroup, ctx, light) {
|
|
842
1453
|
const shadowMapParams = ctx.shadowMapInfo.get(light);
|
|
843
1454
|
this._lightUniformShadow.sunDir = ctx.sunLight ? ctx.sunLight.directionAndCutoff.xyz().scaleBy(-1) : this.defaultSunDir;
|
|
@@ -857,6 +1468,9 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
857
1468
|
this._lightUniformShadow.envLightSpecularStrength = ctx.env?.light.specularStrength ?? 1;
|
|
858
1469
|
bindGroup.setValue('light', this._lightUniformShadow);
|
|
859
1470
|
bindGroup.setTexture(UNIFORM_NAME_SHADOW_MAP, shadowMapParams.shadowMap, shadowMapParams.shadowMapSampler);
|
|
1471
|
+
if (this.usesWaterCaustics(ctx)) {
|
|
1472
|
+
this.setWaterCausticUniforms(bindGroup, ctx);
|
|
1473
|
+
}
|
|
860
1474
|
bindGroup.setTexture(UNIFORM_NAME_BAKED_SKY_MAP, ctx.scene.env.sky.getBakedSkyTexture(ctx));
|
|
861
1475
|
if (ctx.drawEnvLight) {
|
|
862
1476
|
ctx.env.light.envLight.updateBindGroup(bindGroup, ctx);
|
|
@@ -978,6 +1592,50 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
978
1592
|
return scope.camera.elapsedTime;
|
|
979
1593
|
}
|
|
980
1594
|
/**
|
|
1595
|
+
* Declares the one camera field {@link ShaderHelper.getElapsedTime} reads.
|
|
1596
|
+
*
|
|
1597
|
+
* Wave generators animate off `camera.elapsedTime`, which the light and depth
|
|
1598
|
+
* passes provide as part of the full camera struct. A standalone program that
|
|
1599
|
+
* drives a wave generator - the caustics splat pass - has no such struct, so it
|
|
1600
|
+
* declares that single field under the same name. Any generator that starts
|
|
1601
|
+
* reading a second camera field will need it added here.
|
|
1602
|
+
*
|
|
1603
|
+
* The three padding floats round the struct up to 16 bytes. std140 aligns a
|
|
1604
|
+
* struct to a vec4 boundary, so a lone float leaves the GLSL block larger than
|
|
1605
|
+
* the buffer the engine sizes from the struct's own members - WebGL2 then
|
|
1606
|
+
* rejects every draw with "uniform buffer that is too small" and rasterises
|
|
1607
|
+
* nothing, while WebGPU is unaffected.
|
|
1608
|
+
*
|
|
1609
|
+
* @param scope - Global scope of the standalone program.
|
|
1610
|
+
* @param uniformGroup - Bind group the uniform belongs to.
|
|
1611
|
+
* @internal
|
|
1612
|
+
*/ static declareStandaloneCameraTime(scope, uniformGroup) {
|
|
1613
|
+
const pb = scope.$builder;
|
|
1614
|
+
scope.camera = pb.defineStruct([
|
|
1615
|
+
pb.float('elapsedTime'),
|
|
1616
|
+
pb.float('_pad0'),
|
|
1617
|
+
pb.float('_pad1'),
|
|
1618
|
+
pb.float('_pad2')
|
|
1619
|
+
])().uniform(uniformGroup);
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Uploads the value declared by {@link ShaderHelper.declareStandaloneCameraTime}.
|
|
1623
|
+
*
|
|
1624
|
+
* Must be the same clock the light pass uses, or a wave generator's caustics
|
|
1625
|
+
* would animate out of step with the surface they are refracted through.
|
|
1626
|
+
*
|
|
1627
|
+
* @param bindGroup - Bind group of the standalone program.
|
|
1628
|
+
* @param ctx - Current draw context.
|
|
1629
|
+
* @internal
|
|
1630
|
+
*/ static setStandaloneCameraTime(bindGroup, ctx) {
|
|
1631
|
+
bindGroup.setValue('camera', {
|
|
1632
|
+
elapsedTime: ctx.device.frameInfo.elapsedOverall * 0.001,
|
|
1633
|
+
_pad0: 0,
|
|
1634
|
+
_pad1: 0,
|
|
1635
|
+
_pad2: 0
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
981
1639
|
* Gets the elapsed time since last frame in seconds
|
|
982
1640
|
* @param scope - Current shader scope
|
|
983
1641
|
* @returns The elapsed time since last frame in seconds
|
|
@@ -1291,18 +1949,67 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1291
1949
|
return scope[UNIFORM_NAME_SHADOW_MAP];
|
|
1292
1950
|
}
|
|
1293
1951
|
/**
|
|
1952
|
+
* Per-cascade scale for both bias terms.
|
|
1953
|
+
*
|
|
1954
|
+
* @remarks
|
|
1955
|
+
* Only `depthBiasValues[0]` is uploaded; every cascade's value is recovered by
|
|
1956
|
+
* multiplying with this ratio. It is derived from the `.x` components, but
|
|
1957
|
+
* since `.x` and the normal offset `.y` are both proportional to the cascade's
|
|
1958
|
+
* texel world size, the same ratio applies to either.
|
|
1959
|
+
* @internal
|
|
1960
|
+
*/ static getShadowCascadeBiasScale(scope, split) {
|
|
1961
|
+
const pb = scope.$builder;
|
|
1962
|
+
const splitFlags = pb.vec4(pb.float(pb.equal(split, 0)), pb.float(pb.equal(split, 1)), pb.float(pb.equal(split, 2)), pb.float(pb.equal(split, 3)));
|
|
1963
|
+
return pb.dot(this.getDepthBiasScales(scope), splitFlags);
|
|
1964
|
+
}
|
|
1965
|
+
/**
|
|
1966
|
+
* Moves the receiver along its normal before the shadow lookup.
|
|
1967
|
+
*
|
|
1968
|
+
* @remarks
|
|
1969
|
+
* Grazing-angle self-shadowing cannot be fixed with a depth bias: the depth
|
|
1970
|
+
* spanned by one shadow texel is `texelWorldSize * tan(theta)`, which diverges
|
|
1971
|
+
* as NdotL approaches zero. Offsetting the sample position sideways moves it
|
|
1972
|
+
* out of the offending texel instead, and the required distance is bounded.
|
|
1973
|
+
*
|
|
1974
|
+
* `depthBiasValues.y` carries the world-space distance of one `normalBias`
|
|
1975
|
+
* unit (see `ShadowMapper.calcDepthBiasParams`).
|
|
1976
|
+
* @internal
|
|
1977
|
+
*/ static applyShadowNormalOffset(lightType, scope, worldPos, worldNormal, NdotL, cascadeScale) {
|
|
1978
|
+
const pb = scope.$builder;
|
|
1979
|
+
const depthBiasParam = this.getDepthBiasValues(scope);
|
|
1980
|
+
const receiverNoL = getShadowReceiverNoL(scope, NdotL, lightType);
|
|
1981
|
+
// sin(theta): 0 when facing the light, 1 at grazing incidence.
|
|
1982
|
+
const sinTheta = pb.sqrt(pb.clamp(pb.sub(1, pb.mul(receiverNoL, receiverNoL)), 0, 1));
|
|
1983
|
+
let offset = pb.mul(depthBiasParam.y, getShadowReceiverBiasFactor(scope), sinTheta);
|
|
1984
|
+
if (cascadeScale) {
|
|
1985
|
+
offset = pb.mul(offset, cascadeScale);
|
|
1986
|
+
}
|
|
1987
|
+
if (lightType !== LIGHT_TYPE_DIRECTIONAL) {
|
|
1988
|
+
// Perspective shadow cameras: one texel covers more world space further
|
|
1989
|
+
// from the light. depthBiasValues.w is the far/near footprint ratio.
|
|
1990
|
+
const posRange = this.getLightPositionAndRangeForShadow(scope);
|
|
1991
|
+
const linearDepth = pb.clamp(pb.div(pb.distance(worldPos, posRange.xyz), pb.max(posRange.w, 1e-6)), 0, 1);
|
|
1992
|
+
offset = pb.mul(offset, pb.mix(1, depthBiasParam.w, linearDepth));
|
|
1993
|
+
}
|
|
1994
|
+
// Guard against a degenerate interpolated normal rather than normalize()ing
|
|
1995
|
+
// a possibly-zero vector into NaN.
|
|
1996
|
+
const unitNormal = pb.mul(worldNormal, pb.div(1, pb.max(pb.length(worldNormal), 1e-6)));
|
|
1997
|
+
return pb.add(worldPos, pb.mul(unitNormal, offset));
|
|
1998
|
+
}
|
|
1999
|
+
/**
|
|
1294
2000
|
* Calculates shadow of current fragment
|
|
1295
2001
|
*
|
|
1296
2002
|
* @param scope - Shader scope
|
|
1297
2003
|
* @param NoL - NdotL vector
|
|
1298
2004
|
* @returns Shadow of current fragment, 1 means no shadow and 0 means full shadowed.
|
|
1299
|
-
*/ static calculateShadow(scope, worldPos, NoL, ctx) {
|
|
2005
|
+
*/ static calculateShadow(scope, worldPos, worldNormal, NoL, ctx) {
|
|
1300
2006
|
const pb = scope.$builder;
|
|
1301
2007
|
const that = this;
|
|
1302
2008
|
const shadowMapParams = ctx.shadowMapInfo.get(ctx.currentShadowLight);
|
|
1303
2009
|
const funcName = 'Z_calculateShadow';
|
|
1304
2010
|
pb.func(funcName, [
|
|
1305
2011
|
pb.vec3('worldPos'),
|
|
2012
|
+
pb.vec3('worldNormal'),
|
|
1306
2013
|
pb.float('NoL')
|
|
1307
2014
|
], function() {
|
|
1308
2015
|
if (shadowMapParams.numShadowCascades > 1) {
|
|
@@ -1313,18 +2020,21 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1313
2020
|
this.$l.comparison = pb.vec4(pb.greaterThan(pb.vec4(this.linearDepth), this.splitDistances));
|
|
1314
2021
|
this.$l.cascadeFlags = pb.vec4(pb.float(pb.greaterThan(this.shadowCascades, 0)), pb.float(pb.greaterThan(this.shadowCascades, 1)), pb.float(pb.greaterThan(this.shadowCascades, 2)), pb.float(pb.greaterThan(this.shadowCascades, 3)));
|
|
1315
2022
|
this.$l.split = pb.int(pb.dot(this.comparison, this.cascadeFlags));
|
|
2023
|
+
// Normal offset bias: move the receiver out of the shadow texel that
|
|
2024
|
+
// would self-occlude it before projecting into shadow space. Scaled per
|
|
2025
|
+
// cascade, because each cascade has its own texel world size.
|
|
2026
|
+
this.$l.biasedPos = that.applyShadowNormalOffset(shadowMapParams.lightType, this, this.worldPos, this.worldNormal, this.NoL, that.getShadowCascadeBiasScale(this, this.split));
|
|
1316
2027
|
if (ctx.device.type === 'webgl') {
|
|
1317
2028
|
this.$l.shadowVertex = pb.vec4();
|
|
1318
2029
|
this.$for(pb.int('cascade'), 0, 4, function() {
|
|
1319
2030
|
this.$if(pb.equal(this.cascade, this.split), function() {
|
|
1320
|
-
this.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.
|
|
2031
|
+
this.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.biasedPos, 1), this.cascade);
|
|
1321
2032
|
this.$break();
|
|
1322
2033
|
});
|
|
1323
2034
|
});
|
|
1324
2035
|
} else {
|
|
1325
|
-
this.$l.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.
|
|
2036
|
+
this.$l.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.biasedPos, 1), this.split);
|
|
1326
2037
|
}
|
|
1327
|
-
const shadowMapParams = ctx.shadowMapInfo.get(ctx.currentShadowLight);
|
|
1328
2038
|
this.$l.shadow = shadowMapParams.impl.computeShadowCSM(shadowMapParams, this, this.shadowVertex, this.NoL, this.split);
|
|
1329
2039
|
this.shadow = pb.clamp(this.shadow, 0, 1);
|
|
1330
2040
|
this.$l.shadowDistance = that.getShadowCameraParams(scope).w;
|
|
@@ -1336,8 +2046,9 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1336
2046
|
});
|
|
1337
2047
|
this.$return(this.shadow);
|
|
1338
2048
|
} else {
|
|
1339
|
-
|
|
1340
|
-
|
|
2049
|
+
// Normal offset bias - see the cascaded branch above.
|
|
2050
|
+
this.$l.biasedPos = that.applyShadowNormalOffset(shadowMapParams.lightType, this, this.worldPos, this.worldNormal, this.NoL);
|
|
2051
|
+
this.$l.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.biasedPos, 1));
|
|
1341
2052
|
this.$l.shadow = shadowMapParams.impl.computeShadow(shadowMapParams, this, this.shadowVertex, this.NoL);
|
|
1342
2053
|
this.shadow = pb.clamp(this.shadow, 0, 1);
|
|
1343
2054
|
this.$l.shadowDistance = that.getShadowCameraParams(scope).w;
|
|
@@ -1347,7 +2058,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1347
2058
|
this.$return(this.shadow);
|
|
1348
2059
|
}
|
|
1349
2060
|
});
|
|
1350
|
-
return pb.getGlobalScope()[funcName](worldPos, NoL);
|
|
2061
|
+
return pb.getGlobalScope()[funcName](worldPos, worldNormal, NoL);
|
|
1351
2062
|
}
|
|
1352
2063
|
static applyFog(scope, worldPos, color, ctx) {
|
|
1353
2064
|
const pb = scope.$builder;
|
|
@@ -1555,5 +2266,5 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1555
2266
|
}
|
|
1556
2267
|
}
|
|
1557
2268
|
|
|
1558
|
-
export { ShaderHelper };
|
|
2269
|
+
export { CAUSTIC_HEIGHT_BIAS, MAX_CAUSTIC_WATERS, ShaderHelper };
|
|
1559
2270
|
//# sourceMappingURL=helper.js.map
|