@plastic-software/three 0.183.3 → 0.184.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 +783 -290
- package/build/three.core.js +372 -110
- package/build/three.core.min.js +1 -1
- package/build/three.module.js +436 -184
- package/build/three.module.min.js +1 -1
- package/build/three.tsl.js +7 -1
- package/build/three.tsl.min.js +1 -1
- package/build/three.webgpu.js +2979 -1281
- package/build/three.webgpu.min.js +1 -1
- package/build/three.webgpu.nodes.js +2942 -1281
- package/build/three.webgpu.nodes.min.js +1 -1
- package/examples/jsm/Addons.js +11 -0
- package/examples/jsm/animation/CCDIKSolver.js +5 -1
- package/examples/jsm/controls/ArcballControls.js +4 -1
- package/examples/jsm/controls/DragControls.js +2 -2
- package/examples/jsm/controls/FirstPersonControls.js +58 -54
- package/examples/jsm/controls/FlyControls.js +4 -0
- package/examples/jsm/controls/OrbitControls.js +2 -2
- package/examples/jsm/controls/TrackballControls.js +2 -2
- package/examples/jsm/controls/TransformControls.js +34 -2
- package/examples/jsm/csm/CSMShadowNode.js +6 -2
- package/examples/jsm/exporters/GLTFExporter.js +21 -5
- package/examples/jsm/geometries/TextGeometry.js +18 -0
- package/examples/jsm/helpers/LightProbeGridHelper.js +221 -0
- package/examples/jsm/inspector/Extension.js +13 -0
- package/examples/jsm/inspector/Inspector.js +169 -114
- package/examples/jsm/inspector/RendererInspector.js +2 -2
- package/examples/jsm/inspector/extensions/extensions.json +6 -0
- package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphEditor.js +916 -0
- package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphLoader.js +281 -0
- package/examples/jsm/inspector/tabs/Memory.js +128 -0
- package/examples/jsm/inspector/tabs/Parameters.js +34 -2
- package/examples/jsm/inspector/tabs/Performance.js +2 -2
- package/examples/jsm/inspector/tabs/Settings.js +264 -0
- package/examples/jsm/inspector/tabs/Timeline.js +1611 -0
- package/examples/jsm/inspector/tabs/Viewer.js +105 -3
- package/examples/jsm/inspector/ui/Graph.js +2 -2
- package/examples/jsm/inspector/ui/List.js +1 -1
- package/examples/jsm/inspector/ui/Profiler.js +273 -176
- package/examples/jsm/inspector/ui/Style.js +64 -10
- package/examples/jsm/inspector/ui/Tab.js +39 -7
- package/examples/jsm/inspector/ui/Values.js +39 -2
- package/examples/jsm/inspector/ui/utils.js +13 -0
- package/examples/jsm/interaction/InteractionManager.js +226 -0
- package/examples/jsm/libs/meshopt_decoder.module.js +8 -8
- package/examples/jsm/lighting/DynamicLighting.js +82 -0
- package/examples/jsm/lighting/LightProbeGrid.js +651 -0
- package/examples/jsm/lines/LineMaterial.js +1 -1
- package/examples/jsm/loaders/EXRLoader.js +682 -43
- package/examples/jsm/loaders/FBXLoader.js +233 -33
- package/examples/jsm/loaders/GLTFLoader.js +24 -7
- package/examples/jsm/loaders/HDRLoader.js +1 -1
- package/examples/jsm/loaders/KTX2Loader.js +8 -2
- package/examples/jsm/loaders/LDrawLoader.js +39 -47
- package/examples/jsm/loaders/SVGLoader.js +1 -1
- package/examples/jsm/loaders/VTKLoader.js +5 -1
- package/examples/jsm/loaders/collada/ColladaComposer.js +101 -7
- package/examples/jsm/loaders/collada/ColladaParser.js +19 -4
- package/examples/jsm/loaders/usd/USDAParser.js +6 -0
- package/examples/jsm/loaders/usd/USDCParser.js +26 -0
- package/examples/jsm/loaders/usd/USDComposer.js +656 -103
- package/examples/jsm/misc/GPUComputationRenderer.js +2 -0
- package/examples/jsm/misc/RollerCoaster.js +42 -4
- package/examples/jsm/modifiers/TessellateModifier.js +1 -1
- package/examples/jsm/objects/Reflector.js +73 -25
- package/examples/jsm/objects/Sky.js +14 -2
- package/examples/jsm/objects/SkyMesh.js +23 -6
- package/examples/jsm/renderers/Projector.js +18 -38
- package/examples/jsm/renderers/SVGRenderer.js +6 -25
- package/examples/jsm/transpiler/GLSLDecoder.js +2 -2
- package/examples/jsm/tsl/WebGLNodesHandler.js +605 -0
- package/examples/jsm/tsl/display/AfterImageNode.js +10 -0
- package/examples/jsm/tsl/display/AnamorphicNode.js +11 -0
- package/examples/jsm/tsl/display/BilateralBlurNode.js +10 -0
- package/examples/jsm/tsl/display/ChromaticAberrationNode.js +3 -36
- package/examples/jsm/tsl/display/FSR1Node.js +477 -0
- package/examples/jsm/tsl/display/GTAONode.js +2 -1
- package/examples/jsm/tsl/display/GaussianBlurNode.js +10 -0
- package/examples/jsm/tsl/display/GodraysNode.js +2 -11
- package/examples/jsm/tsl/display/OutlineNode.js +66 -16
- package/examples/jsm/tsl/display/SSGINode.js +0 -4
- package/examples/jsm/tsl/display/SharpenNode.js +283 -0
- package/examples/jsm/tsl/display/TAAUNode.js +835 -0
- package/examples/jsm/tsl/display/TRAANode.js +48 -7
- package/examples/jsm/tsl/lighting/DynamicLightsNode.js +300 -0
- package/examples/jsm/tsl/lighting/data/AmbientLightDataNode.js +61 -0
- package/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js +111 -0
- package/examples/jsm/tsl/lighting/data/HemisphereLightDataNode.js +99 -0
- package/examples/jsm/tsl/lighting/data/PointLightDataNode.js +134 -0
- package/examples/jsm/tsl/lighting/data/SpotLightDataNode.js +161 -0
- package/examples/jsm/tsl/math/Bayer.js +13 -2
- package/examples/jsm/utils/BufferGeometryUtils.js +2 -3
- package/examples/jsm/utils/ColorUtils.js +76 -0
- package/examples/jsm/utils/SkeletonUtils.js +14 -8
- package/examples/jsm/webxr/XRHandMeshModel.js +36 -10
- package/examples/jsm/webxr/XRHandModelFactory.js +2 -1
- package/package.json +4 -4
- package/src/Three.Core.js +1 -0
- package/src/Three.TSL.js +6 -0
- package/src/Three.WebGPU.Nodes.js +3 -0
- package/src/Three.WebGPU.js +6 -0
- package/src/animation/AnimationAction.js +11 -1
- package/src/audio/AudioContext.js +2 -2
- package/src/constants.js +1 -1
- package/src/core/BufferAttribute.js +13 -1
- package/src/core/Clock.js +1 -1
- package/src/core/Object3D.js +1 -5
- package/src/core/RenderTarget.js +1 -0
- package/src/extras/PMREMGenerator.js +1 -1
- package/src/extras/curves/CatmullRomCurve3.js +3 -2
- package/src/loaders/AudioLoader.js +11 -1
- package/src/loaders/DataTextureLoader.js +6 -4
- package/src/loaders/FileLoader.js +1 -2
- package/src/loaders/ImageBitmapLoader.js +4 -6
- package/src/loaders/MaterialLoader.js +1 -1
- package/src/loaders/ObjectLoader.js +25 -4
- package/src/loaders/nodes/NodeObjectLoader.js +18 -0
- package/src/materials/MeshToonMaterial.js +1 -1
- package/src/materials/nodes/Line2NodeMaterial.js +27 -0
- package/src/materials/nodes/NodeMaterial.js +0 -27
- package/src/materials/nodes/manager/NodeMaterialObserver.js +188 -89
- package/src/math/Line3.js +3 -0
- package/src/math/Matrix2.js +13 -9
- package/src/math/Matrix3.js +13 -9
- package/src/math/Matrix4.js +13 -9
- package/src/math/Plane.js +4 -3
- package/src/math/Triangle.js +1 -1
- package/src/math/Vector2.js +11 -7
- package/src/math/Vector3.js +12 -8
- package/src/math/Vector4.js +13 -9
- package/src/nodes/Nodes.js +0 -1
- package/src/nodes/TSL.js +1 -1
- package/src/nodes/accessors/BufferAttributeNode.js +9 -3
- package/src/nodes/accessors/CubeTextureNode.js +7 -1
- package/src/nodes/accessors/MaterialProperties.js +2 -5
- package/src/nodes/accessors/Object3DNode.js +1 -1
- package/src/nodes/accessors/ReferenceBaseNode.js +2 -2
- package/src/nodes/accessors/ReferenceNode.js +4 -4
- package/src/nodes/accessors/SceneProperties.js +2 -8
- package/src/nodes/accessors/StorageBufferNode.js +10 -4
- package/src/nodes/accessors/StorageTextureNode.js +4 -9
- package/src/nodes/accessors/TextureNode.js +10 -2
- package/src/nodes/accessors/UniformArrayNode.js +2 -2
- package/src/nodes/code/FunctionCallNode.js +1 -1
- package/src/nodes/code/FunctionNode.js +1 -1
- package/src/nodes/core/ArrayNode.js +1 -1
- package/src/nodes/core/AssignNode.js +1 -1
- package/src/nodes/core/AttributeNode.js +1 -1
- package/src/nodes/core/BypassNode.js +1 -1
- package/src/nodes/core/ContextNode.js +1 -1
- package/src/nodes/core/IndexNode.js +2 -1
- package/src/nodes/core/InputNode.js +1 -1
- package/src/nodes/core/InspectorNode.js +1 -1
- package/src/nodes/core/IsolateNode.js +1 -1
- package/src/nodes/core/Node.js +83 -12
- package/src/nodes/core/NodeBuilder.js +117 -16
- package/src/nodes/core/NodeUtils.js +1 -1
- package/src/nodes/core/OutputStructNode.js +1 -1
- package/src/nodes/core/ParameterNode.js +1 -1
- package/src/nodes/core/StackNode.js +1 -1
- package/src/nodes/core/StructNode.js +1 -1
- package/src/nodes/core/StructTypeNode.js +1 -1
- package/src/nodes/core/SubBuildNode.js +1 -1
- package/src/nodes/core/UniformGroupNode.js +36 -6
- package/src/nodes/core/VarNode.js +1 -1
- package/src/nodes/core/VaryingNode.js +1 -1
- package/src/nodes/display/NormalMapNode.js +2 -2
- package/src/nodes/display/PassNode.js +27 -7
- package/src/nodes/display/RenderOutputNode.js +4 -4
- package/src/nodes/display/ScreenNode.js +1 -1
- package/src/nodes/display/ViewportDepthTextureNode.js +11 -15
- package/src/nodes/display/ViewportTextureNode.js +18 -7
- package/src/nodes/functions/BSDF/V_GGX_SmithCorrelated_Anisotropic.js +2 -2
- package/src/nodes/geometry/RangeNode.js +1 -1
- package/src/nodes/gpgpu/AtomicFunctionNode.js +1 -1
- package/src/nodes/gpgpu/BarrierNode.js +9 -0
- package/src/nodes/gpgpu/ComputeBuiltinNode.js +1 -1
- package/src/nodes/gpgpu/ComputeNode.js +69 -44
- package/src/nodes/gpgpu/SubgroupFunctionNode.js +1 -1
- package/src/nodes/lighting/LightsNode.js +6 -27
- package/src/nodes/lighting/ShadowNode.js +24 -2
- package/src/nodes/math/BitcastNode.js +1 -1
- package/src/nodes/math/ConditionalNode.js +1 -1
- package/src/nodes/math/MathNode.js +73 -1
- package/src/nodes/math/OperatorNode.js +1 -1
- package/src/nodes/math/PackFloatNode.js +1 -1
- package/src/nodes/math/UnpackFloatNode.js +1 -1
- package/src/nodes/tsl/TSLBase.js +1 -1
- package/src/nodes/tsl/TSLCore.js +21 -3
- package/src/nodes/utils/ArrayElementNode.js +1 -1
- package/src/nodes/utils/ConvertNode.js +1 -1
- package/src/nodes/utils/DebugNode.js +1 -1
- package/src/nodes/utils/EventNode.js +30 -0
- package/src/nodes/utils/FlipNode.js +1 -1
- package/src/nodes/utils/FunctionOverloadingNode.js +1 -1
- package/src/nodes/utils/JoinNode.js +1 -1
- package/src/nodes/utils/MemberNode.js +1 -1
- package/src/nodes/utils/Remap.js +48 -0
- package/src/nodes/utils/RotateNode.js +1 -1
- package/src/nodes/utils/SetNode.js +1 -1
- package/src/nodes/utils/SplitNode.js +1 -1
- package/src/objects/BatchedMesh.js +17 -2
- package/src/objects/InstancedMesh.js +19 -3
- package/src/objects/SkinnedMesh.js +26 -9
- package/src/renderers/WebGLRenderer.js +148 -49
- package/src/renderers/common/Animation.js +3 -3
- package/src/renderers/common/Attributes.js +15 -1
- package/src/renderers/common/Backend.js +0 -8
- package/src/renderers/common/Background.js +2 -2
- package/src/renderers/common/BindGroup.js +1 -8
- package/src/renderers/common/Bindings.js +2 -2
- package/src/renderers/common/ComputePipeline.js +1 -1
- package/src/renderers/common/CubeRenderTarget.js +1 -1
- package/src/renderers/common/Info.js +333 -4
- package/src/renderers/common/InspectorBase.js +6 -1
- package/src/renderers/common/Pipelines.js +36 -3
- package/src/renderers/common/ReadbackBuffer.js +78 -0
- package/src/renderers/common/RenderBundle.js +3 -1
- package/src/renderers/common/RenderBundles.js +5 -2
- package/src/renderers/common/RenderObject.js +2 -2
- package/src/renderers/common/RenderObjects.js +3 -3
- package/src/renderers/common/RenderPipeline.js +35 -6
- package/src/renderers/common/Renderer.js +232 -53
- package/src/renderers/common/Textures.js +72 -3
- package/src/renderers/common/UniformsGroup.js +1 -1
- package/src/renderers/common/XRManager.js +34 -27
- package/src/renderers/common/extras/PMREMGenerator.js +23 -15
- package/src/renderers/common/nodes/NodeBuilderState.js +1 -1
- package/src/renderers/common/nodes/NodeManager.js +230 -99
- package/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js +20 -0
- package/src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl.js +9 -1
- package/src/renderers/shaders/ShaderChunk/envmap_common_pars_fragment.glsl.js +0 -1
- package/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/lightprobes_pars_fragment.glsl.js +80 -0
- package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +8 -0
- package/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js +2 -0
- package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +1 -3
- package/src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/premultiplied_alpha_fragment.glsl.js +0 -1
- package/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js +12 -2
- package/src/renderers/shaders/ShaderChunk.js +2 -0
- package/src/renderers/shaders/ShaderLib/backgroundCube.glsl.js +1 -2
- package/src/renderers/shaders/ShaderLib.js +0 -1
- package/src/renderers/shaders/UniformsLib.js +7 -2
- package/src/renderers/shaders/UniformsUtils.js +27 -5
- package/src/renderers/webgl/WebGLAnimation.js +2 -1
- package/src/renderers/webgl/WebGLBackground.js +13 -13
- package/src/renderers/webgl/WebGLBufferRenderer.js +0 -32
- package/src/renderers/webgl/WebGLCapabilities.js +6 -0
- package/src/renderers/webgl/WebGLIndexedBufferRenderer.js +0 -32
- package/src/renderers/webgl/WebGLMaterials.js +12 -13
- package/src/renderers/webgl/WebGLOutput.js +4 -1
- package/src/renderers/webgl/WebGLProgram.js +5 -0
- package/src/renderers/webgl/WebGLPrograms.js +24 -3
- package/src/renderers/webgl/WebGLRenderStates.js +13 -2
- package/src/renderers/webgl/WebGLState.js +43 -0
- package/src/renderers/webgl/WebGLTextures.js +129 -26
- package/src/renderers/webgl/WebGLUniformsGroups.js +19 -0
- package/src/renderers/webgl-fallback/WebGLBackend.js +106 -65
- package/src/renderers/webgl-fallback/WebGLBufferRenderer.js +0 -41
- package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +29 -51
- package/src/renderers/webgl-fallback/utils/WebGLAttributeUtils.js +53 -19
- package/src/renderers/webgl-fallback/utils/WebGLCapabilities.js +25 -0
- package/src/renderers/webgl-fallback/utils/WebGLState.js +42 -1
- package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +63 -50
- package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +1 -1
- package/src/renderers/webgpu/WebGPUBackend.js +160 -146
- package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +55 -33
- package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +103 -17
- package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +1 -1
- package/src/renderers/webgpu/utils/WebGPUCapabilities.js +48 -0
- package/src/renderers/webgpu/utils/WebGPUConstants.js +8 -0
- package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +91 -17
- package/src/renderers/webgpu/utils/WebGPUUtils.js +18 -2
- package/src/renderers/webxr/WebXRController.js +12 -0
- package/src/textures/HTMLTexture.js +74 -0
- package/src/textures/Source.js +1 -1
- package/src/textures/Texture.js +13 -2
- package/src/utils.js +23 -1
- package/src/nodes/utils/RemapNode.js +0 -125
|
@@ -0,0 +1,835 @@
|
|
|
1
|
+
import { HalfFloatType, Vector2, RenderTarget, RendererUtils, QuadMesh, NodeMaterial, TempNode, NodeUpdateType, Matrix4, DepthTexture } from 'three/webgpu';
|
|
2
|
+
import { add, exp, float, If, Fn, max, texture, uniform, uv, vec2, vec4, luminance, convertToTexture, passTexture, velocity, getViewPosition, viewZToPerspectiveDepth, struct, ivec2, mix, property, outputStruct } from 'three/tsl';
|
|
3
|
+
|
|
4
|
+
const _quadMesh = /*@__PURE__*/ new QuadMesh();
|
|
5
|
+
const _size = /*@__PURE__*/ new Vector2();
|
|
6
|
+
|
|
7
|
+
let _rendererState;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A special node that performs Temporal Anti-Aliasing Upscaling (TAAU).
|
|
12
|
+
*
|
|
13
|
+
* Like TRAA, the node accumulates jittered samples over multiple frames and
|
|
14
|
+
* reprojects history with motion vectors. Unlike TRAA, the input buffers
|
|
15
|
+
* (beauty, depth, velocity) are expected to be rendered at a lower resolution
|
|
16
|
+
* than the renderer's drawing buffer — typically by lowering the upstream
|
|
17
|
+
* pass's resolution via {@link PassNode#setResolutionScale} — and the resolve
|
|
18
|
+
* pass reconstructs an output-resolution image using a 9-tap Blackman-Harris
|
|
19
|
+
* filter (Gaussian approximation) over the jittered input samples. The result
|
|
20
|
+
* is an alternative to FSR2/3 that does anti-aliasing and upscaling in a
|
|
21
|
+
* single pass.
|
|
22
|
+
*
|
|
23
|
+
* References:
|
|
24
|
+
* - Karis, "High Quality Temporal Supersampling", SIGGRAPH 2014, {@link https://advances.realtimerendering.com/s2014/}
|
|
25
|
+
* - Riley/Arcila, FidelityFX Super Resolution 2, GDC 2022, {@link https://gpuopen.com/download/GDC_FidelityFX_Super_Resolution_2_0.pdf}
|
|
26
|
+
*
|
|
27
|
+
* Note: MSAA must be disabled when TAAU is in use.
|
|
28
|
+
*
|
|
29
|
+
* @augments TempNode
|
|
30
|
+
* @three_import import { taau } from 'three/addons/tsl/display/TAAUNode.js';
|
|
31
|
+
*/
|
|
32
|
+
class TAAUNode extends TempNode {
|
|
33
|
+
|
|
34
|
+
static get type() {
|
|
35
|
+
|
|
36
|
+
return 'TAAUNode';
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Constructs a new TAAU node.
|
|
42
|
+
*
|
|
43
|
+
* @param {TextureNode} beautyNode - The texture node that represents the input of the effect.
|
|
44
|
+
* @param {TextureNode} depthNode - A node that represents the scene's depth.
|
|
45
|
+
* @param {TextureNode} velocityNode - A node that represents the scene's velocity.
|
|
46
|
+
* @param {Camera} camera - The camera the scene is rendered with.
|
|
47
|
+
*/
|
|
48
|
+
constructor( beautyNode, depthNode, velocityNode, camera ) {
|
|
49
|
+
|
|
50
|
+
super( 'vec4' );
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* This flag can be used for type testing.
|
|
54
|
+
*
|
|
55
|
+
* @type {boolean}
|
|
56
|
+
* @readonly
|
|
57
|
+
* @default true
|
|
58
|
+
*/
|
|
59
|
+
this.isTAAUNode = true;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node renders
|
|
63
|
+
* its effect once per frame in `updateBefore()`.
|
|
64
|
+
*
|
|
65
|
+
* @type {string}
|
|
66
|
+
* @default 'frame'
|
|
67
|
+
*/
|
|
68
|
+
this.updateBeforeType = NodeUpdateType.FRAME;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The texture node that represents the input of the effect.
|
|
72
|
+
*
|
|
73
|
+
* @type {TextureNode}
|
|
74
|
+
*/
|
|
75
|
+
this.beautyNode = beautyNode;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A node that represents the scene's depth.
|
|
79
|
+
*
|
|
80
|
+
* @type {TextureNode}
|
|
81
|
+
*/
|
|
82
|
+
this.depthNode = depthNode;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A node that represents the scene's velocity.
|
|
86
|
+
*
|
|
87
|
+
* @type {TextureNode}
|
|
88
|
+
*/
|
|
89
|
+
this.velocityNode = velocityNode;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The camera the scene is rendered with.
|
|
93
|
+
*
|
|
94
|
+
* @type {Camera}
|
|
95
|
+
*/
|
|
96
|
+
this.camera = camera;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* When the difference between the current and previous depth goes above this threshold,
|
|
100
|
+
* the history is considered invalid.
|
|
101
|
+
*
|
|
102
|
+
* @type {number}
|
|
103
|
+
* @default 0.0005
|
|
104
|
+
*/
|
|
105
|
+
this.depthThreshold = 0.0005;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The depth difference within the 3×3 neighborhood to consider a pixel as an edge.
|
|
109
|
+
*
|
|
110
|
+
* @type {number}
|
|
111
|
+
* @default 0.001
|
|
112
|
+
*/
|
|
113
|
+
this.edgeDepthDiff = 0.001;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The history becomes invalid as the pixel length of the velocity approaches this value.
|
|
117
|
+
*
|
|
118
|
+
* @type {number}
|
|
119
|
+
* @default 128
|
|
120
|
+
*/
|
|
121
|
+
this.maxVelocityLength = 128;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Baseline weight applied to the current frame in the resolve. Lower
|
|
125
|
+
* values produce smoother results with longer accumulation but slower
|
|
126
|
+
* convergence on disoccluded regions; the motion factor is added on
|
|
127
|
+
* top, so fast-moving pixels still respond quickly.
|
|
128
|
+
*
|
|
129
|
+
* @type {number}
|
|
130
|
+
* @default 0.025
|
|
131
|
+
*/
|
|
132
|
+
this.currentFrameWeight = 0.025;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The jitter index selects the current camera offset value.
|
|
136
|
+
*
|
|
137
|
+
* @private
|
|
138
|
+
* @type {number}
|
|
139
|
+
* @default 0
|
|
140
|
+
*/
|
|
141
|
+
this._jitterIndex = 0;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* A uniform node holding the current jitter offset in input-pixel
|
|
145
|
+
* units. The shader needs this to know where each input sample was
|
|
146
|
+
* actually rendered when computing per-tap reconstruction weights.
|
|
147
|
+
*
|
|
148
|
+
* @private
|
|
149
|
+
* @type {UniformNode<vec2>}
|
|
150
|
+
*/
|
|
151
|
+
this._jitterOffset = uniform( new Vector2() );
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The render target that represents the history of frame data.
|
|
155
|
+
* Sized to the renderer's drawing buffer (the output resolution).
|
|
156
|
+
*
|
|
157
|
+
* @private
|
|
158
|
+
* @type {?RenderTarget}
|
|
159
|
+
*/
|
|
160
|
+
this._historyRenderTarget = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType, count: 2 } );
|
|
161
|
+
this._historyRenderTarget.textures[ 0 ].name = 'TAAUNode.history.color';
|
|
162
|
+
this._historyRenderTarget.textures[ 1 ].name = 'TAAUNode.history.lock';
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The render target for the resolve. Sized to the renderer's drawing
|
|
166
|
+
* buffer (the output resolution).
|
|
167
|
+
*
|
|
168
|
+
* @private
|
|
169
|
+
* @type {?RenderTarget}
|
|
170
|
+
*/
|
|
171
|
+
this._resolveRenderTarget = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
|
|
172
|
+
this._resolveRenderTarget.texture.name = 'TAAUNode.resolve';
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Render target whose depth attachment holds the previous frame's
|
|
176
|
+
* depth buffer. The depth texture must be owned by a render target
|
|
177
|
+
* so that `copyTextureToTexture` can copy into it on the WebGL
|
|
178
|
+
* backend, which uses a framebuffer blit and therefore needs the
|
|
179
|
+
* destination depth texture to be attached to a framebuffer. This
|
|
180
|
+
* render target is sized independently of the history target so it
|
|
181
|
+
* can match the (lower-resolution) input depth texture.
|
|
182
|
+
*
|
|
183
|
+
* @private
|
|
184
|
+
* @type {RenderTarget}
|
|
185
|
+
*/
|
|
186
|
+
this._previousDepthRenderTarget = new RenderTarget( 1, 1, { depthBuffer: false, depthTexture: new DepthTexture() } );
|
|
187
|
+
this._previousDepthRenderTarget.depthTexture.name = 'TAAUNode.previousDepth';
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Material used for the resolve step.
|
|
191
|
+
*
|
|
192
|
+
* @private
|
|
193
|
+
* @type {NodeMaterial}
|
|
194
|
+
*/
|
|
195
|
+
this._resolveMaterial = new NodeMaterial();
|
|
196
|
+
this._resolveMaterial.name = 'TAAU.resolve';
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Material used to seed the history render target on resize. It
|
|
200
|
+
* performs a bilinear upscale of the current beauty buffer into the
|
|
201
|
+
* output-sized history target so that the first frames after a
|
|
202
|
+
* resize do not fade in from black.
|
|
203
|
+
*
|
|
204
|
+
* @private
|
|
205
|
+
* @type {NodeMaterial}
|
|
206
|
+
*/
|
|
207
|
+
this._seedMaterial = new NodeMaterial();
|
|
208
|
+
this._seedMaterial.name = 'TAAU.seed';
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The result of the effect is represented as a separate texture node.
|
|
212
|
+
*
|
|
213
|
+
* @private
|
|
214
|
+
* @type {PassTextureNode}
|
|
215
|
+
*/
|
|
216
|
+
this._textureNode = passTexture( this, this._resolveRenderTarget.texture );
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Used to save the original/unjittered projection matrix.
|
|
220
|
+
*
|
|
221
|
+
* @private
|
|
222
|
+
* @type {Matrix4}
|
|
223
|
+
*/
|
|
224
|
+
this._originalProjectionMatrix = new Matrix4();
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* A uniform node holding the camera's near and far.
|
|
228
|
+
*
|
|
229
|
+
* @private
|
|
230
|
+
* @type {UniformNode<vec2>}
|
|
231
|
+
*/
|
|
232
|
+
this._cameraNearFar = uniform( new Vector2() );
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* A uniform node holding the camera world matrix.
|
|
236
|
+
*
|
|
237
|
+
* @private
|
|
238
|
+
* @type {UniformNode<mat4>}
|
|
239
|
+
*/
|
|
240
|
+
this._cameraWorldMatrix = uniform( new Matrix4() );
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* A uniform node holding the camera world matrix inverse.
|
|
244
|
+
*
|
|
245
|
+
* @private
|
|
246
|
+
* @type {UniformNode<mat4>}
|
|
247
|
+
*/
|
|
248
|
+
this._cameraWorldMatrixInverse = uniform( new Matrix4() );
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* A uniform node holding the camera projection matrix inverse.
|
|
252
|
+
*
|
|
253
|
+
* @private
|
|
254
|
+
* @type {UniformNode<mat4>}
|
|
255
|
+
*/
|
|
256
|
+
this._cameraProjectionMatrixInverse = uniform( new Matrix4() );
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* A uniform node holding the previous frame's view matrix.
|
|
260
|
+
*
|
|
261
|
+
* @private
|
|
262
|
+
* @type {UniformNode<mat4>}
|
|
263
|
+
*/
|
|
264
|
+
this._previousCameraWorldMatrix = uniform( new Matrix4() );
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* A uniform node holding the previous frame's projection matrix inverse.
|
|
268
|
+
*
|
|
269
|
+
* @private
|
|
270
|
+
* @type {UniformNode<mat4>}
|
|
271
|
+
*/
|
|
272
|
+
this._previousCameraProjectionMatrixInverse = uniform( new Matrix4() );
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* A texture node for the previous depth buffer.
|
|
276
|
+
*
|
|
277
|
+
* @private
|
|
278
|
+
* @type {TextureNode}
|
|
279
|
+
*/
|
|
280
|
+
this._previousDepthNode = texture( this._previousDepthRenderTarget.depthTexture );
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Sync the post processing stack with the TAAU node.
|
|
284
|
+
*
|
|
285
|
+
* @private
|
|
286
|
+
* @type {boolean}
|
|
287
|
+
*/
|
|
288
|
+
this._needsPostProcessingSync = false;
|
|
289
|
+
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Returns the result of the effect as a texture node.
|
|
294
|
+
*
|
|
295
|
+
* @return {PassTextureNode} A texture node that represents the result of the effect.
|
|
296
|
+
*/
|
|
297
|
+
getTextureNode() {
|
|
298
|
+
|
|
299
|
+
return this._textureNode;
|
|
300
|
+
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Sets the output size of the effect (history and resolve targets). The
|
|
305
|
+
* previous-depth texture is sized independently in `updateBefore()` to
|
|
306
|
+
* track the scene's current depth texture.
|
|
307
|
+
*
|
|
308
|
+
* @param {number} outputWidth - The output width (drawing buffer width).
|
|
309
|
+
* @param {number} outputHeight - The output height (drawing buffer height).
|
|
310
|
+
*/
|
|
311
|
+
setSize( outputWidth, outputHeight ) {
|
|
312
|
+
|
|
313
|
+
this._historyRenderTarget.setSize( outputWidth, outputHeight );
|
|
314
|
+
this._resolveRenderTarget.setSize( outputWidth, outputHeight );
|
|
315
|
+
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Defines the TAAU's current jitter as a view offset to the scene's
|
|
320
|
+
* camera. The jitter is shrunk to one *output* pixel (rather than one
|
|
321
|
+
* input pixel) so that the halton sequence gradually fills the output
|
|
322
|
+
* sub-pixel grid over multiple frames.
|
|
323
|
+
*
|
|
324
|
+
* @param {number} inputWidth - The width of the input buffers the camera renders into.
|
|
325
|
+
* @param {number} inputHeight - The height of the input buffers the camera renders into.
|
|
326
|
+
*/
|
|
327
|
+
setViewOffset( inputWidth, inputHeight ) {
|
|
328
|
+
|
|
329
|
+
// save original/unjittered projection matrix for velocity pass
|
|
330
|
+
|
|
331
|
+
this.camera.updateProjectionMatrix();
|
|
332
|
+
this._originalProjectionMatrix.copy( this.camera.projectionMatrix );
|
|
333
|
+
|
|
334
|
+
velocity.setProjectionMatrix( this._originalProjectionMatrix );
|
|
335
|
+
|
|
336
|
+
// The jitter range must span one output pixel (not one input pixel),
|
|
337
|
+
// so we shrink the input-pixel-unit offset by the ratio of input to
|
|
338
|
+
// output resolution.
|
|
339
|
+
|
|
340
|
+
const haltonOffset = _haltonOffsets[ this._jitterIndex ];
|
|
341
|
+
const jitterX = ( haltonOffset[ 0 ] - 0.5 );
|
|
342
|
+
const jitterY = ( haltonOffset[ 1 ] - 0.5 );
|
|
343
|
+
|
|
344
|
+
this._jitterOffset.value.set( jitterX, jitterY );
|
|
345
|
+
|
|
346
|
+
this.camera.setViewOffset(
|
|
347
|
+
|
|
348
|
+
inputWidth, inputHeight,
|
|
349
|
+
|
|
350
|
+
jitterX, jitterY,
|
|
351
|
+
|
|
352
|
+
inputWidth, inputHeight
|
|
353
|
+
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Clears the view offset from the scene's camera.
|
|
360
|
+
*/
|
|
361
|
+
clearViewOffset() {
|
|
362
|
+
|
|
363
|
+
this.camera.clearViewOffset();
|
|
364
|
+
|
|
365
|
+
velocity.setProjectionMatrix( null );
|
|
366
|
+
|
|
367
|
+
// update jitter index
|
|
368
|
+
|
|
369
|
+
this._jitterIndex ++;
|
|
370
|
+
this._jitterIndex = this._jitterIndex % ( _haltonOffsets.length - 1 );
|
|
371
|
+
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* This method is used to render the effect once per frame.
|
|
376
|
+
*
|
|
377
|
+
* @param {NodeFrame} frame - The current node frame.
|
|
378
|
+
*/
|
|
379
|
+
updateBefore( frame ) {
|
|
380
|
+
|
|
381
|
+
const { renderer } = frame;
|
|
382
|
+
|
|
383
|
+
// store previous frame matrices before updating current ones
|
|
384
|
+
|
|
385
|
+
this._previousCameraWorldMatrix.value.copy( this._cameraWorldMatrix.value );
|
|
386
|
+
this._previousCameraProjectionMatrixInverse.value.copy( this._cameraProjectionMatrixInverse.value );
|
|
387
|
+
|
|
388
|
+
// update camera matrices uniforms
|
|
389
|
+
|
|
390
|
+
this._cameraNearFar.value.set( this.camera.near, this.camera.far );
|
|
391
|
+
this._cameraWorldMatrix.value.copy( this.camera.matrixWorld );
|
|
392
|
+
this._cameraWorldMatrixInverse.value.copy( this.camera.matrixWorldInverse );
|
|
393
|
+
this._cameraProjectionMatrixInverse.value.copy( this.camera.projectionMatrixInverse );
|
|
394
|
+
|
|
395
|
+
// extract input dimensions from the beauty buffer and output
|
|
396
|
+
// dimensions from the renderer's drawing buffer
|
|
397
|
+
|
|
398
|
+
const beautyRenderTarget = ( this.beautyNode.isRTTNode ) ? this.beautyNode.renderTarget : this.beautyNode.passNode.renderTarget;
|
|
399
|
+
|
|
400
|
+
const inputWidth = beautyRenderTarget.texture.width;
|
|
401
|
+
const inputHeight = beautyRenderTarget.texture.height;
|
|
402
|
+
|
|
403
|
+
const drawingBufferSize = renderer.getDrawingBufferSize( _size );
|
|
404
|
+
const outputWidth = drawingBufferSize.width;
|
|
405
|
+
const outputHeight = drawingBufferSize.height;
|
|
406
|
+
|
|
407
|
+
//
|
|
408
|
+
|
|
409
|
+
_rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
|
|
410
|
+
|
|
411
|
+
//
|
|
412
|
+
|
|
413
|
+
const needsRestart =
|
|
414
|
+
this._historyRenderTarget.width !== outputWidth ||
|
|
415
|
+
this._historyRenderTarget.height !== outputHeight;
|
|
416
|
+
|
|
417
|
+
this.setSize( outputWidth, outputHeight );
|
|
418
|
+
|
|
419
|
+
// every time the dimensions change we need fresh history data
|
|
420
|
+
|
|
421
|
+
if ( needsRestart === true ) {
|
|
422
|
+
|
|
423
|
+
// make sure render targets are initialized after the resize which triggers a dispose()
|
|
424
|
+
|
|
425
|
+
renderer.initRenderTarget( this._historyRenderTarget );
|
|
426
|
+
renderer.initRenderTarget( this._resolveRenderTarget );
|
|
427
|
+
|
|
428
|
+
// Seed the history with a bilinear upscale of the current beauty
|
|
429
|
+
// buffer. Without this the first frames after a resize fade in
|
|
430
|
+
// from black because the history target was cleared. The seed
|
|
431
|
+
// material is a quad pass that samples beauty at output UVs, so
|
|
432
|
+
// it produces an output-sized image regardless of the input size.
|
|
433
|
+
|
|
434
|
+
renderer.setRenderTarget( this._historyRenderTarget );
|
|
435
|
+
_quadMesh.material = this._seedMaterial;
|
|
436
|
+
_quadMesh.name = 'TAAU.seed';
|
|
437
|
+
_quadMesh.render( renderer );
|
|
438
|
+
renderer.setRenderTarget( null );
|
|
439
|
+
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// must run after needsRestart so it does not affect the seed reset
|
|
443
|
+
|
|
444
|
+
if ( this._needsPostProcessingSync === true ) {
|
|
445
|
+
|
|
446
|
+
this.setViewOffset( inputWidth, inputHeight );
|
|
447
|
+
|
|
448
|
+
this._needsPostProcessingSync = false;
|
|
449
|
+
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// resolve
|
|
453
|
+
|
|
454
|
+
renderer.setRenderTarget( this._resolveRenderTarget );
|
|
455
|
+
_quadMesh.material = this._resolveMaterial;
|
|
456
|
+
_quadMesh.name = 'TAAU';
|
|
457
|
+
_quadMesh.render( renderer );
|
|
458
|
+
renderer.setRenderTarget( null );
|
|
459
|
+
|
|
460
|
+
// update history
|
|
461
|
+
|
|
462
|
+
renderer.copyTextureToTexture( this._resolveRenderTarget.texture, this._historyRenderTarget.texture );
|
|
463
|
+
|
|
464
|
+
// Copy the current scene depth into the previous-depth texture. We
|
|
465
|
+
// keep the destination size locked to the source's actual dimensions
|
|
466
|
+
// so that any one-frame timing mismatch between the scene pass's depth
|
|
467
|
+
// attachment and the beauty render target's bookkeeping cannot
|
|
468
|
+
// produce a copy with mismatched extents (which WebGPU rejects for
|
|
469
|
+
// depth/stencil formats).
|
|
470
|
+
|
|
471
|
+
const currentDepth = this.depthNode.value;
|
|
472
|
+
const srcW = currentDepth.image !== null && currentDepth.image !== undefined ? currentDepth.image.width : 0;
|
|
473
|
+
const srcH = currentDepth.image !== null && currentDepth.image !== undefined ? currentDepth.image.height : 0;
|
|
474
|
+
|
|
475
|
+
if ( srcW > 0 && srcH > 0 ) {
|
|
476
|
+
|
|
477
|
+
if ( this._previousDepthRenderTarget.width !== srcW || this._previousDepthRenderTarget.height !== srcH ) {
|
|
478
|
+
|
|
479
|
+
this._previousDepthRenderTarget.setSize( srcW, srcH );
|
|
480
|
+
renderer.initRenderTarget( this._previousDepthRenderTarget );
|
|
481
|
+
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const dstDepth = this._previousDepthRenderTarget.depthTexture;
|
|
485
|
+
renderer.copyTextureToTexture( currentDepth, dstDepth );
|
|
486
|
+
this._previousDepthNode.value = dstDepth;
|
|
487
|
+
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// restore
|
|
491
|
+
|
|
492
|
+
RendererUtils.restoreRendererState( renderer, _rendererState );
|
|
493
|
+
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* This method is used to setup the effect's render targets and TSL code.
|
|
498
|
+
*
|
|
499
|
+
* @param {NodeBuilder} builder - The current node builder.
|
|
500
|
+
* @return {PassTextureNode}
|
|
501
|
+
*/
|
|
502
|
+
setup( builder ) {
|
|
503
|
+
|
|
504
|
+
const renderPipeline = builder.context.renderPipeline;
|
|
505
|
+
|
|
506
|
+
if ( renderPipeline ) {
|
|
507
|
+
|
|
508
|
+
this._needsPostProcessingSync = true;
|
|
509
|
+
|
|
510
|
+
renderPipeline.context.onBeforeRenderPipeline = () => {
|
|
511
|
+
|
|
512
|
+
const beautyRenderTarget = ( this.beautyNode.isRTTNode ) ? this.beautyNode.renderTarget : this.beautyNode.passNode.renderTarget;
|
|
513
|
+
|
|
514
|
+
const inputWidth = beautyRenderTarget.texture.width;
|
|
515
|
+
const inputHeight = beautyRenderTarget.texture.height;
|
|
516
|
+
|
|
517
|
+
this.setViewOffset( inputWidth, inputHeight );
|
|
518
|
+
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
renderPipeline.context.onAfterRenderPipeline = () => {
|
|
522
|
+
|
|
523
|
+
this.clearViewOffset();
|
|
524
|
+
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const currentDepthStruct = struct( {
|
|
530
|
+
|
|
531
|
+
closestDepth: 'float',
|
|
532
|
+
closestPositionTexel: 'vec2',
|
|
533
|
+
farthestDepth: 'float',
|
|
534
|
+
|
|
535
|
+
} );
|
|
536
|
+
|
|
537
|
+
// Samples 3×3 neighborhood pixels and returns the closest and farthest depths.
|
|
538
|
+
const sampleCurrentDepth = Fn( ( [ positionTexel ] ) => {
|
|
539
|
+
|
|
540
|
+
const closestDepth = float( 2 ).toVar();
|
|
541
|
+
const closestPositionTexel = vec2( 0 ).toVar();
|
|
542
|
+
const farthestDepth = float( - 1 ).toVar();
|
|
543
|
+
|
|
544
|
+
for ( let x = - 1; x <= 1; ++ x ) {
|
|
545
|
+
|
|
546
|
+
for ( let y = - 1; y <= 1; ++ y ) {
|
|
547
|
+
|
|
548
|
+
const neighbor = positionTexel.add( vec2( x, y ) ).toVar();
|
|
549
|
+
const depth = this.depthNode.load( neighbor ).r.toVar();
|
|
550
|
+
|
|
551
|
+
If( depth.lessThan( closestDepth ), () => {
|
|
552
|
+
|
|
553
|
+
closestDepth.assign( depth );
|
|
554
|
+
closestPositionTexel.assign( neighbor );
|
|
555
|
+
|
|
556
|
+
} );
|
|
557
|
+
|
|
558
|
+
If( depth.greaterThan( farthestDepth ), () => {
|
|
559
|
+
|
|
560
|
+
farthestDepth.assign( depth );
|
|
561
|
+
|
|
562
|
+
} );
|
|
563
|
+
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return currentDepthStruct( closestDepth, closestPositionTexel, farthestDepth );
|
|
569
|
+
|
|
570
|
+
} );
|
|
571
|
+
|
|
572
|
+
// Samples a previous depth and reproject it using the current camera matrices.
|
|
573
|
+
const samplePreviousDepth = ( uv ) => {
|
|
574
|
+
|
|
575
|
+
const depth = this._previousDepthNode.sample( uv ).r;
|
|
576
|
+
const positionView = getViewPosition( uv, depth, this._previousCameraProjectionMatrixInverse );
|
|
577
|
+
const positionWorld = this._previousCameraWorldMatrix.mul( vec4( positionView, 1 ) ).xyz;
|
|
578
|
+
const viewZ = this._cameraWorldMatrixInverse.mul( vec4( positionWorld, 1 ) ).z;
|
|
579
|
+
return viewZToPerspectiveDepth( viewZ, this._cameraNearFar.x, this._cameraNearFar.y );
|
|
580
|
+
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
// Optimized version of AABB clipping.
|
|
584
|
+
// Reference: https://github.com/playdeadgames/temporal
|
|
585
|
+
const clipAABB = Fn( ( [ currentColor, historyColor, minColor, maxColor ] ) => {
|
|
586
|
+
|
|
587
|
+
const pClip = maxColor.rgb.add( minColor.rgb ).mul( 0.5 );
|
|
588
|
+
const eClip = maxColor.rgb.sub( minColor.rgb ).mul( 0.5 ).add( 1e-7 );
|
|
589
|
+
const vClip = historyColor.sub( vec4( pClip, currentColor.a ) );
|
|
590
|
+
const vUnit = vClip.xyz.div( eClip );
|
|
591
|
+
const absUnit = vUnit.abs();
|
|
592
|
+
const maxUnit = max( absUnit.x, absUnit.y, absUnit.z );
|
|
593
|
+
return maxUnit.greaterThan( 1 ).select(
|
|
594
|
+
vec4( pClip, currentColor.a ).add( vClip.div( maxUnit ) ),
|
|
595
|
+
historyColor
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
} ).setLayout( {
|
|
599
|
+
name: 'clipAABB',
|
|
600
|
+
type: 'vec4',
|
|
601
|
+
inputs: [
|
|
602
|
+
{ name: 'currentColor', type: 'vec4' },
|
|
603
|
+
{ name: 'historyColor', type: 'vec4' },
|
|
604
|
+
{ name: 'minColor', type: 'vec4' },
|
|
605
|
+
{ name: 'maxColor', type: 'vec4' }
|
|
606
|
+
]
|
|
607
|
+
} );
|
|
608
|
+
|
|
609
|
+
// Flicker reduction based on luminance weighing.
|
|
610
|
+
const flickerReduction = Fn( ( [ currentColor, historyColor, currentWeight ] ) => {
|
|
611
|
+
|
|
612
|
+
const historyWeight = currentWeight.oneMinus();
|
|
613
|
+
const compressedCurrent = currentColor.mul( float( 1 ).div( ( max( currentColor.r, currentColor.g, currentColor.b ).add( 1 ) ) ) );
|
|
614
|
+
const compressedHistory = historyColor.mul( float( 1 ).div( ( max( historyColor.r, historyColor.g, historyColor.b ).add( 1 ) ) ) );
|
|
615
|
+
|
|
616
|
+
const luminanceCurrent = luminance( compressedCurrent.rgb );
|
|
617
|
+
const luminanceHistory = luminance( compressedHistory.rgb );
|
|
618
|
+
|
|
619
|
+
currentWeight.mulAssign( float( 1 ).div( luminanceCurrent.add( 1 ) ) );
|
|
620
|
+
historyWeight.mulAssign( float( 1 ).div( luminanceHistory.add( 1 ) ) );
|
|
621
|
+
|
|
622
|
+
return add( currentColor.mul( currentWeight ), historyColor.mul( historyWeight ) ).div( max( currentWeight.add( historyWeight ), 0.00001 ) ).toVar();
|
|
623
|
+
|
|
624
|
+
} );
|
|
625
|
+
|
|
626
|
+
const historyNode = texture( this._historyRenderTarget.textures[ 0 ] );
|
|
627
|
+
const lockNode = texture( this._historyRenderTarget.textures[ 1 ] );
|
|
628
|
+
|
|
629
|
+
// --- TAAU resolve ---
|
|
630
|
+
//
|
|
631
|
+
// For each output pixel, we map its position into input-pixel space,
|
|
632
|
+
// find the closest jittered input sample, and reconstruct the current
|
|
633
|
+
// color as a weighted sum of the 3×3 neighborhood around that sample.
|
|
634
|
+
// Each tap's weight is a Gaussian approximation of a Blackman-Harris
|
|
635
|
+
// window evaluated at the distance between the tap's (jittered)
|
|
636
|
+
// sample center and the output pixel center. The same neighborhood
|
|
637
|
+
// also supplies the moments used for variance clipping of the
|
|
638
|
+
// reprojected history, so no second neighborhood read is needed.
|
|
639
|
+
|
|
640
|
+
const colorOutput = property( 'vec4' );
|
|
641
|
+
const lockOutput = property( 'vec4' );
|
|
642
|
+
|
|
643
|
+
const outputNode = outputStruct( colorOutput, lockOutput );
|
|
644
|
+
|
|
645
|
+
const resolve = Fn( () => {
|
|
646
|
+
|
|
647
|
+
const uvNode = uv();
|
|
648
|
+
const inputSize = this.beautyNode.size(); // ivec2
|
|
649
|
+
const inputSizeF = vec2( inputSize );
|
|
650
|
+
|
|
651
|
+
// output pixel center in input-pixel coordinates
|
|
652
|
+
|
|
653
|
+
const pIn = uvNode.mul( inputSizeF );
|
|
654
|
+
|
|
655
|
+
// the input sample at integer texel (m, n) was rendered at world
|
|
656
|
+
// position (m + 0.5 + jitter). Solving for the closest tap gives:
|
|
657
|
+
|
|
658
|
+
const closestTapF = pIn.sub( vec2( 0.5 ).add( this._jitterOffset ) ).round();
|
|
659
|
+
const closestTap = ivec2( closestTapF );
|
|
660
|
+
|
|
661
|
+
// depth dilation around the closest input tap
|
|
662
|
+
|
|
663
|
+
const currentDepth = sampleCurrentDepth( closestTapF );
|
|
664
|
+
const closestDepth = currentDepth.get( 'closestDepth' );
|
|
665
|
+
const closestPositionTexel = currentDepth.get( 'closestPositionTexel' );
|
|
666
|
+
const farthestDepth = currentDepth.get( 'farthestDepth' );
|
|
667
|
+
|
|
668
|
+
// reproject using the velocity sampled at the dilated depth tap
|
|
669
|
+
|
|
670
|
+
const offsetUV = this.velocityNode.load( closestPositionTexel ).xy.mul( vec2( 0.5, - 0.5 ) );
|
|
671
|
+
const historyUV = uvNode.sub( offsetUV );
|
|
672
|
+
const previousDepth = samplePreviousDepth( historyUV );
|
|
673
|
+
|
|
674
|
+
// history validity
|
|
675
|
+
|
|
676
|
+
const isValidUV = historyUV.greaterThanEqual( 0 ).all().and( historyUV.lessThanEqual( 1 ).all() );
|
|
677
|
+
const isEdge = farthestDepth.sub( closestDepth ).greaterThan( this.edgeDepthDiff );
|
|
678
|
+
const isDisocclusion = closestDepth.sub( previousDepth ).greaterThan( this.depthThreshold );
|
|
679
|
+
const hasValidHistory = isValidUV.and( isEdge.or( isDisocclusion.not() ) );
|
|
680
|
+
|
|
681
|
+
// 9-tap Blackman-Harris (Gaussian approximation) reconstruction
|
|
682
|
+
// of the current frame color, plus moment accumulation for the
|
|
683
|
+
// variance clip of the history.
|
|
684
|
+
|
|
685
|
+
const sumColor = vec4( 0 ).toVar();
|
|
686
|
+
const sumWeight = float( 0 ).toVar();
|
|
687
|
+
const moment1 = vec4( 0 ).toVar();
|
|
688
|
+
const moment2 = vec4( 0 ).toVar();
|
|
689
|
+
|
|
690
|
+
const offsets = [
|
|
691
|
+
[ - 1, - 1 ], [ 0, - 1 ], [ 1, - 1 ],
|
|
692
|
+
[ - 1, 0 ], [ 0, 0 ], [ 1, 0 ],
|
|
693
|
+
[ - 1, 1 ], [ 0, 1 ], [ 1, 1 ]
|
|
694
|
+
];
|
|
695
|
+
|
|
696
|
+
for ( const [ x, y ] of offsets ) {
|
|
697
|
+
|
|
698
|
+
const tap = closestTap.add( ivec2( x, y ) );
|
|
699
|
+
const tapCenter = vec2( tap ).add( vec2( 0.5 ).add( this._jitterOffset ) );
|
|
700
|
+
const delta = pIn.sub( tapCenter );
|
|
701
|
+
const d2 = delta.dot( delta );
|
|
702
|
+
const w = exp( d2.mul( - 2.29 ) );
|
|
703
|
+
|
|
704
|
+
// Use max() to prevent NaN values from propagating.
|
|
705
|
+
const c = this.beautyNode.load( tap ).max( 0 );
|
|
706
|
+
|
|
707
|
+
sumColor.addAssign( c.mul( w ) );
|
|
708
|
+
sumWeight.addAssign( w );
|
|
709
|
+
|
|
710
|
+
moment1.addAssign( c );
|
|
711
|
+
moment2.addAssign( c.pow2() );
|
|
712
|
+
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const currentColor = sumColor.div( sumWeight.max( 1e-5 ) );
|
|
716
|
+
|
|
717
|
+
// variance clipping using the moments we just gathered
|
|
718
|
+
|
|
719
|
+
const N = float( offsets.length );
|
|
720
|
+
const mean = moment1.div( N );
|
|
721
|
+
const motionFactor = uvNode.sub( historyUV ).mul( inputSizeF ).length().div( this.maxVelocityLength ).saturate();
|
|
722
|
+
const varianceGamma = mix( 0.5, 1, motionFactor.oneMinus().pow2() );
|
|
723
|
+
const variance = moment2.div( N ).sub( mean.pow2() ).max( 0 ).sqrt().mul( varianceGamma );
|
|
724
|
+
const minColor = mean.sub( variance );
|
|
725
|
+
const maxColor = mean.add( variance );
|
|
726
|
+
|
|
727
|
+
const historyColor = historyNode.sample( historyUV );
|
|
728
|
+
const clippedHistoryColor = clipAABB( mean.clamp( minColor, maxColor ), historyColor, minColor, maxColor );
|
|
729
|
+
|
|
730
|
+
// Current weight. Under TAAU a single input frame covers less of
|
|
731
|
+
// the output grid, so the baseline current weight is lower than
|
|
732
|
+
// in standard TRAA to give the accumulator more frames to fill
|
|
733
|
+
// in sub-pixel detail. Motion still biases toward the current
|
|
734
|
+
// frame to keep disoccluded and fast-moving pixels responsive.
|
|
735
|
+
|
|
736
|
+
const currentLuma = luminance( currentColor.rgb );
|
|
737
|
+
const meanLuma = luminance( mean.rgb ).toConst();
|
|
738
|
+
const thinFeature = currentLuma.sub( meanLuma ).abs().div( meanLuma ).smoothstep( 0, 0.2 );
|
|
739
|
+
|
|
740
|
+
// Gate the lock by a two-sided depth change check. The
|
|
741
|
+
// existing `isDisocclusion` is one-sided (only fires when
|
|
742
|
+
// the scene moves farther), but new geometry appearing
|
|
743
|
+
// closer also makes the history stale.
|
|
744
|
+
const isDepthChanged = closestDepth.sub( previousDepth ).abs().greaterThan( this.depthThreshold );
|
|
745
|
+
const canLock = isValidUV.and( isDepthChanged.not() );
|
|
746
|
+
const gatedThinFeature = canLock.select( thinFeature, float( 0 ) );
|
|
747
|
+
|
|
748
|
+
const decay = isDisocclusion.select( 0, 0.5 );
|
|
749
|
+
const lock = max( gatedThinFeature, lockNode.r.mul( decay ) ).saturate();
|
|
750
|
+
const lockedHistoryColor = mix( clippedHistoryColor, historyColor, lock );
|
|
751
|
+
|
|
752
|
+
const currentWeight = float( this.currentFrameWeight ).toVar();
|
|
753
|
+
currentWeight.assign( hasValidHistory.select( currentWeight.add( motionFactor ).saturate(), 1 ) );
|
|
754
|
+
|
|
755
|
+
const output = flickerReduction( currentColor, lockedHistoryColor, currentWeight );
|
|
756
|
+
|
|
757
|
+
colorOutput.assign( output );
|
|
758
|
+
lockOutput.assign( lock );
|
|
759
|
+
|
|
760
|
+
return vec4( 0 ); // temporary solution until TSL does not complain anymore
|
|
761
|
+
|
|
762
|
+
} );
|
|
763
|
+
|
|
764
|
+
// materials
|
|
765
|
+
|
|
766
|
+
this._resolveMaterial.colorNode = resolve();
|
|
767
|
+
this._resolveMaterial.outputNode = outputNode;
|
|
768
|
+
|
|
769
|
+
this._seedMaterial.colorNode = Fn( () => {
|
|
770
|
+
|
|
771
|
+
colorOutput.assign( this.beautyNode.sample( uv() ) );
|
|
772
|
+
lockOutput.assign( 0 );
|
|
773
|
+
|
|
774
|
+
return vec4( 0 );
|
|
775
|
+
|
|
776
|
+
} )();
|
|
777
|
+
|
|
778
|
+
this._seedMaterial.outputNode = outputNode;
|
|
779
|
+
|
|
780
|
+
return this._textureNode;
|
|
781
|
+
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Frees internal resources. This method should be called
|
|
786
|
+
* when the effect is no longer required.
|
|
787
|
+
*/
|
|
788
|
+
dispose() {
|
|
789
|
+
|
|
790
|
+
this._historyRenderTarget.dispose();
|
|
791
|
+
this._resolveRenderTarget.dispose();
|
|
792
|
+
this._previousDepthRenderTarget.dispose();
|
|
793
|
+
|
|
794
|
+
this._resolveMaterial.dispose();
|
|
795
|
+
this._seedMaterial.dispose();
|
|
796
|
+
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
export default TAAUNode;
|
|
802
|
+
|
|
803
|
+
function _halton( index, base ) {
|
|
804
|
+
|
|
805
|
+
let fraction = 1;
|
|
806
|
+
let result = 0;
|
|
807
|
+
while ( index > 0 ) {
|
|
808
|
+
|
|
809
|
+
fraction /= base;
|
|
810
|
+
result += fraction * ( index % base );
|
|
811
|
+
index = Math.floor( index / base );
|
|
812
|
+
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
return result;
|
|
816
|
+
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const _haltonOffsets = /*@__PURE__*/ Array.from(
|
|
820
|
+
{ length: 32 },
|
|
821
|
+
( _, index ) => [ _halton( index + 1, 2 ), _halton( index + 1, 3 ) ]
|
|
822
|
+
);
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* TSL function for creating a TAAU node for Temporal Anti-Aliasing Upscaling.
|
|
826
|
+
*
|
|
827
|
+
* @tsl
|
|
828
|
+
* @function
|
|
829
|
+
* @param {TextureNode} beautyNode - The texture node that represents the input of the effect.
|
|
830
|
+
* @param {TextureNode} depthNode - A node that represents the scene's depth.
|
|
831
|
+
* @param {TextureNode} velocityNode - A node that represents the scene's velocity.
|
|
832
|
+
* @param {Camera} camera - The camera the scene is rendered with.
|
|
833
|
+
* @returns {TAAUNode}
|
|
834
|
+
*/
|
|
835
|
+
export const taau = ( beautyNode, depthNode, velocityNode, camera ) => new TAAUNode( convertToTexture( beautyNode ), depthNode, velocityNode, camera );
|