|
|
@@ -1,5 +1,5 @@
|
|
|
-import { Color, Vector2, NearestFilter, Matrix4, RendererUtils, PassNode, QuadMesh, NodeMaterial } from 'three/webgpu';
|
|
|
-import { add, float, If, Loop, int, Fn, min, max, clamp, nodeObject, texture, uniform, uv, vec2, vec4, luminance } from 'three/tsl';
|
|
|
+import { HalfFloatType, Vector2, RenderTarget, RendererUtils, QuadMesh, NodeMaterial, TempNode, NodeUpdateType, Matrix4 } from 'three/webgpu';
|
|
|
+import { add, float, If, Loop, int, Fn, min, max, clamp, nodeObject, texture, uniform, uv, vec2, vec4, luminance, convertToTexture, passTexture, velocity } from 'three/tsl';
|
|
|
|
|
|
const _quadMesh = /*@__PURE__*/ new QuadMesh();
|
|
|
const _size = /*@__PURE__*/ new Vector2();
|
|
|
@@ -8,34 +8,34 @@ let _rendererState;
|
|
|
|
|
|
|
|
|
/**
|
|
|
- * A special render pass node that renders the scene with TRAA (Temporal Reprojection Anti-Aliasing).
|
|
|
- *
|
|
|
- * Note: The current implementation does not yet support MRT setups.
|
|
|
+ * A special node that applies TRAA (Temporal Reprojection Anti-Aliasing).
|
|
|
*
|
|
|
* References:
|
|
|
* - {@link https://alextardif.com/TAA.html}
|
|
|
* - {@link https://www.elopezr.com/temporal-aa-and-the-quest-for-the-holy-trail/}
|
|
|
*
|
|
|
* @augments PassNode
|
|
|
- * @three_import import { traaPass } from 'three/addons/tsl/display/TRAAPassNode.js';
|
|
|
+ * @three_import import { traa } from 'three/addons/tsl/display/TRAANode.js';
|
|
|
*/
|
|
|
-class TRAAPassNode extends PassNode {
|
|
|
+class TRAANode extends TempNode {
|
|
|
|
|
|
static get type() {
|
|
|
|
|
|
- return 'TRAAPassNode';
|
|
|
+ return 'TRAANode';
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Constructs a new TRAA pass node.
|
|
|
+ * Constructs a new TRAA node.
|
|
|
*
|
|
|
- * @param {Scene} scene - The scene to render.
|
|
|
- * @param {Camera} camera - The camera to render the scene with.
|
|
|
+ * @param {TextureNode} beautyNode - The texture node that represents the input of the effect.
|
|
|
+ * @param {TextureNode} depthNode - A node that represents the scene's depth.
|
|
|
+ * @param {TextureNode} velocityNode - A node that represents the scene's velocity.
|
|
|
+ * @param {Camera} camera - The camera the scene is rendered with.
|
|
|
*/
|
|
|
- constructor( scene, camera ) {
|
|
|
+ constructor( beautyNode, depthNode, velocityNode, camera ) {
|
|
|
|
|
|
- super( PassNode.COLOR, scene, camera );
|
|
|
+ super( 'vec4' );
|
|
|
|
|
|
/**
|
|
|
* This flag can be used for type testing.
|
|
|
@@ -44,40 +44,53 @@ class TRAAPassNode extends PassNode {
|
|
|
* @readonly
|
|
|
* @default true
|
|
|
*/
|
|
|
- this.isTRAAPassNode = true;
|
|
|
+ this.isTRAANode = true;
|
|
|
|
|
|
/**
|
|
|
- * The clear color of the pass.
|
|
|
+ * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node renders
|
|
|
+ * its effect once per frame in `updateBefore()`.
|
|
|
*
|
|
|
- * @type {Color}
|
|
|
- * @default 0x000000
|
|
|
+ * @type {string}
|
|
|
+ * @default 'frame'
|
|
|
*/
|
|
|
- this.clearColor = new Color( 0x000000 );
|
|
|
+ this.updateBeforeType = NodeUpdateType.FRAME;
|
|
|
|
|
|
/**
|
|
|
- * The clear alpha of the pass.
|
|
|
+ * The texture node that represents the input of the effect.
|
|
|
*
|
|
|
- * @type {number}
|
|
|
- * @default 0
|
|
|
+ * @type {TextureNode}
|
|
|
*/
|
|
|
- this.clearAlpha = 0;
|
|
|
+ this.beautyNode = beautyNode;
|
|
|
|
|
|
/**
|
|
|
- * The jitter index selects the current camera offset value.
|
|
|
+ * A node that represents the scene's velocity.
|
|
|
*
|
|
|
- * @private
|
|
|
- * @type {number}
|
|
|
- * @default 0
|
|
|
+ * @type {TextureNode}
|
|
|
*/
|
|
|
- this._jitterIndex = 0;
|
|
|
+ this.depthNode = depthNode;
|
|
|
|
|
|
/**
|
|
|
- * Used to save the original/unjittered projection matrix.
|
|
|
+ * A node that represents the scene's velocity.
|
|
|
+ *
|
|
|
+ * @type {TextureNode}
|
|
|
+ */
|
|
|
+ this.velocityNode = velocityNode;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The camera the scene is rendered with.
|
|
|
+ *
|
|
|
+ * @type {TextureNode}
|
|
|
+ */
|
|
|
+ this.camera = camera;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The jitter index selects the current camera offset value.
|
|
|
*
|
|
|
* @private
|
|
|
- * @type {Matrix4}
|
|
|
+ * @type {number}
|
|
|
+ * @default 0
|
|
|
*/
|
|
|
- this._originalProjectionMatrix = new Matrix4();
|
|
|
+ this._jitterIndex = 0;
|
|
|
|
|
|
/**
|
|
|
* A uniform node holding the inverse resolution value.
|
|
|
@@ -88,22 +101,22 @@ class TRAAPassNode extends PassNode {
|
|
|
this._invSize = uniform( new Vector2() );
|
|
|
|
|
|
/**
|
|
|
- * The render target that holds the current sample.
|
|
|
+ * The render target that represents the history of frame data.
|
|
|
*
|
|
|
* @private
|
|
|
* @type {?RenderTarget}
|
|
|
- * @default null
|
|
|
*/
|
|
|
- this._sampleRenderTarget = null;
|
|
|
+ this._historyRenderTarget = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
|
|
|
+ this._historyRenderTarget.texture.name = 'TRAANode.history';
|
|
|
|
|
|
/**
|
|
|
- * The render target that represents the history of frame data.
|
|
|
+ * The render target for the resolve.
|
|
|
*
|
|
|
* @private
|
|
|
* @type {?RenderTarget}
|
|
|
- * @default null
|
|
|
*/
|
|
|
- this._historyRenderTarget = null;
|
|
|
+ this._resolveRenderTarget = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
|
|
|
+ this._resolveRenderTarget.texture.name = 'TRAANode.resolve';
|
|
|
|
|
|
/**
|
|
|
* Material used for the resolve step.
|
|
|
@@ -112,7 +125,41 @@ class TRAAPassNode extends PassNode {
|
|
|
* @type {NodeMaterial}
|
|
|
*/
|
|
|
this._resolveMaterial = new NodeMaterial();
|
|
|
- this._resolveMaterial.name = 'TRAA.Resolve';
|
|
|
+ this._resolveMaterial.name = 'TRAA.resolve';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The result of the effect is represented as a separate texture node.
|
|
|
+ *
|
|
|
+ * @private
|
|
|
+ * @type {PassTextureNode}
|
|
|
+ */
|
|
|
+ this._textureNode = passTexture( this, this._resolveRenderTarget.texture );
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Used to save the original/unjittered projection matrix.
|
|
|
+ *
|
|
|
+ * @private
|
|
|
+ * @type {Matrix4}
|
|
|
+ */
|
|
|
+ this._originalProjectionMatrix = new Matrix4();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Sync the post processing stack with the TRAA node.
|
|
|
+ * @private
|
|
|
+ * @type {boolean}
|
|
|
+ */
|
|
|
+ this._needsPostProcessingSync = false;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the result of the effect as a texture node.
|
|
|
+ *
|
|
|
+ * @return {PassTextureNode} A texture node that represents the result of the effect.
|
|
|
+ */
|
|
|
+ getTextureNode() {
|
|
|
+
|
|
|
+ return this._textureNode;
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -121,78 +168,48 @@ class TRAAPassNode extends PassNode {
|
|
|
*
|
|
|
* @param {number} width - The width of the effect.
|
|
|
* @param {number} height - The height of the effect.
|
|
|
- * @return {boolean} Whether the TRAA needs a restart or not. That is required after a resize since buffer data with different sizes can't be resolved.
|
|
|
*/
|
|
|
setSize( width, height ) {
|
|
|
|
|
|
- super.setSize( width, height );
|
|
|
-
|
|
|
- let needsRestart = false;
|
|
|
-
|
|
|
- if ( this.renderTarget.width !== this._sampleRenderTarget.width || this.renderTarget.height !== this._sampleRenderTarget.height ) {
|
|
|
-
|
|
|
- this._sampleRenderTarget.setSize( this.renderTarget.width, this.renderTarget.height );
|
|
|
- this._historyRenderTarget.setSize( this.renderTarget.width, this.renderTarget.height );
|
|
|
-
|
|
|
- this._invSize.value.set( 1 / this.renderTarget.width, 1 / this.renderTarget.height );
|
|
|
-
|
|
|
- needsRestart = true;
|
|
|
-
|
|
|
- }
|
|
|
+ this._historyRenderTarget.setSize( width, height );
|
|
|
+ this._resolveRenderTarget.setSize( width, height );
|
|
|
|
|
|
- return needsRestart;
|
|
|
+ this._invSize.value.set( 1 / width, 1 / height );
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * This method is used to render the effect once per frame.
|
|
|
+ * Defines the TRAA's current jitter as a view offset
|
|
|
+ * to the scene's camera.
|
|
|
*
|
|
|
- * @param {NodeFrame} frame - The current node frame.
|
|
|
+ * @param {number} width - The width of the effect.
|
|
|
+ * @param {number} height - The height of the effect.
|
|
|
*/
|
|
|
- updateBefore( frame ) {
|
|
|
-
|
|
|
- const { renderer } = frame;
|
|
|
- const { scene, camera } = this;
|
|
|
-
|
|
|
- _rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
|
|
|
-
|
|
|
- //
|
|
|
-
|
|
|
- this._pixelRatio = renderer.getPixelRatio();
|
|
|
- const size = renderer.getSize( _size );
|
|
|
-
|
|
|
- const needsRestart = this.setSize( size.width, size.height );
|
|
|
+ setViewOffset( width, height ) {
|
|
|
|
|
|
// save original/unjittered projection matrix for velocity pass
|
|
|
|
|
|
- camera.updateProjectionMatrix();
|
|
|
- this._originalProjectionMatrix.copy( camera.projectionMatrix );
|
|
|
+ this.camera.updateProjectionMatrix();
|
|
|
+ this._originalProjectionMatrix.copy( this.camera.projectionMatrix );
|
|
|
|
|
|
- // camera configuration
|
|
|
+ velocity.setProjectionMatrix( this._originalProjectionMatrix );
|
|
|
|
|
|
- this._cameraNear.value = camera.near;
|
|
|
- this._cameraFar.value = camera.far;
|
|
|
-
|
|
|
- // configure jitter as view offset
|
|
|
+ //
|
|
|
|
|
|
const viewOffset = {
|
|
|
|
|
|
- fullWidth: this.renderTarget.width,
|
|
|
- fullHeight: this.renderTarget.height,
|
|
|
+ fullWidth: width,
|
|
|
+ fullHeight: height,
|
|
|
offsetX: 0,
|
|
|
offsetY: 0,
|
|
|
- width: this.renderTarget.width,
|
|
|
- height: this.renderTarget.height
|
|
|
+ width: width,
|
|
|
+ height: height
|
|
|
|
|
|
};
|
|
|
|
|
|
- const originalViewOffset = Object.assign( {}, camera.view );
|
|
|
-
|
|
|
- if ( originalViewOffset.enabled ) Object.assign( viewOffset, originalViewOffset );
|
|
|
-
|
|
|
const jitterOffset = _JitterVectors[ this._jitterIndex ];
|
|
|
|
|
|
- camera.setViewOffset(
|
|
|
+ this.camera.setViewOffset(
|
|
|
|
|
|
viewOffset.fullWidth, viewOffset.fullHeight,
|
|
|
|
|
|
@@ -202,95 +219,88 @@ class TRAAPassNode extends PassNode {
|
|
|
|
|
|
);
|
|
|
|
|
|
- // configure velocity
|
|
|
-
|
|
|
- const mrt = this.getMRT();
|
|
|
- const velocityOutput = mrt.get( 'velocity' );
|
|
|
-
|
|
|
- if ( velocityOutput !== undefined ) {
|
|
|
-
|
|
|
- velocityOutput.setProjectionMatrix( this._originalProjectionMatrix );
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- throw new Error( 'THREE:TRAAPassNode: Missing velocity output in MRT configuration.' );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // render sample
|
|
|
+ }
|
|
|
|
|
|
- renderer.setMRT( mrt );
|
|
|
+ /**
|
|
|
+ * Clears the view offset from the scene's camera.
|
|
|
+ */
|
|
|
+ clearViewOffset() {
|
|
|
|
|
|
- renderer.setClearColor( this.clearColor, this.clearAlpha );
|
|
|
- renderer.setRenderTarget( this._sampleRenderTarget );
|
|
|
- renderer.render( scene, camera );
|
|
|
+ this.camera.clearViewOffset();
|
|
|
|
|
|
- renderer.setRenderTarget( null );
|
|
|
- renderer.setMRT( null );
|
|
|
+ velocity.setProjectionMatrix( null );
|
|
|
|
|
|
- // every time when the dimensions change we need fresh history data. Copy the sample
|
|
|
- // into the history and final render target (no AA happens at that point).
|
|
|
+ // update jitter index
|
|
|
|
|
|
- if ( needsRestart === true ) {
|
|
|
+ this._jitterIndex ++;
|
|
|
+ this._jitterIndex = this._jitterIndex % ( _JitterVectors.length - 1 );
|
|
|
|
|
|
- // bind and clear render target to make sure they are initialized after the resize which triggers a dispose()
|
|
|
+ }
|
|
|
|
|
|
- renderer.setRenderTarget( this._historyRenderTarget );
|
|
|
- renderer.clear();
|
|
|
+ /**
|
|
|
+ * This method is used to render the effect once per frame.
|
|
|
+ *
|
|
|
+ * @param {NodeFrame} frame - The current node frame.
|
|
|
+ */
|
|
|
+ updateBefore( frame ) {
|
|
|
|
|
|
- renderer.setRenderTarget( this.renderTarget );
|
|
|
- renderer.clear();
|
|
|
+ const { renderer } = frame;
|
|
|
|
|
|
- renderer.setRenderTarget( null );
|
|
|
+ // keep the TRAA in sync with the dimensions of the beauty node
|
|
|
|
|
|
- renderer.copyTextureToTexture( this._sampleRenderTarget.texture, this._historyRenderTarget.texture );
|
|
|
- renderer.copyTextureToTexture( this._sampleRenderTarget.texture, this.renderTarget.texture );
|
|
|
+ const beautyRenderTarget = ( this.beautyNode.isRTTNode ) ? this.beautyNode.renderTarget : this.beautyNode.passNode.renderTarget;
|
|
|
|
|
|
- } else {
|
|
|
+ const width = beautyRenderTarget.texture.width;
|
|
|
+ const height = beautyRenderTarget.texture.height;
|
|
|
|
|
|
- // resolve
|
|
|
+ //
|
|
|
|
|
|
- renderer.setRenderTarget( this.renderTarget );
|
|
|
- _quadMesh.material = this._resolveMaterial;
|
|
|
- _quadMesh.render( renderer );
|
|
|
- renderer.setRenderTarget( null );
|
|
|
+ if ( this._needsPostProcessingSync === true ) {
|
|
|
|
|
|
- // update history
|
|
|
+ this.setViewOffset( width, height );
|
|
|
|
|
|
- renderer.copyTextureToTexture( this.renderTarget.texture, this._historyRenderTarget.texture );
|
|
|
+ this._needsPostProcessingSync = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
- // copy depth
|
|
|
+ _rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
|
|
|
+
|
|
|
+ //
|
|
|
|
|
|
- renderer.copyTextureToTexture( this._sampleRenderTarget.depthTexture, this.renderTarget.depthTexture );
|
|
|
+ const needsRestart = this._historyRenderTarget.width !== width || this._historyRenderTarget.height !== height;
|
|
|
+ this.setSize( width, height );
|
|
|
|
|
|
- // update jitter index
|
|
|
+ // every time when the dimensions change we need fresh history data
|
|
|
|
|
|
- this._jitterIndex ++;
|
|
|
- this._jitterIndex = this._jitterIndex % ( _JitterVectors.length - 1 );
|
|
|
+ if ( needsRestart === true ) {
|
|
|
|
|
|
- // restore
|
|
|
+ // bind and clear render target to make sure they are initialized after the resize which triggers a dispose()
|
|
|
|
|
|
- if ( originalViewOffset.enabled ) {
|
|
|
+ renderer.setRenderTarget( this._historyRenderTarget );
|
|
|
+ renderer.clear();
|
|
|
|
|
|
- camera.setViewOffset(
|
|
|
+ renderer.setRenderTarget( this._resolveRenderTarget );
|
|
|
+ renderer.clear();
|
|
|
|
|
|
- originalViewOffset.fullWidth, originalViewOffset.fullHeight,
|
|
|
+ // make sure to reset the history with the contents of the beauty buffer otherwise subsequent frames after the
|
|
|
+ // resize will fade from a darker color to the correct one because the history was cleared with black.
|
|
|
|
|
|
- originalViewOffset.offsetX, originalViewOffset.offsetY,
|
|
|
+ renderer.copyTextureToTexture( beautyRenderTarget.texture, this._historyRenderTarget.texture );
|
|
|
|
|
|
- originalViewOffset.width, originalViewOffset.height
|
|
|
+ }
|
|
|
|
|
|
- );
|
|
|
+ // resolve
|
|
|
|
|
|
- } else {
|
|
|
+ renderer.setRenderTarget( this._resolveRenderTarget );
|
|
|
+ _quadMesh.material = this._resolveMaterial;
|
|
|
+ _quadMesh.render( renderer );
|
|
|
+ renderer.setRenderTarget( null );
|
|
|
|
|
|
- camera.clearViewOffset();
|
|
|
+ // update history
|
|
|
|
|
|
- }
|
|
|
+ renderer.copyTextureToTexture( this._resolveRenderTarget.texture, this._historyRenderTarget.texture );
|
|
|
|
|
|
- velocityOutput.setProjectionMatrix( null );
|
|
|
+ // restore
|
|
|
|
|
|
RendererUtils.restoreRendererState( renderer, _rendererState );
|
|
|
|
|
|
@@ -304,28 +314,31 @@ class TRAAPassNode extends PassNode {
|
|
|
*/
|
|
|
setup( builder ) {
|
|
|
|
|
|
- if ( this._sampleRenderTarget === null ) {
|
|
|
+ const postProcessing = builder.context.postProcessing;
|
|
|
|
|
|
- this._sampleRenderTarget = this.renderTarget.clone();
|
|
|
- this._historyRenderTarget = this.renderTarget.clone();
|
|
|
+ if ( postProcessing ) {
|
|
|
|
|
|
- this._sampleRenderTarget.texture.minFiler = NearestFilter;
|
|
|
- this._sampleRenderTarget.texture.magFilter = NearestFilter;
|
|
|
+ this._needsPostProcessingSync = true;
|
|
|
|
|
|
- const velocityTarget = this._sampleRenderTarget.texture.clone();
|
|
|
- velocityTarget.isRenderTargetTexture = true;
|
|
|
- velocityTarget.name = 'velocity';
|
|
|
+ postProcessing.context.onBeforePostProcessing = () => {
|
|
|
|
|
|
- this._sampleRenderTarget.textures.push( velocityTarget ); // for MRT
|
|
|
+ const size = builder.renderer.getDrawingBufferSize( _size );
|
|
|
+ this.setViewOffset( size.width, size.height );
|
|
|
|
|
|
- }
|
|
|
+ };
|
|
|
|
|
|
- // textures
|
|
|
+ postProcessing.context.onAfterPostProcessing = () => {
|
|
|
+
|
|
|
+ this.clearViewOffset();
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
const historyTexture = texture( this._historyRenderTarget.texture );
|
|
|
- const sampleTexture = texture( this._sampleRenderTarget.textures[ 0 ] );
|
|
|
- const velocityTexture = texture( this._sampleRenderTarget.textures[ 1 ] );
|
|
|
- const depthTexture = texture( this._sampleRenderTarget.depthTexture );
|
|
|
+ const sampleTexture = this.beautyNode;
|
|
|
+ const depthTexture = this.depthNode;
|
|
|
+ const velocityTexture = this.velocityNode;
|
|
|
|
|
|
const resolve = Fn( () => {
|
|
|
|
|
|
@@ -395,9 +408,9 @@ class TRAAPassNode extends PassNode {
|
|
|
|
|
|
// materials
|
|
|
|
|
|
- this._resolveMaterial.fragmentNode = resolve();
|
|
|
+ this._resolveMaterial.colorNode = resolve();
|
|
|
|
|
|
- return super.setup( builder );
|
|
|
+ return this._textureNode;
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -407,14 +420,8 @@ class TRAAPassNode extends PassNode {
|
|
|
*/
|
|
|
dispose() {
|
|
|
|
|
|
- super.dispose();
|
|
|
-
|
|
|
- if ( this._sampleRenderTarget !== null ) {
|
|
|
-
|
|
|
- this._sampleRenderTarget.dispose();
|
|
|
- this._historyRenderTarget.dispose();
|
|
|
-
|
|
|
- }
|
|
|
+ this._historyRenderTarget.dispose();
|
|
|
+ this._resolveRenderTarget.dispose();
|
|
|
|
|
|
this._resolveMaterial.dispose();
|
|
|
|
|
|
@@ -422,7 +429,7 @@ class TRAAPassNode extends PassNode {
|
|
|
|
|
|
}
|
|
|
|
|
|
-export default TRAAPassNode;
|
|
|
+export default TRAANode;
|
|
|
|
|
|
// These jitter vectors are specified in integers because it is easier.
|
|
|
// I am assuming a [-8,8) integer grid, but it needs to be mapped onto [-0.5,0.5)
|
|
|
@@ -441,12 +448,14 @@ const _JitterVectors = [
|
|
|
];
|
|
|
|
|
|
/**
|
|
|
- * TSL function for creating a TRAA pass node for Temporal Reprojection Anti-Aliasing.
|
|
|
+ * TSL function for creating a TRAA node for Temporal Reprojection Anti-Aliasing.
|
|
|
*
|
|
|
* @tsl
|
|
|
* @function
|
|
|
- * @param {Scene} scene - The scene to render.
|
|
|
- * @param {Camera} camera - The camera to render the scene with.
|
|
|
+ * @param {TextureNode} beautyNode - The texture node that represents the input of the effect.
|
|
|
+ * @param {TextureNode} depthNode - A node that represents the scene's depth.
|
|
|
+ * @param {TextureNode} velocityNode - A node that represents the scene's velocity.
|
|
|
+ * @param {Camera} camera - The camera the scene is rendered with.
|
|
|
* @returns {TRAAPassNode}
|
|
|
*/
|
|
|
-export const traaPass = ( scene, camera ) => nodeObject( new TRAAPassNode( scene, camera ) );
|
|
|
+export const traa = ( beautyNode, depthNode, velocityNode, camera ) => nodeObject( new TRAANode( convertToTexture( beautyNode ), depthNode, velocityNode, camera ) );
|