Parcourir la source

GTAONode: Add basic support for temporal filtering. (#32076)

Michael Herzog il y a 2 mois
Parent
commit
e0fdb2476f
2 fichiers modifiés avec 42 ajouts et 1 suppressions
  1. 40 1
      examples/jsm/tsl/display/GTAONode.js
  2. 2 0
      examples/webgpu_postprocessing_ao.html

+ 40 - 1
examples/jsm/tsl/display/GTAONode.js

@@ -4,6 +4,9 @@ import { reference, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getNormalF
 const _quadMesh = /*@__PURE__*/ new QuadMesh();
 const _size = /*@__PURE__*/ new Vector2();
 
+// From Activision GTAO paper: https://www.activision.com/cdn/research/s2016_pbs_activision_occlusion.pptx
+const _temporalRotations = [ 60, 300, 180, 240, 120, 0 ];
+
 let _rendererState;
 
 /**
@@ -150,6 +153,20 @@ class GTAONode extends TempNode {
 		 */
 		this.samples = uniform( 16 );
 
+		/**
+		 * Whether to use temporal filtering or not. Setting this property to
+		 * `true` requires the usage of `TRAANode`. This will help to reduce noise
+		 * although it introduces typical TAA artifacts like ghosting and temporal
+		 * instabilities.
+		 *
+		 * If setting this property to `false`, a manual denoise via `DenoiseNode`
+		 * might be required.
+		 *
+		 * @type {boolean}
+		 * @default false
+		 */
+		this.useTemporalFiltering = false;
+
 		/**
 		 * The node represents the internal noise texture used by the AO.
 		 *
@@ -190,6 +207,13 @@ class GTAONode extends TempNode {
 		 */
 		this._cameraFar = reference( 'far', 'float', camera );
 
+		/**
+		 * Temporal direction that influences the rotation angle for each slice.
+		 *
+		 * @type {UniformNode<float>}
+		 */
+		this._temporalDirection = uniform( 0 );
+
 		/**
 		 * The material that is used to render the effect.
 		 *
@@ -247,6 +271,20 @@ class GTAONode extends TempNode {
 
 		_rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
 
+		// update temporal uniforms
+
+		if ( this.useTemporalFiltering === true ) {
+
+			const frameId = frame.frameId;
+
+			this._temporalDirection.value = _temporalRotations[ frameId % 6 ] / 360;
+
+		} else {
+
+			this._temporalDirection.value = 0;
+
+		}
+
 		//
 
 		const size = renderer.getDrawingBufferSize( _size );
@@ -313,6 +351,7 @@ class GTAONode extends TempNode {
 			const noiseResolution = textureSize( this._noiseNode, 0 );
 			let noiseUv = vec2( uvNode.x, uvNode.y.oneMinus() );
 			noiseUv = noiseUv.mul( this.resolution.div( noiseResolution ) );
+
 			const noiseTexel = sampleNoise( noiseUv );
 			const randomVec = noiseTexel.xyz.mul( 2.0 ).sub( 1.0 );
 			const tangent = vec3( randomVec.xy, 0.0 ).normalize();
@@ -328,7 +367,7 @@ class GTAONode extends TempNode {
 
 			Loop( { start: int( 0 ), end: DIRECTIONS, type: 'int', condition: '<' }, ( { i } ) => {
 
-				const angle = float( i ).div( float( DIRECTIONS ) ).mul( PI ).toVar();
+				const angle = float( i ).div( float( DIRECTIONS ) ).mul( PI ).add( this._temporalDirection ).toVar();
 				const sampleDir = vec4( cos( angle ), sin( angle ), 0., add( 0.5, mul( 0.5, noiseTexel.w ) ) );
 				sampleDir.xyz = normalize( kernelMatrix.mul( sampleDir.xyz ) );
 

+ 2 - 0
examples/webgpu_postprocessing_ao.html

@@ -121,6 +121,7 @@
 
 				aoPass = ao( scenePassDepth, scenePassNormal, camera ).toInspector( 'AO' );
 				aoPass.resolutionScale = 0.5; // running AO in half resolution is often sufficient
+				aoPass.useTemporalFiltering = true;
 				blendPassAO = vec4( scenePassColor.rgb.mul( aoPass.r ), scenePassColor.a ); // the AO is stored only in the red channel
 
 				// traa
@@ -167,6 +168,7 @@
 				gui.add( params, 'radius', 0.1, 1 ).onChange( updateParameters );
 				gui.add( params, 'scale', 0.01, 2 ).onChange( updateParameters );
 				gui.add( params, 'thickness', 0.01, 2 ).onChange( updateParameters );
+				gui.add( aoPass, 'useTemporalFiltering' ).name( 'temporal filtering' );
 				gui.add( params, 'aoOnly' ).onChange( ( value ) => {
 
 					if ( value === true ) {

粤ICP备19079148号