Преглед изворни кода

GTAONode: Fix banding issue (#33919)

Co-authored-by: Michael Herzog <michael.herzog@human-interactive.org>
Marco Fugaro пре 1 недеља
родитељ
комит
d37261e996
2 измењених фајлова са 34 додато и 14 уклоњено
  1. 33 14
      examples/jsm/tsl/display/GTAONode.js
  2. 1 0
      examples/webgpu_postprocessing_ao.html

+ 33 - 14
examples/jsm/tsl/display/GTAONode.js

@@ -117,14 +117,6 @@ class GTAONode extends TempNode {
 		 */
 		 */
 		this.radius = uniform( 0.25 );
 		this.radius = uniform( 0.25 );
 
 
-		/**
-		 * The resolution of the effect. Can be scaled via
-		 * `resolutionScale`.
-		 *
-		 * @type {UniformNode<vec2>}
-		 */
-		this.resolution = uniform( new Vector2() );
-
 		/**
 		/**
 		 * The thickness of the ambient occlusion.
 		 * The thickness of the ambient occlusion.
 		 *
 		 *
@@ -178,6 +170,14 @@ class GTAONode extends TempNode {
 		 */
 		 */
 		this.useTemporalFiltering = false;
 		this.useTemporalFiltering = false;
 
 
+		/**
+		 * The resolution of the effect. Can be scaled via `resolutionScale`.
+		 *
+		 * @private
+		 * @type {UniformNode<vec2>}
+		 */
+		this._resolution = uniform( new Vector2() );
+
 		/**
 		/**
 		 * The node represents the internal noise texture used by the AO.
 		 * The node represents the internal noise texture used by the AO.
 		 *
 		 *
@@ -234,6 +234,13 @@ class GTAONode extends TempNode {
 		 */
 		 */
 		this._temporalOffset = uniform( 0 );
 		this._temporalOffset = uniform( 0 );
 
 
+		/**
+		 * Resolution scale uniform.
+		 *
+		 * @private
+		 * @type {UniformNode<float>}
+		 */
+		this._resolutionScale = uniform( 0 );
 
 
 		/**
 		/**
 		 * The material that is used to render the effect.
 		 * The material that is used to render the effect.
@@ -276,7 +283,8 @@ class GTAONode extends TempNode {
 		width = Math.round( this.resolutionScale * width );
 		width = Math.round( this.resolutionScale * width );
 		height = Math.round( this.resolutionScale * height );
 		height = Math.round( this.resolutionScale * height );
 
 
-		this.resolution.value.set( width, height );
+		this._resolutionScale.value = this.resolutionScale;
+		this._resolution.value.set( width, height );
 		this._aoRenderTarget.setSize( width, height );
 		this._aoRenderTarget.setSize( width, height );
 
 
 	}
 	}
@@ -341,9 +349,7 @@ class GTAONode extends TempNode {
 
 
 		const uvNode = uv();
 		const uvNode = uv();
 
 
-		const sampleDepth = ( uv ) => {
-
-			const depth = this.depthNode.sample( uv ).r;
+		const linearizeDepth = ( depth ) => {
 
 
 			if ( builder.renderer.logarithmicDepthBuffer === true ) {
 			if ( builder.renderer.logarithmicDepthBuffer === true ) {
 
 
@@ -357,12 +363,25 @@ class GTAONode extends TempNode {
 
 
 		};
 		};
 
 
+		const sampleDepth = ( uv ) => linearizeDepth( this.depthNode.sample( uv ).r );
+
+		const sampleCenterDepth = ( uv ) => {
+
+			// Sidestep the nearest-rounding during depth access for the unjittered center pixel to avoid banding
+
+			const g = this.depthNode.gather().sample( uv );
+			const depth = min( min( g.x, g.y ), min( g.z, g.w ) );
+
+			return linearizeDepth( depth );
+
+		};
+
 		const sampleNoise = ( uv ) => this._noiseNode.sample( uv );
 		const sampleNoise = ( uv ) => this._noiseNode.sample( uv );
 		const sampleNormal = ( uv ) => ( this.normalNode !== null ) ? this.normalNode.sample( uv ).rgb.normalize() : getNormalFromDepth( uv, this.depthNode.value, this._cameraProjectionMatrixInverse );
 		const sampleNormal = ( uv ) => ( this.normalNode !== null ) ? this.normalNode.sample( uv ).rgb.normalize() : getNormalFromDepth( uv, this.depthNode.value, this._cameraProjectionMatrixInverse );
 
 
 		const ao = Fn( () => {
 		const ao = Fn( () => {
 
 
-			const depth = sampleDepth( uvNode ).toVar();
+			const depth = this._resolutionScale.lessThan( 1 ).select( sampleCenterDepth( uvNode ), sampleDepth( uvNode ) ).toConst();
 
 
 			depth.greaterThanEqual( 1.0 ).discard();
 			depth.greaterThanEqual( 1.0 ).discard();
 
 
@@ -375,7 +394,7 @@ class GTAONode extends TempNode {
 
 
 			const noiseResolution = textureSize( this._noiseNode, 0 );
 			const noiseResolution = textureSize( this._noiseNode, 0 );
 			let noiseUv = vec2( uvNode.x, uvNode.y.oneMinus() );
 			let noiseUv = vec2( uvNode.x, uvNode.y.oneMinus() );
-			noiseUv = noiseUv.mul( this.resolution.div( noiseResolution ) );
+			noiseUv = noiseUv.mul( this._resolution.div( noiseResolution ) );
 
 
 			const noiseTexel = sampleNoise( noiseUv );
 			const noiseTexel = sampleNoise( noiseUv );
 			const randomVec = noiseTexel.xyz.mul( 2.0 ).sub( 1.0 );
 			const randomVec = noiseTexel.xyz.mul( 2.0 ).sub( 1.0 );

+ 1 - 0
examples/webgpu_postprocessing_ao.html

@@ -500,6 +500,7 @@
 				gui.add( params, 'radius', 0.1, 1 ).onChange( updateParameters );
 				gui.add( params, 'radius', 0.1, 1 ).onChange( updateParameters );
 				gui.add( params, 'scale', 0.01, 1 ).onChange( updateParameters );
 				gui.add( params, 'scale', 0.01, 1 ).onChange( updateParameters );
 				gui.add( params, 'thickness', 0.01, 2 ).onChange( updateParameters );
 				gui.add( params, 'thickness', 0.01, 2 ).onChange( updateParameters );
+				gui.add( aoPass, 'resolutionScale', 0, 1 ).name( 'resolution scale' );
 				gui.add( aoPass, 'useTemporalFiltering' ).name( 'temporal filtering' );
 				gui.add( aoPass, 'useTemporalFiltering' ).name( 'temporal filtering' );
 				gui.add( transparentMesh, 'visible' ).name( 'show transparent mesh' );
 				gui.add( transparentMesh, 'visible' ).name( 'show transparent mesh' );
 				gui.add( params, 'transparentOpacity', 0, 1, 0.01 ).name( 'transparent opacity' ).onChange( updateParameters );
 				gui.add( params, 'transparentOpacity', 0, 1, 0.01 ).name( 'transparent opacity' ).onChange( updateParameters );

粤ICP备19079148号