super-three 0.184.0 → 0.185.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/build/three.cjs +1260 -927
- package/build/three.core.js +1104 -829
- package/build/three.core.min.js +1 -1
- package/build/three.module.js +156 -98
- package/build/three.module.min.js +1 -1
- package/build/three.tsl.js +11 -4
- package/build/three.tsl.min.js +1 -1
- package/build/three.webgpu.js +7538 -3367
- package/build/three.webgpu.min.js +1 -1
- package/build/three.webgpu.nodes.js +7538 -3366
- package/build/three.webgpu.nodes.min.js +1 -1
- package/examples/jsm/Addons.js +3 -0
- package/examples/jsm/controls/ArcballControls.js +6 -2
- package/examples/jsm/controls/FirstPersonControls.js +79 -42
- package/examples/jsm/controls/TransformControls.js +68 -15
- package/examples/jsm/csm/CSMFrustum.js +31 -6
- package/examples/jsm/csm/CSMShadowNode.js +4 -1
- package/examples/jsm/effects/AsciiEffect.js +14 -1
- package/examples/jsm/exporters/DRACOExporter.js +27 -6
- package/examples/jsm/exporters/GLTFExporter.js +114 -12
- package/examples/jsm/exporters/PLYExporter.js +286 -73
- package/examples/jsm/exporters/USDZExporter.js +347 -112
- package/examples/jsm/generators/CityGenerator.js +346 -0
- package/examples/jsm/generators/ForestGenerator.js +347 -0
- package/examples/jsm/generators/TerrainGenerator.js +504 -0
- package/examples/jsm/generators/TreeGenerator.js +377 -0
- package/examples/jsm/generators/city/SidewalkGenerator.js +253 -0
- package/examples/jsm/generators/city/SkyscraperGenerator.js +1357 -0
- package/examples/jsm/geometries/LoftGeometry.js +355 -0
- package/examples/jsm/helpers/LightProbeGridHelper.js +1 -1
- package/examples/jsm/helpers/LightProbeHelper.js +5 -4
- package/examples/jsm/helpers/ViewHelper.js +16 -6
- package/examples/jsm/inspector/Inspector.js +53 -15
- package/examples/jsm/inspector/RendererInspector.js +152 -36
- package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphEditor.js +1 -1
- package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphLoader.js +1 -1
- package/examples/jsm/inspector/tabs/Console.js +200 -18
- package/examples/jsm/inspector/tabs/Memory.js +8 -0
- package/examples/jsm/inspector/tabs/Parameters.js +64 -4
- package/examples/jsm/inspector/tabs/Performance.js +21 -6
- package/examples/jsm/inspector/tabs/Settings.js +51 -9
- package/examples/jsm/inspector/tabs/Timeline.js +178 -93
- package/examples/jsm/inspector/tabs/Viewer.js +628 -16
- package/examples/jsm/inspector/ui/Graph.js +162 -25
- package/examples/jsm/inspector/ui/Item.js +92 -13
- package/examples/jsm/inspector/ui/List.js +1 -1
- package/examples/jsm/inspector/ui/Profiler.js +90 -45
- package/examples/jsm/inspector/ui/Style.js +1788 -1401
- package/examples/jsm/inspector/ui/Tab.js +5 -3
- package/examples/jsm/inspector/ui/Values.js +71 -6
- package/examples/jsm/inspector/ui/utils.js +130 -10
- package/examples/jsm/libs/basis/README.md +0 -1
- package/examples/jsm/libs/demuxer_mp4.js +3 -3
- package/examples/jsm/libs/draco/README.md +0 -1
- package/examples/jsm/libs/meshopt_clusterizer.module.js +421 -0
- package/examples/jsm/libs/meshopt_simplifier.module.js +627 -0
- package/examples/jsm/libs/mikktspace.module.js +34 -9
- package/examples/jsm/lighting/ClusteredLighting.js +55 -0
- package/examples/jsm/lighting/LightProbeGrid.js +106 -76
- package/examples/jsm/lines/LineMaterial.js +38 -15
- package/examples/jsm/lines/LineSegments2.js +8 -0
- package/examples/jsm/lines/webgpu/LineSegments2.js +8 -0
- package/examples/jsm/loaders/3DMLoader.js +1 -1
- package/examples/jsm/loaders/DRACOLoader.js +46 -13
- package/examples/jsm/loaders/EXRLoader.js +27 -55
- package/examples/jsm/loaders/HDRLoader.js +6 -28
- package/examples/jsm/loaders/KTX2Loader.js +31 -12
- package/examples/jsm/loaders/LDrawLoader.js +8 -8
- package/examples/jsm/loaders/LUT3dlLoader.js +2 -2
- package/examples/jsm/loaders/LUTCubeLoader.js +2 -2
- package/examples/jsm/loaders/LWOLoader.js +4 -0
- package/examples/jsm/loaders/LottieLoader.js +1 -1
- package/examples/jsm/loaders/MaterialXLoader.js +31 -9
- package/examples/jsm/loaders/NRRDLoader.js +3 -3
- package/examples/jsm/loaders/PCDLoader.js +7 -7
- package/examples/jsm/loaders/PLYLoader.js +218 -55
- package/examples/jsm/loaders/SVGLoader.js +547 -495
- package/examples/jsm/loaders/TTFLoader.js +1 -1
- package/examples/jsm/loaders/USDLoader.js +33 -9
- package/examples/jsm/loaders/VRMLLoader.js +1 -1
- package/examples/jsm/loaders/usd/USDAParser.js +93 -20
- package/examples/jsm/loaders/usd/USDCParser.js +1 -1
- package/examples/jsm/loaders/usd/USDComposer.js +52 -19
- package/examples/jsm/misc/TileCreasedNormalsPlugin.js +270 -0
- package/examples/jsm/misc/Volume.js +2 -2
- package/examples/jsm/objects/GroundedSkybox.js +1 -1
- package/examples/jsm/objects/Reflector.js +2 -3
- package/examples/jsm/objects/Water2Mesh.js +1 -1
- package/examples/jsm/physics/AmmoPhysics.js +15 -8
- package/examples/jsm/physics/RapierPhysics.js +27 -2
- package/examples/jsm/shaders/VolumeShader.js +31 -44
- package/examples/jsm/transpiler/GLSLDecoder.js +6 -6
- package/examples/jsm/transpiler/Linker.js +1 -1
- package/examples/jsm/tsl/display/BloomNode.js +59 -19
- package/examples/jsm/tsl/display/DepthOfFieldNode.js +2 -2
- package/examples/jsm/tsl/display/FXAANode.js +11 -12
- package/examples/jsm/tsl/display/GTAONode.js +34 -13
- package/examples/jsm/tsl/display/GodraysNode.js +1 -1
- package/examples/jsm/tsl/display/ImportanceSampledEnvironment.js +560 -0
- package/examples/jsm/tsl/display/PixelationPassNode.js +2 -2
- package/examples/jsm/tsl/display/RecurrentDenoiseNode.js +912 -0
- package/examples/jsm/tsl/display/SSAAPassNode.js +13 -25
- package/examples/jsm/tsl/display/SSGINode.js +89 -39
- package/examples/jsm/tsl/display/SSRNode.js +829 -132
- package/examples/jsm/tsl/display/SSSNode.js +2 -2
- package/examples/jsm/tsl/display/TemporalReprojectNode.js +1023 -0
- package/examples/jsm/tsl/lighting/ClusteredLightsNode.js +622 -0
- package/examples/jsm/tsl/lighting/DynamicLightsNode.js +1 -1
- package/examples/jsm/tsl/math/curlNoise.js +107 -0
- package/examples/jsm/tsl/shadows/TileShadowNode.js +1 -1
- package/examples/jsm/tsl/shadows/TileShadowNodeHelper.js +3 -3
- package/examples/jsm/tsl/utils/GroundedSkybox.js +62 -0
- package/examples/jsm/tsl/utils/RNoise.js +51 -0
- package/examples/jsm/tsl/utils/SpecularHelpers.js +325 -0
- package/examples/jsm/utils/BufferGeometryUtils.js +126 -59
- package/examples/jsm/utils/GeometryCompressionUtils.js +1 -1
- package/examples/jsm/utils/SceneOptimizer.js +1 -1
- package/examples/jsm/webxr/ARButton.js +1 -1
- package/examples/jsm/webxr/WebGLXRFallback.js +89 -0
- package/examples/jsm/webxr/XRControllerModelFactory.js +2 -2
- package/package.json +7 -7
- package/src/Three.TSL.js +10 -3
- package/src/Three.WebGPU.js +4 -0
- package/src/animation/AnimationAction.js +15 -11
- package/src/animation/AnimationClip.js +0 -139
- package/src/animation/KeyframeTrack.js +2 -2
- package/src/animation/PropertyBinding.js +2 -2
- package/src/cameras/Camera.js +2 -2
- package/src/cameras/StereoCamera.js +5 -2
- package/src/constants.js +1 -1
- package/src/core/BufferGeometry.js +29 -7
- package/src/core/Object3D.js +17 -7
- package/src/core/Raycaster.js +1 -1
- package/src/core/RenderTarget.js +15 -2
- package/src/extras/PMREMGenerator.js +6 -4
- package/src/extras/TextureUtils.js +1 -1
- package/src/extras/core/ShapePath.js +149 -160
- package/src/geometries/SphereGeometry.js +8 -3
- package/src/helpers/DirectionalLightHelper.js +2 -0
- package/src/helpers/HemisphereLightHelper.js +2 -0
- package/src/helpers/PointLightHelper.js +2 -0
- package/src/helpers/SpotLightHelper.js +1 -1
- package/src/loaders/DataTextureLoader.js +69 -38
- package/src/loaders/LoadingManager.js +5 -0
- package/src/loaders/MaterialLoader.js +36 -266
- package/src/loaders/ObjectLoader.js +3 -1
- package/src/materials/LineBasicMaterial.js +4 -0
- package/src/materials/Material.js +196 -1
- package/src/materials/MeshBasicMaterial.js +24 -0
- package/src/materials/MeshDepthMaterial.js +10 -0
- package/src/materials/MeshDistanceMaterial.js +10 -0
- package/src/materials/MeshLambertMaterial.js +40 -1
- package/src/materials/MeshMatcapMaterial.js +25 -1
- package/src/materials/MeshNormalMaterial.js +9 -1
- package/src/materials/MeshPhongMaterial.js +40 -1
- package/src/materials/MeshPhysicalMaterial.js +38 -0
- package/src/materials/MeshStandardMaterial.js +42 -1
- package/src/materials/MeshToonMaterial.js +34 -1
- package/src/materials/PointsMaterial.js +7 -0
- package/src/materials/ShaderMaterial.js +86 -0
- package/src/materials/SpriteMaterial.js +7 -0
- package/src/materials/nodes/Line2NodeMaterial.js +378 -322
- package/src/materials/nodes/MeshBasicNodeMaterial.js +2 -2
- package/src/materials/nodes/MeshNormalNodeMaterial.js +2 -2
- package/src/materials/nodes/MeshPhongNodeMaterial.js +0 -9
- package/src/materials/nodes/MeshPhysicalNodeMaterial.js +0 -30
- package/src/materials/nodes/MeshSSSNodeMaterial.js +0 -13
- package/src/materials/nodes/MeshStandardNodeMaterial.js +0 -11
- package/src/materials/nodes/NodeMaterial.js +115 -71
- package/src/materials/nodes/SpriteNodeMaterial.js +0 -10
- package/src/materials/nodes/manager/NodeMaterialObserver.js +49 -20
- package/src/math/Box3.js +5 -0
- package/src/math/FrustumArray.js +103 -133
- package/src/math/Interpolant.js +1 -1
- package/src/math/MathUtils.js +2 -2
- package/src/math/Matrix3.js +11 -0
- package/src/math/Matrix4.js +28 -3
- package/src/math/Vector2.js +2 -2
- package/src/math/Vector3.js +2 -2
- package/src/math/Vector4.js +2 -2
- package/src/math/interpolants/BezierInterpolant.js +4 -6
- package/src/nodes/Nodes.js +2 -5
- package/src/nodes/TSL.js +8 -6
- package/src/nodes/accessors/Arrays.js +4 -4
- package/src/nodes/accessors/Batch.js +108 -0
- package/src/nodes/accessors/Bitangent.js +2 -2
- package/src/nodes/accessors/BufferAttributeNode.js +13 -2
- package/src/nodes/accessors/Camera.js +7 -7
- package/src/nodes/accessors/ClippingNode.js +1 -1
- package/src/nodes/accessors/Instance.js +264 -0
- package/src/nodes/accessors/MaterialNode.js +18 -2
- package/src/nodes/accessors/MaterialProperties.js +4 -3
- package/src/nodes/accessors/{MorphNode.js → Morph.js} +99 -112
- package/src/nodes/accessors/Normal.js +13 -13
- package/src/nodes/accessors/Position.js +5 -1
- package/src/nodes/accessors/ReferenceNode.js +1 -0
- package/src/nodes/accessors/ReflectVector.js +3 -3
- package/src/nodes/accessors/SceneProperties.js +1 -1
- package/src/nodes/accessors/Skinning.js +263 -0
- package/src/nodes/accessors/StorageBufferNode.js +2 -2
- package/src/nodes/accessors/StorageTexture3DNode.js +100 -0
- package/src/nodes/accessors/StorageTextureNode.js +39 -7
- package/src/nodes/accessors/Tangent.js +4 -4
- package/src/nodes/accessors/TextureNode.js +60 -5
- package/src/nodes/code/FunctionNode.js +2 -16
- package/src/nodes/core/MRTNode.js +7 -2
- package/src/nodes/core/Node.js +39 -2
- package/src/nodes/core/NodeBuilder.js +201 -41
- package/src/nodes/core/NodeUtils.js +13 -13
- package/src/nodes/core/OutputStructNode.js +1 -1
- package/src/nodes/core/OverrideContextNode.js +153 -0
- package/src/nodes/core/PropertyNode.js +51 -5
- package/src/nodes/core/StackNode.js +56 -34
- package/src/nodes/core/StructNode.js +4 -6
- package/src/nodes/core/StructTypeNode.js +6 -6
- package/src/nodes/core/VarNode.js +8 -0
- package/src/nodes/core/VaryingNode.js +12 -2
- package/src/nodes/display/BlendModes.js +1 -41
- package/src/nodes/display/BumpMapNode.js +6 -1
- package/src/nodes/display/ColorAdjustment.js +10 -9
- package/src/nodes/display/FrontFacingNode.js +38 -9
- package/src/nodes/display/NormalMapNode.js +2 -2
- package/src/nodes/display/PassNode.js +35 -43
- package/src/nodes/display/PremultiplyAlphaFunctions.js +39 -0
- package/src/nodes/display/RenderOutputNode.js +12 -3
- package/src/nodes/functions/VolumetricLightingModel.js +40 -7
- package/src/nodes/gpgpu/WorkgroupInfoNode.js +1 -1
- package/src/nodes/lighting/EnvironmentNode.js +2 -2
- package/src/nodes/lighting/IESSpotLightNode.js +40 -2
- package/src/nodes/lighting/LightingContextNode.js +10 -2
- package/src/nodes/lighting/LightsNode.js +90 -37
- package/src/nodes/lighting/PointShadowNode.js +6 -1
- package/src/nodes/lighting/ShadowFilterNode.js +21 -52
- package/src/nodes/lighting/ShadowNode.js +34 -14
- package/src/nodes/materialx/lib/mx_noise.js +2 -2
- package/src/nodes/math/ConditionalNode.js +3 -3
- package/src/nodes/math/MathNode.js +39 -11
- package/src/nodes/math/OperatorNode.js +23 -23
- package/src/nodes/parsers/GLSLNodeFunction.js +1 -1
- package/src/nodes/pmrem/PMREMNode.js +33 -2
- package/src/nodes/tsl/TSLCore.js +63 -18
- package/src/nodes/utils/EquirectUV.js +30 -5
- package/src/nodes/utils/FunctionOverloadingNode.js +1 -1
- package/src/nodes/utils/Packing.js +35 -4
- package/src/nodes/utils/RTTNode.js +40 -24
- package/src/nodes/utils/ReflectorNode.js +1 -0
- package/src/nodes/utils/StorageArrayElementNode.js +1 -1
- package/src/objects/BatchedMesh.js +22 -14
- package/src/objects/InstancedMesh.js +0 -11
- package/src/objects/Skeleton.js +0 -9
- package/src/renderers/WebGLRenderer.js +19 -15
- package/src/renderers/common/Backend.js +49 -5
- package/src/renderers/common/Bindings.js +114 -45
- package/src/renderers/common/Buffer.js +9 -0
- package/src/renderers/common/ClippingContext.js +10 -2
- package/src/renderers/common/Info.js +63 -22
- package/src/renderers/common/Lighting.js +53 -5
- package/src/renderers/common/RenderList.js +41 -4
- package/src/renderers/common/RenderObject.js +73 -13
- package/src/renderers/common/Renderer.js +281 -108
- package/src/renderers/common/Sampler.js +16 -48
- package/src/renderers/common/Textures.js +32 -5
- package/src/renderers/common/TimestampQueryPool.js +1 -1
- package/src/renderers/common/UniformsGroup.js +31 -9
- package/src/renderers/common/XRManager.js +340 -22
- package/src/renderers/common/extras/PMREMGenerator.js +15 -38
- package/src/renderers/common/nodes/NodeBuilderState.js +9 -1
- package/src/renderers/common/nodes/NodeLibrary.js +4 -4
- package/src/renderers/common/nodes/NodeManager.js +63 -14
- package/src/renderers/common/nodes/NodeUniformBuffer.js +13 -0
- package/src/renderers/shaders/ShaderChunk/common.glsl.js +13 -4
- package/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js +0 -6
- package/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js +2 -2
- package/src/renderers/shaders/ShaderChunk/envmap_vertex.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js +2 -2
- package/src/renderers/shaders/ShaderChunk/normal_vertex.glsl.js +6 -0
- package/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/transmission_fragment.glsl.js +1 -1
- package/src/renderers/shaders/ShaderLib/distance.glsl.js +1 -1
- package/src/renderers/webgl/WebGLOutput.js +2 -1
- package/src/renderers/webgl/WebGLProgram.js +5 -4
- package/src/renderers/webgl/WebGLPrograms.js +12 -5
- package/src/renderers/webgl/WebGLRenderLists.js +9 -1
- package/src/renderers/webgl/WebGLShaderCache.js +5 -11
- package/src/renderers/webgl/WebGLTextures.js +20 -7
- package/src/renderers/webgl/WebGLUniformsGroups.js +66 -38
- package/src/renderers/webgl-fallback/WebGLBackend.js +84 -25
- package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +138 -8
- package/src/renderers/webgl-fallback/utils/WebGLAttributeUtils.js +1 -1
- package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +52 -22
- package/src/renderers/webgl-fallback/utils/WebGLUtils.js +4 -2
- package/src/renderers/webgpu/WebGPUBackend.js +480 -169
- package/src/renderers/webgpu/descriptors/GPUBindGroupDescriptor.js +48 -0
- package/src/renderers/webgpu/descriptors/GPUBufferDescriptor.js +57 -0
- package/src/renderers/webgpu/descriptors/GPUCommandEncoderDescriptor.js +30 -0
- package/src/renderers/webgpu/descriptors/GPUComputePassDescriptor.js +38 -0
- package/src/renderers/webgpu/descriptors/GPUComputePipelineDescriptor.js +48 -0
- package/src/renderers/webgpu/descriptors/GPUCopyExternalImageDestInfo.js +47 -0
- package/src/renderers/webgpu/descriptors/GPUCopyExternalImageSourceInfo.js +50 -0
- package/src/renderers/webgpu/descriptors/GPUExtent3D.js +51 -0
- package/src/renderers/webgpu/descriptors/GPUPipelineLayoutDescriptor.js +39 -0
- package/src/renderers/webgpu/descriptors/GPUQuerySetDescriptor.js +47 -0
- package/src/renderers/webgpu/descriptors/GPURenderBundleEncoderDescriptor.js +74 -0
- package/src/renderers/webgpu/descriptors/GPURenderPassColorAttachment.js +72 -0
- package/src/renderers/webgpu/descriptors/GPURenderPassDepthStencilAttachment.js +99 -0
- package/src/renderers/webgpu/descriptors/GPURenderPassDescriptor.js +72 -0
- package/src/renderers/webgpu/descriptors/GPURenderPassTimestampWrites.js +49 -0
- package/src/renderers/webgpu/descriptors/GPURenderPipelineDescriptor.js +130 -0
- package/src/renderers/webgpu/descriptors/GPUSamplerDescriptor.js +119 -0
- package/src/renderers/webgpu/descriptors/GPUShaderModuleDescriptor.js +46 -0
- package/src/renderers/webgpu/descriptors/GPUTexelCopyBufferInfo.js +57 -0
- package/src/renderers/webgpu/descriptors/GPUTexelCopyBufferLayout.js +48 -0
- package/src/renderers/webgpu/descriptors/GPUTexelCopyTextureInfo.js +61 -0
- package/src/renderers/webgpu/descriptors/GPUTextureDescriptor.js +99 -0
- package/src/renderers/webgpu/descriptors/GPUTextureViewDescriptor.js +108 -0
- package/src/renderers/webgpu/nodes/StandardNodeLibrary.js +0 -1
- package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +164 -7
- package/src/renderers/webgpu/nodes/WGSLNodeFunction.js +1 -1
- package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +98 -38
- package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +72 -68
- package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +164 -69
- package/src/renderers/webgpu/utils/WebGPUTexturePassUtils.js +158 -95
- package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +297 -141
- package/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +33 -18
- package/src/renderers/webgpu/utils/WebGPUUtils.js +38 -3
- package/src/textures/DepthTexture.js +1 -1
- package/src/textures/ExternalTexture.js +0 -3
- package/examples/fonts/LICENSE +0 -13
- package/examples/fonts/MPLUSRounded1c/MPLUSRounded1c-Regular.typeface.json.zip +0 -0
- package/examples/fonts/MPLUSRounded1c/OFL.txt +0 -91
- package/examples/fonts/README.md +0 -11
- package/examples/fonts/droid/NOTICE +0 -190
- package/examples/fonts/droid/README.txt +0 -18
- package/examples/fonts/droid/droid_sans_bold.typeface.json +0 -1
- package/examples/fonts/droid/droid_sans_mono_regular.typeface.json +0 -1
- package/examples/fonts/droid/droid_sans_regular.typeface.json +0 -1
- package/examples/fonts/droid/droid_serif_bold.typeface.json +0 -1
- package/examples/fonts/droid/droid_serif_regular.typeface.json +0 -1
- package/examples/fonts/gentilis_bold.typeface.json +0 -1
- package/examples/fonts/gentilis_regular.typeface.json +0 -1
- package/examples/fonts/helvetiker_bold.typeface.json +0 -1
- package/examples/fonts/helvetiker_regular.typeface.json +0 -1
- package/examples/fonts/optimer_bold.typeface.json +0 -1
- package/examples/fonts/optimer_regular.typeface.json +0 -1
- package/examples/fonts/ttf/README.md +0 -9
- package/examples/fonts/ttf/kenpixel.ttf +0 -0
- package/examples/jsm/inspector/extensions/extensions.json +0 -6
- package/examples/jsm/libs/ammo.wasm.js +0 -822
- package/examples/jsm/libs/ammo.wasm.wasm +0 -0
- package/examples/jsm/libs/draco/draco_encoder.js +0 -33
- package/examples/jsm/libs/draco/gltf/draco_encoder.js +0 -33
- package/examples/jsm/libs/lottie_canvas.module.js +0 -14849
- package/examples/jsm/libs/opentype.module.js +0 -14506
- package/examples/jsm/libs/rhino3dm/rhino3dm.js +0 -21
- package/examples/jsm/libs/rhino3dm/rhino3dm.module.js +0 -16
- package/examples/jsm/libs/rhino3dm/rhino3dm.wasm +0 -0
- package/examples/jsm/lighting/TiledLighting.js +0 -42
- package/examples/jsm/tsl/display/AnamorphicNode.js +0 -293
- package/examples/jsm/tsl/lighting/TiledLightsNode.js +0 -442
- package/src/nodes/accessors/BatchNode.js +0 -163
- package/src/nodes/accessors/InstanceNode.js +0 -349
- package/src/nodes/accessors/InstancedMeshNode.js +0 -50
- package/src/nodes/accessors/SkinningNode.js +0 -328
|
@@ -1,11 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Break, Continue, Fn, If, Loop, abs, bool, cross, distance, div, dot, float, getScreenPosition, getViewPosition, int, logarithmicDepthToViewZ, luminance, max, min, mix, mul, nodeObject, normalize, orthographicDepthToViewZ, passTexture, perspectiveDepthToViewZ, reference, reflect, sub, texture, trunc, uniform, uv, vec2, vec3, vec4, viewZToPerspectiveDepth } from 'three/tsl';
|
|
2
|
+
import { HalfFloatType, LinearFilter, LinearMipmapLinearFilter, Matrix4, NodeMaterial, NodeUpdateType, QuadMesh, RenderTarget, RendererUtils, TempNode, Vector2, Vector3 } from 'three/webgpu';
|
|
3
|
+
import { bindAnalyticNoise } from '../utils/RNoise.js';
|
|
4
|
+
import { ENV_RAY_LENGTH, getSpecularDominantFactor, ggxReflectionSample } from '../utils/SpecularHelpers.js';
|
|
3
5
|
import { boxBlur } from './boxBlur.js';
|
|
6
|
+
import ImportanceSampledEnvironment from './ImportanceSampledEnvironment.js';
|
|
4
7
|
|
|
5
8
|
const _quadMesh = /*@__PURE__*/ new QuadMesh();
|
|
6
9
|
const _size = /*@__PURE__*/ new Vector2();
|
|
7
10
|
let _rendererState;
|
|
8
11
|
|
|
12
|
+
// Maximum ray-march step count; `quality` (0..1) scales it to a fixed per-ray count.
|
|
13
|
+
const MAX_STEPS = 64;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} SSRNodeOptions
|
|
17
|
+
* @property {boolean} [stochastic=false] - When `false`, traces a single mirror reflection and softens roughness with a blur pass (first-generation SSR). When `true`, varies the reflection direction per pixel with stochastic GGX rays (second-generation SSR); higher quality on rough/glossy surfaces but noisier, so it expects a temporal/spatial denoiser downstream.
|
|
18
|
+
* @property {Node<float>} [metalnessNode=null] - Per-pixel metalness. Drives GGX reflection sampling and, with `reflectNonMetals=false`, the non-metal early-out.
|
|
19
|
+
* @property {Node<float>} [roughnessNode=null] - Per-pixel roughness. Drives GGX sampling and the blur mip selection.
|
|
20
|
+
* @property {boolean} [reflectNonMetals=false] - Only used when `stochastic=false`. When `false`, non-metallic surfaces are discarded for a noticeable performance gain; set `true` to also reflect dielectrics (e.g. marble, polished wood, plastic).
|
|
21
|
+
* @property {Texture} [environmentNode=null] - Equirectangular HDR environment map with CPU-side `image.data` (e.g. from RGBELoader). Not compatible with PMREM / `scene.environment` cubemaps.
|
|
22
|
+
* @property {boolean} [envImportanceSampling=false] - When `true`, precomputes env-luminance CDF tables and uses MIS for environment misses. Build-time only.
|
|
23
|
+
* @property {Node} [diffuseNode=null] - Scene diffuse / base color. Defaults to `vec3(1)` in the shader when omitted.
|
|
24
|
+
* @property {boolean} [binaryRefine=false] - Sub-step binary-search refinement of detected hits. Compile-time constant (baked into the shader at construction).
|
|
25
|
+
* @property {Camera} [camera=null] - Camera the scene is rendered with. Inferred from the color pass when omitted.
|
|
26
|
+
*/
|
|
27
|
+
|
|
9
28
|
/**
|
|
10
29
|
* Post processing node for computing screen space reflections (SSR).
|
|
11
30
|
*
|
|
@@ -28,14 +47,42 @@ class SSRNode extends TempNode {
|
|
|
28
47
|
* @param {Node<vec4>} colorNode - The node that represents the beauty pass.
|
|
29
48
|
* @param {Node<float>} depthNode - A node that represents the beauty pass's depth.
|
|
30
49
|
* @param {Node<vec3>} normalNode - A node that represents the beauty pass's normals.
|
|
31
|
-
* @param {
|
|
32
|
-
* @param {?Node<float>} [roughnessNode=null] - A node that represents the beauty pass's roughness.
|
|
33
|
-
* @param {?Camera} [camera=null] - The camera the scene is rendered with.
|
|
50
|
+
* @param {SSRNodeOptions} [options] - Optional inputs for material and environment data.
|
|
34
51
|
*/
|
|
35
|
-
constructor( colorNode, depthNode, normalNode,
|
|
52
|
+
constructor( colorNode, depthNode, normalNode, options = {} ) {
|
|
36
53
|
|
|
37
54
|
super( 'vec4' );
|
|
38
55
|
|
|
56
|
+
const {
|
|
57
|
+
stochastic = false,
|
|
58
|
+
metalnessNode = null,
|
|
59
|
+
roughnessNode = null,
|
|
60
|
+
reflectNonMetals = false,
|
|
61
|
+
environmentNode = null,
|
|
62
|
+
envImportanceSampling = false,
|
|
63
|
+
diffuseNode = null,
|
|
64
|
+
binaryRefine = false
|
|
65
|
+
} = options;
|
|
66
|
+
|
|
67
|
+
let camera = options.camera ?? null;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* When `true`, the reflection direction is varied per pixel with stochastic GGX rays
|
|
71
|
+
* (second-generation SSR). When `false`, a single mirror reflection is traced and
|
|
72
|
+
* roughness is softened with a blur pass (first-generation SSR).
|
|
73
|
+
*
|
|
74
|
+
* @type {boolean}
|
|
75
|
+
*/
|
|
76
|
+
this.stochastic = stochastic;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* When `true`, env-luminance CDF tables are built and MIS is used for environment misses.
|
|
80
|
+
* Fixed at construction time.
|
|
81
|
+
*
|
|
82
|
+
* @type {boolean}
|
|
83
|
+
*/
|
|
84
|
+
this.envImportanceSampling = envImportanceSampling;
|
|
85
|
+
|
|
39
86
|
/**
|
|
40
87
|
* The node that represents the beauty pass.
|
|
41
88
|
*
|
|
@@ -43,6 +90,14 @@ class SSRNode extends TempNode {
|
|
|
43
90
|
*/
|
|
44
91
|
this.colorNode = colorNode;
|
|
45
92
|
|
|
93
|
+
/**
|
|
94
|
+
* A node that represents the scene's diffuse color (typically the MRT `diffuseColor` attachment).
|
|
95
|
+
* When `null`, the shader uses `vec3(1)`.
|
|
96
|
+
*
|
|
97
|
+
* @type {?Node<vec4>}
|
|
98
|
+
*/
|
|
99
|
+
this.diffuseNode = diffuseNode !== null ? nodeObject( diffuseNode ) : null;
|
|
100
|
+
|
|
46
101
|
/**
|
|
47
102
|
* A node that represents the beauty pass's depth.
|
|
48
103
|
*
|
|
@@ -58,23 +113,32 @@ class SSRNode extends TempNode {
|
|
|
58
113
|
this.normalNode = normalNode;
|
|
59
114
|
|
|
60
115
|
/**
|
|
61
|
-
*
|
|
116
|
+
* Per-pixel metalness, used to drive the GGX reflection sampling and the non-metal
|
|
117
|
+
* early-out. When `null`, the shader treats surfaces as non-metallic.
|
|
62
118
|
*
|
|
63
|
-
* @type {Node<float>}
|
|
119
|
+
* @type {?Node<float>}
|
|
64
120
|
*/
|
|
65
121
|
this.metalnessNode = metalnessNode;
|
|
66
122
|
|
|
67
123
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* devices.
|
|
124
|
+
* Per-pixel roughness, used to drive the GGX reflection sampling and the blur mip
|
|
125
|
+
* selection. When `null`, the shader treats surfaces as fully smooth.
|
|
71
126
|
*
|
|
72
|
-
* @
|
|
73
|
-
* @type {Node<float>}
|
|
74
|
-
* @default false
|
|
127
|
+
* @type {?Node<float>}
|
|
75
128
|
*/
|
|
76
129
|
this.roughnessNode = roughnessNode;
|
|
77
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Only used when {@link SSRNode#stochastic} is `false`. When `false`, non-metallic
|
|
133
|
+
* surfaces are discarded for a noticeable performance gain; set `true` to also
|
|
134
|
+
* reflect dielectrics. Baked into the shader as a compile-time constant; assigning a
|
|
135
|
+
* new value recompiles the SSR material.
|
|
136
|
+
*
|
|
137
|
+
* @type {boolean}
|
|
138
|
+
* @default false
|
|
139
|
+
*/
|
|
140
|
+
this._reflectNonMetals = reflectNonMetals;
|
|
141
|
+
|
|
78
142
|
/**
|
|
79
143
|
* The resolution scale. Valid values are in the range
|
|
80
144
|
* `[0,1]`. `1` means best quality but also results in
|
|
@@ -111,11 +175,45 @@ class SSRNode extends TempNode {
|
|
|
111
175
|
this.thickness = uniform( 0.1 );
|
|
112
176
|
|
|
113
177
|
/**
|
|
114
|
-
*
|
|
178
|
+
* A multiplier for the overall reflection intensity. `1` leaves the
|
|
179
|
+
* reflections unchanged, lower values dim them and higher values boost them.
|
|
180
|
+
*
|
|
181
|
+
* @type {UniformNode<float>}
|
|
182
|
+
* @default 1
|
|
183
|
+
*/
|
|
184
|
+
this.intensity = uniform( 1 );
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Screen-edge fade width, in UV units. As a screen-space hit approaches a screen
|
|
188
|
+
* border, the reflection is faded over this distance — either toward the environment
|
|
189
|
+
* reflection ({@link SSRNode#screenEdgeFadeBlack} `false`) or to zero intensity
|
|
190
|
+
* (`true`). `0` disables it.
|
|
191
|
+
*
|
|
192
|
+
* @type {UniformNode<float>}
|
|
193
|
+
* @default 0.2
|
|
194
|
+
*/
|
|
195
|
+
this.screenEdgeFade = uniform( 0.2 );
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* When `true`, SSR fades to zero near screen borders instead of blending toward
|
|
199
|
+
* the environment map. Hits are faded by the reflection sample UV; misses are
|
|
200
|
+
* faded by the surface pixel UV.
|
|
201
|
+
*
|
|
202
|
+
* Baked into the shader as a compile-time constant so the unused fade branch is
|
|
203
|
+
* eliminated; assigning a new value recompiles the SSR material.
|
|
204
|
+
*
|
|
205
|
+
* @type {boolean}
|
|
206
|
+
* @default false
|
|
207
|
+
*/
|
|
208
|
+
this._screenEdgeFadeBlack = false;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Absolute env luminance cap. HDR env samples above this are scaled down (hue preserved).
|
|
115
212
|
*
|
|
116
213
|
* @type {UniformNode<float>}
|
|
214
|
+
* @default 10
|
|
117
215
|
*/
|
|
118
|
-
this.
|
|
216
|
+
this.maxLuminance = uniform( 10 );
|
|
119
217
|
|
|
120
218
|
/**
|
|
121
219
|
* This parameter controls how detailed the raymarching process works.
|
|
@@ -130,12 +228,72 @@ class SSRNode extends TempNode {
|
|
|
130
228
|
*/
|
|
131
229
|
this.quality = uniform( 0.5 );
|
|
132
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Mirror bias for the stochastic GGX sampling. Concentrates the reflected rays toward
|
|
233
|
+
* the lobe's narrow (near-mirror) core, trading a small amount of bias for less noise.
|
|
234
|
+
* `0` samples the full VNDF lobe; values toward `1` tighten the cone. Range `[0,1]`.
|
|
235
|
+
*
|
|
236
|
+
* @type {UniformNode<float>}
|
|
237
|
+
* @default 0.5
|
|
238
|
+
*/
|
|
239
|
+
this.mirrorBias = uniform( 0.5 );
|
|
240
|
+
|
|
133
241
|
/**
|
|
134
242
|
* The quality of the blur. Must be an integer in the range `[1,3]`.
|
|
135
243
|
*
|
|
136
|
-
*
|
|
244
|
+
* Baked into the blur shader as a compile-time constant so the `(size*2+1)²`
|
|
245
|
+
* sample loop unrolls; assigning a new value recompiles the blur material.
|
|
246
|
+
*
|
|
247
|
+
* @type {number}
|
|
248
|
+
* @default 2
|
|
249
|
+
*/
|
|
250
|
+
this._blurQuality = 2;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Enables sub-step binary-search refinement of a detected hit. When on, a coarse
|
|
254
|
+
* crossing is bisected toward the exact intersection (sharper hits, less step
|
|
255
|
+
* aliasing) at the cost of extra depth samples. Baked into the shader as a
|
|
256
|
+
* compile-time constant; assigning a new value rebuilds the SSR material.
|
|
257
|
+
*
|
|
258
|
+
* @type {boolean}
|
|
259
|
+
* @default false
|
|
137
260
|
*/
|
|
138
|
-
this.
|
|
261
|
+
this._binaryRefine = binaryRefine;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Non-linear step distribution exponent. `1` = uniform steps; `> 1` concentrates
|
|
265
|
+
* samples near the ray origin — where most short-range reflections are missed — and
|
|
266
|
+
* spaces them out toward maxDistance, as `s = (i / steps) ^ stepExponent`.
|
|
267
|
+
*
|
|
268
|
+
* Baked into the shader as a compile-time constant so `pow()` folds to a few
|
|
269
|
+
* multiplies; assigning a new value recompiles the SSR material. Only used by the
|
|
270
|
+
* stochastic reflection path.
|
|
271
|
+
*
|
|
272
|
+
* @type {number}
|
|
273
|
+
* @default 2
|
|
274
|
+
*/
|
|
275
|
+
this._stepExponent = 2;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* HDR environment map for screen-space misses.
|
|
279
|
+
*
|
|
280
|
+
* @type {?Texture}
|
|
281
|
+
*/
|
|
282
|
+
this.environmentNode = environmentNode;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* A node that represents the history texture for multi-bounce reflections.
|
|
286
|
+
*
|
|
287
|
+
* @type {?Texture}
|
|
288
|
+
*/
|
|
289
|
+
this.historyTexture = null;
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* A node that represents the velocity texture for reprojection.
|
|
293
|
+
*
|
|
294
|
+
* @type {?Node<vec2>}
|
|
295
|
+
*/
|
|
296
|
+
this.velocityTexture = null;
|
|
139
297
|
|
|
140
298
|
//
|
|
141
299
|
|
|
@@ -147,7 +305,7 @@ class SSRNode extends TempNode {
|
|
|
147
305
|
|
|
148
306
|
} else {
|
|
149
307
|
|
|
150
|
-
throw new Error( 'THREE.
|
|
308
|
+
throw new Error( 'THREE.SSRNode: No camera found. ssr() requires a camera.' );
|
|
151
309
|
|
|
152
310
|
}
|
|
153
311
|
|
|
@@ -200,21 +358,38 @@ class SSRNode extends TempNode {
|
|
|
200
358
|
*/
|
|
201
359
|
this._cameraFar = reference( 'far', 'float', camera );
|
|
202
360
|
|
|
361
|
+
this._cameraWorldMatrix = uniform( new Matrix4().copy( camera.matrixWorld ) );
|
|
362
|
+
this._cameraWorldPosition = uniform( new Vector3().copy( camera.position ) );
|
|
363
|
+
|
|
364
|
+
this._cameraViewMatrix = uniform( new Matrix4().copy( camera.matrixWorld ) );
|
|
365
|
+
this._cameraViewMatrixInverse = uniform( new Matrix4().copy( camera.matrixWorldInverse ) );
|
|
366
|
+
|
|
203
367
|
/**
|
|
204
|
-
*
|
|
368
|
+
* The resolution of the pass.
|
|
205
369
|
*
|
|
206
370
|
* @private
|
|
207
|
-
* @type {UniformNode<
|
|
371
|
+
* @type {UniformNode<vec2>}
|
|
208
372
|
*/
|
|
209
|
-
this.
|
|
373
|
+
this._resolution = uniform( new Vector2() );
|
|
374
|
+
|
|
375
|
+
this._noiseIndex = uniform( 0 );
|
|
210
376
|
|
|
211
377
|
/**
|
|
212
|
-
*
|
|
378
|
+
* CDF-backed environment sampler. Created when {@link setEnvMap} is called.
|
|
213
379
|
*
|
|
214
380
|
* @private
|
|
215
|
-
* @type {
|
|
381
|
+
* @type {?ImportanceSampledEnvironment}
|
|
216
382
|
*/
|
|
217
|
-
this.
|
|
383
|
+
this._importanceEnvironment = null;
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Intensity multiplier applied to environment-map reflections on screen-space
|
|
387
|
+
* misses and at screen edges. Defaults to π to match the former hardcoded multiplier.
|
|
388
|
+
*
|
|
389
|
+
* @type {UniformNode<float>}
|
|
390
|
+
* @default Math.PI
|
|
391
|
+
*/
|
|
392
|
+
this.environmentIntensity = uniform( Math.PI );
|
|
218
393
|
|
|
219
394
|
/**
|
|
220
395
|
* The render target the SSR is rendered into.
|
|
@@ -244,6 +419,16 @@ class SSRNode extends TempNode {
|
|
|
244
419
|
this._ssrMaterial = new NodeMaterial();
|
|
245
420
|
this._ssrMaterial.name = 'SSRNode.SSR';
|
|
246
421
|
|
|
422
|
+
/**
|
|
423
|
+
* The SSR fragment `Fn` and its shared context, captured in {@link SSRNode#setup}.
|
|
424
|
+
* Re-invoking the `Fn` produces a fresh node graph, which is how the baked
|
|
425
|
+
* compile-time constants are re-applied when they change (see {@link SSRNode#_buildSSRMaterial}).
|
|
426
|
+
*
|
|
427
|
+
* @private
|
|
428
|
+
*/
|
|
429
|
+
this._ssrFn = null;
|
|
430
|
+
this._sharedContext = null;
|
|
431
|
+
|
|
247
432
|
/**
|
|
248
433
|
* The blur material.
|
|
249
434
|
*
|
|
@@ -272,10 +457,10 @@ class SSRNode extends TempNode {
|
|
|
272
457
|
|
|
273
458
|
let blurredTextureNode = null;
|
|
274
459
|
|
|
275
|
-
if ( this.roughnessNode !== null ) {
|
|
460
|
+
if ( this.stochastic === false && this.roughnessNode !== null ) {
|
|
276
461
|
|
|
277
462
|
const mips = this._blurRenderTarget.texture.mipmaps.length - 1;
|
|
278
|
-
const r =
|
|
463
|
+
const r = this.roughnessNode;
|
|
279
464
|
const lod = r.mul( r ).mul( mips ).clamp( 0, mips );
|
|
280
465
|
|
|
281
466
|
blurredTextureNode = passTexture( this, this._blurRenderTarget.texture ).level( lod );
|
|
@@ -290,6 +475,159 @@ class SSRNode extends TempNode {
|
|
|
290
475
|
*/
|
|
291
476
|
this._blurredTextureNode = blurredTextureNode;
|
|
292
477
|
|
|
478
|
+
if ( environmentNode !== null && environmentNode.isTexture === true ) {
|
|
479
|
+
|
|
480
|
+
this.setEnvMap( environmentNode );
|
|
481
|
+
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Non-linear step distribution exponent (compile-time constant). See the backing
|
|
488
|
+
* field for details. Assigning a new value recompiles the SSR material.
|
|
489
|
+
*
|
|
490
|
+
* @type {number}
|
|
491
|
+
*/
|
|
492
|
+
get stepExponent() {
|
|
493
|
+
|
|
494
|
+
return this._stepExponent;
|
|
495
|
+
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
set stepExponent( value ) {
|
|
499
|
+
|
|
500
|
+
if ( value !== this._stepExponent ) {
|
|
501
|
+
|
|
502
|
+
this._stepExponent = value;
|
|
503
|
+
this._buildSSRMaterial();
|
|
504
|
+
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Blur kernel size (compile-time constant). Assigning a new value recompiles the
|
|
511
|
+
* blur material.
|
|
512
|
+
*
|
|
513
|
+
* @type {number}
|
|
514
|
+
*/
|
|
515
|
+
get blurQuality() {
|
|
516
|
+
|
|
517
|
+
return this._blurQuality;
|
|
518
|
+
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
set blurQuality( value ) {
|
|
522
|
+
|
|
523
|
+
if ( value !== this._blurQuality ) {
|
|
524
|
+
|
|
525
|
+
this._blurQuality = value;
|
|
526
|
+
|
|
527
|
+
// The size is baked into the boxBlur node tree, so rebuild it (recompiles the material).
|
|
528
|
+
if ( this.stochastic === false ) this._buildBlurMaterial();
|
|
529
|
+
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Builds (or rebuilds) the blur material's node graph, baking the current
|
|
536
|
+
* {@link SSRNode#blurQuality} as the kernel size so the sample loop unrolls.
|
|
537
|
+
*
|
|
538
|
+
* @private
|
|
539
|
+
*/
|
|
540
|
+
_buildBlurMaterial() {
|
|
541
|
+
|
|
542
|
+
this._blurMaterial.fragmentNode = boxBlur( texture( this._ssrRenderTarget.texture ), { size: this._blurQuality, separation: this._blurSpread } );
|
|
543
|
+
this._blurMaterial.needsUpdate = true;
|
|
544
|
+
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Whether SSR fades to black near screen borders (compile-time constant). Assigning
|
|
549
|
+
* a new value recompiles the SSR material.
|
|
550
|
+
*
|
|
551
|
+
* @type {boolean}
|
|
552
|
+
*/
|
|
553
|
+
get screenEdgeFadeBlack() {
|
|
554
|
+
|
|
555
|
+
return this._screenEdgeFadeBlack;
|
|
556
|
+
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
set screenEdgeFadeBlack( value ) {
|
|
560
|
+
|
|
561
|
+
if ( value !== this._screenEdgeFadeBlack ) {
|
|
562
|
+
|
|
563
|
+
this._screenEdgeFadeBlack = value;
|
|
564
|
+
this._buildSSRMaterial();
|
|
565
|
+
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Whether sub-step binary-search hit refinement is enabled (compile-time constant).
|
|
572
|
+
* Assigning a new value rebuilds the SSR material.
|
|
573
|
+
*
|
|
574
|
+
* @type {boolean}
|
|
575
|
+
*/
|
|
576
|
+
get binaryRefine() {
|
|
577
|
+
|
|
578
|
+
return this._binaryRefine;
|
|
579
|
+
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
set binaryRefine( value ) {
|
|
583
|
+
|
|
584
|
+
if ( value !== this._binaryRefine ) {
|
|
585
|
+
|
|
586
|
+
this._binaryRefine = value;
|
|
587
|
+
this._buildSSRMaterial();
|
|
588
|
+
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Whether dielectrics are reflected in the non-stochastic path (compile-time constant).
|
|
595
|
+
* Assigning a new value rebuilds the SSR material.
|
|
596
|
+
*
|
|
597
|
+
* @type {boolean}
|
|
598
|
+
*/
|
|
599
|
+
get reflectNonMetals() {
|
|
600
|
+
|
|
601
|
+
return this._reflectNonMetals;
|
|
602
|
+
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
set reflectNonMetals( value ) {
|
|
606
|
+
|
|
607
|
+
if ( value !== this._reflectNonMetals ) {
|
|
608
|
+
|
|
609
|
+
this._reflectNonMetals = value;
|
|
610
|
+
this._buildSSRMaterial();
|
|
611
|
+
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Rebuilds the SSR material's node graph by re-invoking the fragment `Fn`, which
|
|
618
|
+
* re-bakes the compile-time constants ({@link SSRNode#binaryRefine},
|
|
619
|
+
* {@link SSRNode#stepExponent}, {@link SSRNode#screenEdgeFadeBlack}) at their current
|
|
620
|
+
* values. A no-op until {@link SSRNode#setup} has captured the `Fn`.
|
|
621
|
+
*
|
|
622
|
+
* @private
|
|
623
|
+
*/
|
|
624
|
+
_buildSSRMaterial() {
|
|
625
|
+
|
|
626
|
+
if ( this._ssrFn === null ) return;
|
|
627
|
+
|
|
628
|
+
this._ssrMaterial.fragmentNode = this._ssrFn().context( this._sharedContext );
|
|
629
|
+
this._ssrMaterial.needsUpdate = true;
|
|
630
|
+
|
|
293
631
|
}
|
|
294
632
|
|
|
295
633
|
/**
|
|
@@ -299,7 +637,7 @@ class SSRNode extends TempNode {
|
|
|
299
637
|
*/
|
|
300
638
|
getTextureNode() {
|
|
301
639
|
|
|
302
|
-
return this.roughnessNode !== null ? this._blurredTextureNode : this._textureNode;
|
|
640
|
+
return ( this.stochastic === false && this.roughnessNode !== null ) ? this._blurredTextureNode : this._textureNode;
|
|
303
641
|
|
|
304
642
|
}
|
|
305
643
|
|
|
@@ -320,6 +658,81 @@ class SSRNode extends TempNode {
|
|
|
320
658
|
|
|
321
659
|
}
|
|
322
660
|
|
|
661
|
+
/**
|
|
662
|
+
* Wires the feedback inputs for multi-bounce reflections: the previous frame's
|
|
663
|
+
* denoised result (`history`) and the velocity buffer used to reproject it
|
|
664
|
+
* (`velocity`). `history` accepts the producing node (e.g. a
|
|
665
|
+
* {@link RecurrentDenoiseNode}) — its output render target is used — or a raw
|
|
666
|
+
* texture. Pass `null` for both to disable multi-bounce.
|
|
667
|
+
*
|
|
668
|
+
* @param {Texture} history
|
|
669
|
+
* @param {Node<vec2>} velocity
|
|
670
|
+
*/
|
|
671
|
+
setHistory( history, velocity ) {
|
|
672
|
+
|
|
673
|
+
this.historyTexture = ( history && typeof history.getRenderTarget === 'function' )
|
|
674
|
+
? history.getRenderTarget().texture
|
|
675
|
+
: history;
|
|
676
|
+
this.velocityTexture = velocity;
|
|
677
|
+
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Sets the environment map for importance-sampled env lighting when
|
|
682
|
+
* screen-space rays miss. Call this whenever the scene's env map changes.
|
|
683
|
+
*
|
|
684
|
+
* Uses {@link ImportanceSampledEnvironment} (CDF + MIS adapted from
|
|
685
|
+
* [three-gpu-pathtracer](https://github.com/gkjohnson/three-gpu-pathtracer)).
|
|
686
|
+
*
|
|
687
|
+
* @param {Texture|null} hdr - The equirectangular HDR environment map, or null to disable.
|
|
688
|
+
* @see {@link https://github.com/gkjohnson/three-gpu-pathtracer}
|
|
689
|
+
*/
|
|
690
|
+
setEnvMap( hdr ) {
|
|
691
|
+
|
|
692
|
+
if ( hdr === null ) {
|
|
693
|
+
|
|
694
|
+
if ( this._importanceEnvironment !== null ) {
|
|
695
|
+
|
|
696
|
+
this._importanceEnvironment.clear();
|
|
697
|
+
this._importanceEnvironment = null;
|
|
698
|
+
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
this._buildSSRMaterial();
|
|
702
|
+
return;
|
|
703
|
+
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if ( hdr.image === undefined || hdr.image.data === undefined ) {
|
|
707
|
+
|
|
708
|
+
console.warn( 'SSRNode: `environmentNode` / `setEnvMap()` expects an equirectangular HDR texture with CPU-side image data (e.g. RGBELoader). PMREM cubemaps and `scene.environment` are not supported.' );
|
|
709
|
+
return;
|
|
710
|
+
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if ( this._importanceEnvironment === null ) {
|
|
714
|
+
|
|
715
|
+
this._importanceEnvironment = new ImportanceSampledEnvironment( this.envImportanceSampling );
|
|
716
|
+
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
this._importanceEnvironment.updateFrom( hdr );
|
|
720
|
+
this._buildSSRMaterial();
|
|
721
|
+
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Intensity multiplier for the importance-sampled env contribution.
|
|
726
|
+
* Only available after {@link setEnvMap} has been called.
|
|
727
|
+
*
|
|
728
|
+
* @type {?UniformNode<float>}
|
|
729
|
+
*/
|
|
730
|
+
get envMapIntensity() {
|
|
731
|
+
|
|
732
|
+
return this._importanceEnvironment !== null ? this._importanceEnvironment.intensity : null;
|
|
733
|
+
|
|
734
|
+
}
|
|
735
|
+
|
|
323
736
|
/**
|
|
324
737
|
* This method is used to render the effect once per frame.
|
|
325
738
|
*
|
|
@@ -329,6 +742,9 @@ class SSRNode extends TempNode {
|
|
|
329
742
|
|
|
330
743
|
const { renderer } = frame;
|
|
331
744
|
|
|
745
|
+
this._cameraWorldMatrix.value.copy( this.camera.matrixWorld );
|
|
746
|
+
this._cameraWorldPosition.value.copy( this.camera.position );
|
|
747
|
+
|
|
332
748
|
_rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
|
|
333
749
|
|
|
334
750
|
const ssrRenderTarget = this._ssrRenderTarget;
|
|
@@ -340,6 +756,9 @@ class SSRNode extends TempNode {
|
|
|
340
756
|
|
|
341
757
|
this.setSize( size.width, size.height );
|
|
342
758
|
|
|
759
|
+
// Advance the noise index once per frame (matches SSGI / Denoise).
|
|
760
|
+
this._noiseIndex.value = ( this._noiseIndex.value + 1 ) % 0x7fffffff;
|
|
761
|
+
|
|
343
762
|
// clear
|
|
344
763
|
|
|
345
764
|
renderer.setMRT( null );
|
|
@@ -353,7 +772,7 @@ class SSRNode extends TempNode {
|
|
|
353
772
|
|
|
354
773
|
// blur (optional)
|
|
355
774
|
|
|
356
|
-
if ( this.roughnessNode !== null ) {
|
|
775
|
+
if ( this.stochastic === false && this.roughnessNode !== null ) {
|
|
357
776
|
|
|
358
777
|
// blur mips but leave the base mip unblurred
|
|
359
778
|
|
|
@@ -386,7 +805,7 @@ class SSRNode extends TempNode {
|
|
|
386
805
|
|
|
387
806
|
const uvNode = uv();
|
|
388
807
|
|
|
389
|
-
const pointToLineDistance = Fn( ( [ point, linePointA, linePointB ] )=> {
|
|
808
|
+
const pointToLineDistance = Fn( ( [ point, linePointA, linePointB ] ) => {
|
|
390
809
|
|
|
391
810
|
// https://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
|
|
392
811
|
|
|
@@ -394,13 +813,13 @@ class SSRNode extends TempNode {
|
|
|
394
813
|
|
|
395
814
|
} );
|
|
396
815
|
|
|
397
|
-
const pointPlaneDistance = Fn( ( [ point, planePoint, planeNormal ] )=> {
|
|
816
|
+
const pointPlaneDistance = Fn( ( [ point, planePoint, planeNormal ] ) => {
|
|
398
817
|
|
|
399
818
|
// https://mathworld.wolfram.com/Point-PlaneDistance.html
|
|
400
819
|
// https://en.wikipedia.org/wiki/Plane_(geometry)
|
|
401
820
|
// http://paulbourke.net/geometry/pointlineplane/
|
|
402
821
|
|
|
403
|
-
// planeNormal is already normalized, so denominator is 1
|
|
822
|
+
// planeNormal is already normalized, so the denominator is 1.
|
|
404
823
|
const d = mul( planeNormal.x, planePoint.x ).add( mul( planeNormal.y, planePoint.y ) ).add( mul( planeNormal.z, planePoint.z ) ).negate().toVar();
|
|
405
824
|
const distance = mul( planeNormal.x, point.x ).add( mul( planeNormal.y, point.y ) ).add( mul( planeNormal.z, point.z ) ).add( d );
|
|
406
825
|
return distance;
|
|
@@ -441,176 +860,438 @@ class SSRNode extends TempNode {
|
|
|
441
860
|
|
|
442
861
|
};
|
|
443
862
|
|
|
863
|
+
const sampleMarchNoise = this.stochastic === true ? bindAnalyticNoise( this._resolution, 47 ) : null;
|
|
864
|
+
|
|
865
|
+
const computeScreenBorderFactor = Fn( ( [ uvCoord, borderWidth ] ) => {
|
|
866
|
+
|
|
867
|
+
const border = borderWidth.max( 1e-4 );
|
|
868
|
+
|
|
869
|
+
// Distance to the nearest screen edge — uniform falloff at corners.
|
|
870
|
+
const edgeDist = min(
|
|
871
|
+
min( uvCoord.x, float( 1 ).sub( uvCoord.x ) ),
|
|
872
|
+
min( uvCoord.y, float( 1 ).sub( uvCoord.y ) )
|
|
873
|
+
);
|
|
874
|
+
|
|
875
|
+
// Two smoothsteps for a softer ease-in-out than a single ramp.
|
|
876
|
+
const t = edgeDist.smoothstep( 0, border );
|
|
877
|
+
|
|
878
|
+
return t.smoothstep( 0, 1 ).pow( 0.125 );
|
|
879
|
+
|
|
880
|
+
} ).setLayout( {
|
|
881
|
+
name: 'computeScreenBorderFactor',
|
|
882
|
+
type: 'float',
|
|
883
|
+
inputs: [
|
|
884
|
+
{ name: 'uvCoord', type: 'vec2' },
|
|
885
|
+
{ name: 'borderWidth', type: 'float' }
|
|
886
|
+
]
|
|
887
|
+
} );
|
|
888
|
+
|
|
444
889
|
const ssr = Fn( () => {
|
|
445
890
|
|
|
446
|
-
const
|
|
891
|
+
const noise = this.stochastic === true ? sampleMarchNoise( uvNode, this._noiseIndex ) : null;
|
|
892
|
+
const uvPos = uvNode.toVar();
|
|
893
|
+
|
|
894
|
+
const depth = sampleDepth( uvPos ).toVar();
|
|
447
895
|
|
|
448
|
-
//
|
|
449
|
-
|
|
896
|
+
// Skip background pixels (cleared far-plane depth); the target is cleared each frame.
|
|
897
|
+
depth.greaterThanEqual( 1.0 ).discard();
|
|
450
898
|
|
|
451
|
-
|
|
452
|
-
const
|
|
453
|
-
const viewPosition = getViewPosition( uvNode, depth, this._cameraProjectionMatrixInverse ).toVar();
|
|
899
|
+
const viewPosition = getViewPosition( uvPos, depth, this._cameraProjectionMatrixInverse ).toVar();
|
|
900
|
+
const worldPosition = this._cameraWorldMatrix.mul( vec4( viewPosition, 1.0 ) ).xyz.toVar();
|
|
454
901
|
const viewNormal = this.normalNode.rgb.normalize().toVar();
|
|
455
902
|
|
|
456
|
-
// compute the direction from the position in view space to the camera
|
|
457
903
|
const viewIncidentDir = ( ( this.camera.isPerspectiveCamera ) ? normalize( viewPosition ) : vec3( 0, 0, - 1 ) ).toVar();
|
|
458
904
|
|
|
459
|
-
//
|
|
460
|
-
|
|
905
|
+
// The node system samples the metalness/roughness textures at the current uv,
|
|
906
|
+
// so no explicit sample() is needed here.
|
|
907
|
+
const metalness = float( this.metalnessNode );
|
|
908
|
+
|
|
909
|
+
if ( this.stochastic === false && this._reflectNonMetals === false ) {
|
|
910
|
+
|
|
911
|
+
metalness.lessThanEqual( 0.0 ).discard();
|
|
912
|
+
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
const roughness = float( this.roughnessNode );
|
|
916
|
+
const glossiness = min( roughness.div( 0.25 ), 1 ).oneMinus();
|
|
917
|
+
// Only the fade-to-black miss path reads this, and that path is baked out otherwise.
|
|
918
|
+
const surfaceBorderFactor = this.screenEdgeFadeBlack ? computeScreenBorderFactor( uvPos, this.screenEdgeFade ) : null;
|
|
919
|
+
const hitBorderWidth = this.screenEdgeFade.mul( glossiness );
|
|
920
|
+
|
|
921
|
+
const V = viewIncidentDir.negate().normalize().toVar();
|
|
922
|
+
|
|
923
|
+
let viewReflectDir, finalSampleWeight, specDominantFactor;
|
|
924
|
+
const albedo = vec3( 1 ).toVar();
|
|
925
|
+
let sampleEnvReflection = null;
|
|
926
|
+
|
|
927
|
+
if ( this.stochastic === false ) {
|
|
928
|
+
|
|
929
|
+
viewReflectDir = reflect( viewIncidentDir, viewNormal ).normalize().toVar();
|
|
930
|
+
finalSampleWeight = vec3( metalness );
|
|
931
|
+
specDominantFactor = float( 1 );
|
|
932
|
+
|
|
933
|
+
} else {
|
|
934
|
+
|
|
935
|
+
const Xi = noise.toVar();
|
|
936
|
+
// Mirror-bias: pull `Xi.y` toward the cap top to tighten the GGX lobe and cut mid-roughness
|
|
937
|
+
// noise. Unbiased — bounded VNDF keeps brdf·cos/pdf ~constant (EA, "Stochastic SSR").
|
|
938
|
+
Xi.y.assign( mix( Xi.y, 0.0, this.mirrorBias.mul( Xi.w.sqrt() ) ) );
|
|
939
|
+
|
|
940
|
+
albedo.assign( ( this.diffuseNode !== null ? this.diffuseNode.sample( uvPos ).rgb : vec3( 1 ) ) );
|
|
941
|
+
const ggxSample = ggxReflectionSample( viewNormal, V, roughness, metalness, albedo, Xi ).toVar();
|
|
942
|
+
|
|
943
|
+
// Sometimes the GGX sample is facing away from the surface, so we need to re-sample.
|
|
944
|
+
If( ggxSample.get( 'reflectDir' ).dot( viewNormal ).lessThan( 0 ), () => {
|
|
945
|
+
|
|
946
|
+
ggxSample.assign( ggxReflectionSample( viewNormal, V, roughness, metalness, albedo, Xi.add( Xi.mul( 7 ) ).fract() ) );
|
|
947
|
+
|
|
948
|
+
} );
|
|
949
|
+
|
|
950
|
+
viewReflectDir = ggxSample.get( 'reflectDir' ).toVar();
|
|
951
|
+
finalSampleWeight = ggxSample.get( 'sampleWeight' ).toVar();
|
|
952
|
+
specDominantFactor = getSpecularDominantFactor( ggxSample.get( 'NdotV' ), roughness ).toVar();
|
|
953
|
+
|
|
954
|
+
sampleEnvReflection = () => {
|
|
955
|
+
|
|
956
|
+
const envColor = vec3( 0 ).toVar();
|
|
957
|
+
|
|
958
|
+
if ( this.envImportanceSampling ) {
|
|
959
|
+
|
|
960
|
+
const Xi2 = bindAnalyticNoise( this._resolution, 59 )( uvPos, this._noiseIndex );
|
|
961
|
+
|
|
962
|
+
envColor.assign( this._importanceEnvironment.sampleEnvironmentMIS( {
|
|
963
|
+
cameraWorldMatrix: this._cameraWorldMatrix,
|
|
964
|
+
viewReflectDir,
|
|
965
|
+
N: viewNormal,
|
|
966
|
+
V,
|
|
967
|
+
alpha: ggxSample.get( 'alpha' ),
|
|
968
|
+
f0: ggxSample.get( 'f0' ),
|
|
969
|
+
Xi2
|
|
970
|
+
} ) );
|
|
971
|
+
|
|
972
|
+
} else {
|
|
973
|
+
|
|
974
|
+
envColor.assign( this._importanceEnvironment.sampleEnvironmentBRDF( {
|
|
975
|
+
cameraWorldMatrix: this._cameraWorldMatrix,
|
|
976
|
+
viewReflectDir,
|
|
977
|
+
N: viewNormal,
|
|
978
|
+
V,
|
|
979
|
+
alpha: ggxSample.get( 'alpha' ),
|
|
980
|
+
f0: ggxSample.get( 'f0' )
|
|
981
|
+
} ) );
|
|
982
|
+
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
return envColor;
|
|
986
|
+
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// Multi-bounce: fold in the previous frame's reflection at the hit point, reprojected by its
|
|
992
|
+
// own motion. The (1 - history.a) decay damps the feedback. No-op until both textures are set.
|
|
993
|
+
const reprojectHitPointHistory = ( uvHit, color ) => {
|
|
994
|
+
|
|
995
|
+
if ( ! ( this.historyTexture && this.velocityTexture ) ) return color;
|
|
996
|
+
|
|
997
|
+
const velocity = this.velocityTexture.sample( uvHit ).xy;
|
|
998
|
+
const historyUV = uvHit.sub( velocity );
|
|
999
|
+
const historyBounce = texture( this.historyTexture, historyUV ).toVar();
|
|
1000
|
+
const sampleDecay = historyBounce.a.oneMinus();
|
|
1001
|
+
|
|
1002
|
+
return color.add( historyBounce.rgb.mul( sampleDecay ) );
|
|
1003
|
+
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
// Fades a screen-space hit near the screen borders, using the hit sample UV (where the
|
|
1007
|
+
// screen-space data was read). `screenEdgeFadeBlack` is baked, so the two modes branch in
|
|
1008
|
+
// JS: fade the reflection to black, or blend it toward the environment reflection.
|
|
1009
|
+
const applyHitEdgeFade = ( reflectColor, uvS, hitBorderWidth ) => {
|
|
1010
|
+
|
|
1011
|
+
if ( this.screenEdgeFadeBlack ) {
|
|
1012
|
+
|
|
1013
|
+
const hitBorderFactor = computeScreenBorderFactor( uvS, this.screenEdgeFade );
|
|
1014
|
+
reflectColor.rgb.mulAssign( hitBorderFactor );
|
|
1015
|
+
|
|
1016
|
+
} else {
|
|
1017
|
+
|
|
1018
|
+
const hitBorderFactor = computeScreenBorderFactor( uvS, hitBorderWidth );
|
|
1019
|
+
|
|
1020
|
+
If( hitBorderFactor.lessThan( 1 ), () => {
|
|
1021
|
+
|
|
1022
|
+
reflectColor.rgb.assign( mix( sampleEnvReflection().mul( this.environmentIntensity ), reflectColor.rgb, hitBorderFactor ) );
|
|
1023
|
+
|
|
1024
|
+
} );
|
|
1025
|
+
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
};
|
|
461
1029
|
|
|
462
|
-
// adapt maximum distance to the local geometry (see https://www.mathsisfun.com/algebra/vectors-dot-product.html)
|
|
463
1030
|
const maxReflectRayLen = this.maxDistance.div( dot( viewIncidentDir.negate(), viewNormal ) ).toVar();
|
|
464
1031
|
|
|
465
|
-
// compute the maximum point of the reflection ray in view space
|
|
466
1032
|
const d1viewPosition = viewPosition.add( viewReflectDir.mul( maxReflectRayLen ) ).toVar();
|
|
467
1033
|
|
|
468
|
-
//
|
|
469
|
-
|
|
1034
|
+
// Camera type is fixed at build time, so guard the near-plane clamp with a JS branch
|
|
1035
|
+
// rather than a runtime uniform (the orthographic case compiles it out entirely).
|
|
1036
|
+
if ( this.camera.isPerspectiveCamera ) {
|
|
470
1037
|
|
|
471
|
-
|
|
472
|
-
// this prevents artifacts during the ray marching process
|
|
473
|
-
const t = sub( this._cameraNear.negate(), viewPosition.z ).div( viewReflectDir.z );
|
|
474
|
-
d1viewPosition.assign( viewPosition.add( viewReflectDir.mul( t ) ) );
|
|
1038
|
+
If( d1viewPosition.z.greaterThan( this._cameraNear.negate() ), () => {
|
|
475
1039
|
|
|
476
|
-
|
|
1040
|
+
const t = sub( this._cameraNear.negate(), viewPosition.z ).div( viewReflectDir.z );
|
|
1041
|
+
d1viewPosition.assign( viewPosition.add( viewReflectDir.mul( t ) ) );
|
|
477
1042
|
|
|
478
|
-
|
|
479
|
-
const d0 = screenCoordinate.xy.toVar();
|
|
480
|
-
const d1 = getScreenPosition( d1viewPosition, this._cameraProjectionMatrix ).mul( this._resolution ).toVar();
|
|
1043
|
+
} );
|
|
481
1044
|
|
|
482
|
-
|
|
1045
|
+
}
|
|
483
1046
|
|
|
484
|
-
|
|
485
|
-
const
|
|
1047
|
+
const d0 = uvPos.mul( this._resolution ).xy.toVar();
|
|
1048
|
+
const d1 = getScreenPosition( d1viewPosition, this._cameraProjectionMatrix ).mul( this._resolution ).toVar();
|
|
486
1049
|
|
|
487
|
-
// offset in x and y direction
|
|
488
1050
|
const xLen = d1.x.sub( d0.x ).toVar();
|
|
489
1051
|
const yLen = d1.y.sub( d0.y ).toVar();
|
|
490
1052
|
|
|
491
|
-
//
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
1053
|
+
// dominant-axis ray length in texels (used for the per-step floor below)
|
|
1054
|
+
const rayLen = max( xLen.abs(), yLen.abs() ).max( 1 ).toVar();
|
|
1055
|
+
|
|
1056
|
+
// Blur traces a single mirror ray, so spend steps in proportion to the ray's screen-space
|
|
1057
|
+
// length (cheap for the short rays that dominate). Scatter needs a fixed, bounded count for
|
|
1058
|
+
// coherent stochastic sampling; each step then spans the whole ray as rayVec / totalStep.
|
|
1059
|
+
const totalStep = int( this.stochastic === false
|
|
1060
|
+
? trunc( max( abs( xLen ), abs( yLen ) ).mul( this.quality.clamp() ) ).max( int( 1 ) ).toConst()
|
|
1061
|
+
: this.quality.clamp().mul( MAX_STEPS ).max( float( 1 ) ) ).toConst();
|
|
495
1062
|
|
|
496
|
-
// step sizes in the x and y directions
|
|
497
1063
|
const xSpan = xLen.div( totalStep ).toVar();
|
|
498
1064
|
const ySpan = yLen.div( totalStep ).toVar();
|
|
499
1065
|
|
|
500
|
-
const
|
|
1066
|
+
const stepVec = vec2( xSpan, ySpan ).toVar();
|
|
1067
|
+
const invResolution = vec2( float( 1 ), float( 1 ) ).div( this._resolution ).toVar();
|
|
1068
|
+
const uvPixelStepX = vec2( invResolution.x, float( 0 ) ).toVar();
|
|
501
1069
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
1070
|
+
const output = vec4( 0 ).toVar();
|
|
1071
|
+
const hit = float( 0 ).toVar();
|
|
1072
|
+
|
|
1073
|
+
// Reflected-ray view-space Z at ray parameter s ∈ [0,1] (linear in 1/z for perspective),
|
|
1074
|
+
// hoisted so the march and refinement evaluate it identically.
|
|
1075
|
+
const recipVPZ = float( 1 ).div( viewPosition.z ).toConst();
|
|
1076
|
+
const recipD1VPZ = float( 1 ).div( d1viewPosition.z ).toConst();
|
|
1077
|
+
|
|
1078
|
+
// Camera type is known at build time, so branch at compile time rather than via a runtime select.
|
|
1079
|
+
const reflectRayZAt = this.camera.isPerspectiveCamera
|
|
1080
|
+
? ( sVal ) => float( 1 ).div( recipVPZ.add( sVal.mul( recipD1VPZ.sub( recipVPZ ) ) ) )
|
|
1081
|
+
: ( sVal ) => viewPosition.z.add( sVal.mul( d1viewPosition.z.sub( viewPosition.z ) ) );
|
|
1082
|
+
|
|
1083
|
+
// Screen-space position along the ray for a given s ∈ [0,1].
|
|
1084
|
+
const screenPosAt = ( sVal ) => d0.add( stepVec.mul( sVal.mul( totalStep ) ) );
|
|
1085
|
+
|
|
1086
|
+
// Ray parameter s ∈ [0,1] for step `idx`. Blur marches uniformly (matching the original loop:
|
|
1087
|
+
// one ~texel step per iteration). Scatter uses an exponential remap `(idx/steps)^stepExponent`
|
|
1088
|
+
// that concentrates samples near the origin, floored to ≥1 texel/step; `jitter` dissolves banding.
|
|
1089
|
+
const sampleFraction = this.stochastic === false
|
|
1090
|
+
? ( idx ) => idx.div( totalStep )
|
|
1091
|
+
: ( idx ) => max(
|
|
1092
|
+
idx.add( noise.z.sub( 0.5 ) ).div( totalStep ).pow( this.stepExponent ),
|
|
1093
|
+
idx.div( rayLen )
|
|
1094
|
+
);
|
|
1095
|
+
|
|
1096
|
+
// Carry the hit out of the loop so refinement runs after the march, not nested inside it (a
|
|
1097
|
+
// loop-inside-a-loop tripped shader-compiler bugs on some drivers). hitSLo/hitSHi bracket s.
|
|
1098
|
+
const foundHit = bool( false ).toVar();
|
|
1099
|
+
const hitSLo = float( 0 ).toVar();
|
|
1100
|
+
const hitSHi = float( 0 ).toVar();
|
|
1101
|
+
// Carry the coarse hit's UV/depth to skip a redundant fetch when refinement is off.
|
|
1102
|
+
const hitUvS = vec2( 0 ).toVar();
|
|
1103
|
+
const hitD = float( 0 ).toVar();
|
|
1104
|
+
|
|
1105
|
+
// March from d0 toward d1, looking for an intersection with the depth buffer.
|
|
1106
|
+
Loop( { start: int( 1 ), end: totalStep }, ( { i } ) => {
|
|
1107
|
+
|
|
1108
|
+
// Exponentially-distributed ray parameter, shared by the sample position and ray depth.
|
|
1109
|
+
const s = sampleFraction( float( i ) ).toVar();
|
|
1110
|
+
|
|
1111
|
+
const xy = screenPosAt( s ).toVar();
|
|
509
1112
|
|
|
510
|
-
// stop processing if the new position lies outside of the screen
|
|
511
1113
|
If( xy.x.lessThan( 0 ).or( xy.x.greaterThan( this._resolution.x ) ).or( xy.y.lessThan( 0 ) ).or( xy.y.greaterThan( this._resolution.y ) ), () => {
|
|
512
1114
|
|
|
513
1115
|
Break();
|
|
514
1116
|
|
|
515
1117
|
} );
|
|
516
1118
|
|
|
517
|
-
|
|
518
|
-
const
|
|
519
|
-
const d = sampleDepth( uvNode ).toVar();
|
|
1119
|
+
const uvS = xy.mul( invResolution ).toVar();
|
|
1120
|
+
const d = sampleDepth( uvS ).toVar();
|
|
520
1121
|
const vZ = getViewZ( d ).toVar();
|
|
521
1122
|
|
|
522
|
-
const viewReflectRayZ =
|
|
1123
|
+
const viewReflectRayZ = reflectRayZAt( s ).toVar();
|
|
523
1124
|
|
|
524
|
-
|
|
525
|
-
|
|
1125
|
+
If( viewReflectRayZ.lessThanEqual( vZ ), () => {
|
|
1126
|
+
|
|
1127
|
+
// Depth crossing: ray went behind the depth buffer. Gate by thickness before stopping
|
|
1128
|
+
// so an occluder gap doesn't end the march prematurely.
|
|
1129
|
+
const vP = getViewPosition( uvS, d, this._cameraProjectionMatrixInverse ).toVar();
|
|
1130
|
+
const away = pointToLineDistance( vP, viewPosition, d1viewPosition ).toVar();
|
|
1131
|
+
|
|
1132
|
+
const uvNeighbor = uvS.add( uvPixelStepX ).toVar();
|
|
1133
|
+
const vPNeighbor = getViewPosition( uvNeighbor, d, this._cameraProjectionMatrixInverse ).toVar();
|
|
1134
|
+
const minThickness = vPNeighbor.x.sub( vP.x ).mul( 3 ).toVar();
|
|
1135
|
+
const tk = max( minThickness, this.thickness ).toVar();
|
|
1136
|
+
|
|
1137
|
+
If( away.lessThanEqual( tk ), () => {
|
|
1138
|
+
|
|
1139
|
+
const vN = this.normalNode.sample( uvS ).rgb.normalize().toVar();
|
|
1140
|
+
|
|
1141
|
+
// the reflected ray is pointing towards the same side as the fragment's normal (current ray position),
|
|
1142
|
+
// which means it wouldn't reflect off the surface. The loop continues to the next step for the next ray sample.
|
|
1143
|
+
if ( this.stochastic === false ) {
|
|
1144
|
+
|
|
1145
|
+
If( dot( viewReflectDir, vN ).greaterThanEqual( 0 ), () => {
|
|
1146
|
+
|
|
1147
|
+
Continue();
|
|
526
1148
|
|
|
527
|
-
|
|
528
|
-
If( this._isPerspectiveCamera, () => {
|
|
1149
|
+
} );
|
|
529
1150
|
|
|
530
|
-
|
|
531
|
-
|
|
1151
|
+
// this distance represents the depth of the intersection point between the reflected ray and the scene.
|
|
1152
|
+
const distance = pointPlaneDistance( vP, viewPosition, viewNormal ).toVar();
|
|
532
1153
|
|
|
533
|
-
|
|
1154
|
+
// Distance exceeding limit: The reflection is potentially too far away and
|
|
1155
|
+
// might not contribute significantly to the final color
|
|
1156
|
+
If( distance.greaterThan( this.maxDistance ), () => {
|
|
1157
|
+
|
|
1158
|
+
Break();
|
|
1159
|
+
|
|
1160
|
+
} );
|
|
1161
|
+
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
foundHit.assign( true );
|
|
1165
|
+
hitUvS.assign( uvS );
|
|
1166
|
+
hitD.assign( d );
|
|
1167
|
+
|
|
1168
|
+
if ( this.binaryRefine ) {
|
|
1169
|
+
|
|
1170
|
+
hitSLo.assign( sampleFraction( float( i ).sub( 1 ) ) );
|
|
1171
|
+
hitSHi.assign( s );
|
|
1172
|
+
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
Break();
|
|
534
1176
|
|
|
535
|
-
|
|
1177
|
+
|
|
1178
|
+
} );
|
|
536
1179
|
|
|
537
1180
|
} );
|
|
538
1181
|
|
|
539
|
-
|
|
540
|
-
If( viewReflectRayZ.lessThanEqual( vZ ), () => {
|
|
1182
|
+
} );
|
|
541
1183
|
|
|
542
|
-
|
|
543
|
-
// to clarify vP is the fragment's view position which is not an exact point on the ray
|
|
544
|
-
const vP = getViewPosition( uvNode, d, this._cameraProjectionMatrixInverse ).toVar();
|
|
545
|
-
const away = pointToLineDistance( vP, viewPosition, d1viewPosition ).toVar();
|
|
1184
|
+
If( foundHit, () => {
|
|
546
1185
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
const vPNeighbor = getViewPosition( uvNeighbor, d, this._cameraProjectionMatrixInverse ).toVar();
|
|
551
|
-
const minThickness = vPNeighbor.x.sub( vP.x ).toVar();
|
|
552
|
-
minThickness.mulAssign( 3 ); // expand a bit to avoid errors
|
|
1186
|
+
// Bisect the bracketed crossing toward the exact intersection. Run after the march, not
|
|
1187
|
+
// nested (a loop-inside-a-loop tripped shader-compiler bugs on some drivers).
|
|
1188
|
+
if ( this.binaryRefine ) {
|
|
553
1189
|
|
|
554
|
-
|
|
1190
|
+
Loop( { start: int( 0 ), end: int( 8 ), type: 'int', condition: '<' }, () => {
|
|
1191
|
+
|
|
1192
|
+
const sMid = hitSLo.add( hitSHi ).mul( 0.5 ).toVar();
|
|
1193
|
+
const sceneZMid = getViewZ( sampleDepth( screenPosAt( sMid ).mul( invResolution ) ) );
|
|
555
1194
|
|
|
556
|
-
|
|
1195
|
+
If( reflectRayZAt( sMid ).lessThanEqual( sceneZMid ), () => {
|
|
557
1196
|
|
|
558
|
-
|
|
1197
|
+
hitSHi.assign( sMid );
|
|
559
1198
|
|
|
560
|
-
|
|
1199
|
+
} ).Else( () => {
|
|
561
1200
|
|
|
562
|
-
|
|
563
|
-
// which means it wouldn't reflect off the surface. The loop continues to the next step for the next ray sample.
|
|
564
|
-
Continue();
|
|
1201
|
+
hitSLo.assign( sMid );
|
|
565
1202
|
|
|
566
1203
|
} );
|
|
567
1204
|
|
|
568
|
-
|
|
569
|
-
const distance = pointPlaneDistance( vP, viewPosition, viewNormal ).toVar();
|
|
1205
|
+
} );
|
|
570
1206
|
|
|
571
|
-
|
|
1207
|
+
// Refinement moved the crossing, so re-fetch UV/depth at the refined `s`.
|
|
1208
|
+
hitUvS.assign( screenPosAt( hitSHi ).mul( invResolution ) );
|
|
1209
|
+
hitD.assign( sampleDepth( hitUvS ) );
|
|
572
1210
|
|
|
573
|
-
|
|
574
|
-
// might not contribute significantly to the final color
|
|
575
|
-
Break();
|
|
1211
|
+
}
|
|
576
1212
|
|
|
577
|
-
|
|
1213
|
+
// Shade the hit, reusing the depth fetched during the march (or refinement).
|
|
1214
|
+
const uvS = hitUvS;
|
|
1215
|
+
const vP = getViewPosition( uvS, hitD, this._cameraProjectionMatrixInverse ).toVar();
|
|
578
1216
|
|
|
579
|
-
|
|
1217
|
+
// In blur mode the ratio² falloff re-grows past maxDistance, so over-range hits fall back
|
|
1218
|
+
// to env. The scatter path bounds reach via ray length, so every hit shades.
|
|
1219
|
+
const distancePointPlane = this.stochastic === false ? pointPlaneDistance( vP, viewPosition, viewNormal ).toVar() : float( 0 );
|
|
1220
|
+
const withinRange = distancePointPlane.lessThanEqual( this.maxDistance );
|
|
580
1221
|
|
|
581
|
-
|
|
582
|
-
const ratio = float( 1 ).sub( distance.div( this.maxDistance ) ).toVar();
|
|
583
|
-
const attenuation = ratio.mul( ratio );
|
|
584
|
-
op.mulAssign( attenuation );
|
|
1222
|
+
If( withinRange, () => {
|
|
585
1223
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
op.mulAssign( fresnelCoe );
|
|
1224
|
+
const hitWorldPosition = this._cameraWorldMatrix.mul( vec4( vP, 1.0 ) ).xyz.toVar();
|
|
1225
|
+
const worldDistance = distance( worldPosition, hitWorldPosition ).mul( specDominantFactor ).toVar();
|
|
589
1226
|
|
|
590
|
-
|
|
591
|
-
const reflectColor = this.colorNode.sample( uvNode );
|
|
592
|
-
output.assign( vec4( reflectColor.rgb.mul( op ), 1 ) );
|
|
593
|
-
Break();
|
|
1227
|
+
const reflectColor = this.colorNode.sample( uvS ).toVar();
|
|
594
1228
|
|
|
595
|
-
|
|
1229
|
+
// Multi-bounce: add the reprojected previous-frame reflection at the hit point.
|
|
1230
|
+
reflectColor.rgb.assign( reprojectHitPointHistory( uvS, reflectColor.rgb ) );
|
|
1231
|
+
|
|
1232
|
+
if ( this.stochastic === true ) applyHitEdgeFade( reflectColor, uvS, hitBorderWidth );
|
|
1233
|
+
|
|
1234
|
+
// The scatter (GGX) path bakes distance/grazing response into finalSampleWeight.
|
|
1235
|
+
// The mirror/blur path is a plain reflection, so reapply upstream's squared
|
|
1236
|
+
// distance attenuation and grazing Fresnel here to match its falloff.
|
|
1237
|
+
let weightedColor = reflectColor.rgb.mul( finalSampleWeight );
|
|
1238
|
+
|
|
1239
|
+
if ( this.stochastic === false ) {
|
|
1240
|
+
|
|
1241
|
+
const ratio = float( 1 ).sub( distancePointPlane.div( this.maxDistance ) ).toVar();
|
|
1242
|
+
const attenuation = ratio.mul( ratio ).toVar();
|
|
1243
|
+
const fresnelCoe = div( dot( viewIncidentDir, viewReflectDir ).add( 1 ), 2 ).toVar();
|
|
1244
|
+
weightedColor = weightedColor.mul( attenuation.mul( fresnelCoe ) );
|
|
1245
|
+
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
hit.assign( 1 );
|
|
1249
|
+
output.assign( vec4( weightedColor, worldDistance ) );
|
|
596
1250
|
|
|
597
1251
|
} );
|
|
598
1252
|
|
|
599
1253
|
} );
|
|
600
1254
|
|
|
601
|
-
|
|
1255
|
+
// Screen-space ray missed: environment fallback (MIS when CDF env is set up).
|
|
1256
|
+
If( hit.equal( 0 ), () => {
|
|
1257
|
+
|
|
1258
|
+
if ( this.stochastic === true ) {
|
|
1259
|
+
|
|
1260
|
+
output.assign( vec4( sampleEnvReflection().mul( this.environmentIntensity ), float( ENV_RAY_LENGTH ) ) );
|
|
1261
|
+
|
|
1262
|
+
// Misses fade by the surface pixel UV (where the reflection is being shaded).
|
|
1263
|
+
if ( this.screenEdgeFadeBlack ) {
|
|
1264
|
+
|
|
1265
|
+
output.rgb.mulAssign( surfaceBorderFactor );
|
|
1266
|
+
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
} );
|
|
1272
|
+
|
|
1273
|
+
const lum = luminance( output.rgb ).max( 1e-4 ).toVar();
|
|
1274
|
+
output.rgb.mulAssign( this.maxLuminance.div( lum ).min( 1 ) );
|
|
1275
|
+
|
|
1276
|
+
// scale the reflection color by the user-controlled intensity
|
|
1277
|
+
output.rgb.mulAssign( this.intensity );
|
|
1278
|
+
|
|
1279
|
+
return output.max( 0 );
|
|
602
1280
|
|
|
603
1281
|
} );
|
|
604
1282
|
|
|
605
|
-
this._ssrMaterial.fragmentNode = ssr().context( builder.getSharedContext() );
|
|
606
|
-
this._ssrMaterial.needsUpdate = true;
|
|
607
1283
|
|
|
608
|
-
|
|
1284
|
+
this._ssrFn = ssr;
|
|
1285
|
+
this._sharedContext = builder.getSharedContext();
|
|
1286
|
+
this._buildSSRMaterial();
|
|
609
1287
|
|
|
610
1288
|
const reflectionBuffer = texture( this._ssrRenderTarget.texture );
|
|
611
1289
|
|
|
612
|
-
|
|
613
|
-
|
|
1290
|
+
if ( this.stochastic === false ) {
|
|
1291
|
+
|
|
1292
|
+
this._buildBlurMaterial();
|
|
1293
|
+
|
|
1294
|
+
}
|
|
614
1295
|
|
|
615
1296
|
this._copyMaterial.fragmentNode = reflectionBuffer;
|
|
616
1297
|
this._copyMaterial.needsUpdate = true;
|
|
@@ -621,6 +1302,12 @@ class SSRNode extends TempNode {
|
|
|
621
1302
|
|
|
622
1303
|
}
|
|
623
1304
|
|
|
1305
|
+
getRenderTarget() {
|
|
1306
|
+
|
|
1307
|
+
return this._ssrRenderTarget;
|
|
1308
|
+
|
|
1309
|
+
}
|
|
1310
|
+
|
|
624
1311
|
/**
|
|
625
1312
|
* Frees internal resources. This method should be called
|
|
626
1313
|
* when the effect is no longer required.
|
|
@@ -634,6 +1321,13 @@ class SSRNode extends TempNode {
|
|
|
634
1321
|
this._blurMaterial.dispose();
|
|
635
1322
|
this._copyMaterial.dispose();
|
|
636
1323
|
|
|
1324
|
+
if ( this._importanceEnvironment !== null ) {
|
|
1325
|
+
|
|
1326
|
+
this._importanceEnvironment.dispose();
|
|
1327
|
+
this._importanceEnvironment = null;
|
|
1328
|
+
|
|
1329
|
+
}
|
|
1330
|
+
|
|
637
1331
|
}
|
|
638
1332
|
|
|
639
1333
|
}
|
|
@@ -648,9 +1342,12 @@ export default SSRNode;
|
|
|
648
1342
|
* @param {Node<vec4>} colorNode - The node that represents the beauty pass.
|
|
649
1343
|
* @param {Node<float>} depthNode - A node that represents the beauty pass's depth.
|
|
650
1344
|
* @param {Node<vec3>} normalNode - A node that represents the beauty pass's normals.
|
|
651
|
-
* @param {
|
|
652
|
-
* @param {?Node<float>} [roughnessNode=null] - A node that represents the beauty pass's roughness.
|
|
653
|
-
* @param {?Camera} [camera=null] - The camera the scene is rendered with.
|
|
1345
|
+
* @param {SSRNodeOptions} [options] - Optional inputs for material and environment data.
|
|
654
1346
|
* @returns {SSRNode}
|
|
655
1347
|
*/
|
|
656
|
-
export const ssr = ( colorNode, depthNode, normalNode,
|
|
1348
|
+
export const ssr = ( colorNode, depthNode, normalNode, options = {} ) => nodeObject( new SSRNode(
|
|
1349
|
+
nodeObject( colorNode ),
|
|
1350
|
+
nodeObject( depthNode ),
|
|
1351
|
+
nodeObject( normalNode ),
|
|
1352
|
+
options
|
|
1353
|
+
) );
|