Просмотр исходного кода

Replace MagicNoise with BlueNoise

Marco Fugaro 1 месяц назад
Родитель
Сommit
14954cf949
1 измененных файлов с 18 добавлено и 105 удалено
  1. 18 105
      examples/jsm/tsl/display/GTAONode.js

+ 18 - 105
examples/jsm/tsl/display/GTAONode.js

@@ -1,5 +1,6 @@
-import { DataTexture, RenderTarget, RepeatWrapping, Vector2, Vector3, TempNode, QuadMesh, NodeMaterial, RendererUtils, RedFormat, FloatType, NearestFilter } from 'three/webgpu';
+import { RenderTarget, Vector2, TempNode, QuadMesh, NodeMaterial, RendererUtils, RedFormat, FloatType, NearestFilter } from 'three/webgpu';
 import { reference, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getNormalFromDepth, getScreenPosition, getViewPosition, nodeObject, Fn, float, NodeUpdateType, uv, uniform, Loop, vec2, vec3, vec4, int, dot, max, min, pow, abs, If, textureSize, sin, cos, PI, texture, passTexture, mat3, add, normalize, mul, cross, mix, acos, clamp } from 'three/tsl';
+import { generateBlueNoiseTexture } from '../../math/BlueNoise.js';
 
 const _quadMesh = /*@__PURE__*/ new QuadMesh();
 const _size = /*@__PURE__*/ new Vector2();
@@ -186,12 +187,14 @@ class GTAONode extends TempNode {
 		this.useTemporalFiltering = false;
 
 		/**
-		 * The node represents the internal noise texture used by the AO.
+		 * Blue-noise texture sampled for the slice rotation and step jitter.
+		 * Generated by Ulichney's void-and-cluster method (see
+		 * {@link generateBlueNoiseTexture}); 64×64 single-channel, tileable.
 		 *
 		 * @private
 		 * @type {TextureNode}
 		 */
-		this._noiseNode = texture( generateMagicSquareNoise() );
+		this._noiseNode = texture( generateBlueNoiseTexture( 64 ) );
 
 		/**
 		 * Represents the projection matrix of the scene's camera.
@@ -436,13 +439,19 @@ class GTAONode extends TempNode {
 
 			const radiusToUse = this.radius;
 
+			// Tile the blue-noise texture across the screen at its native resolution.
 			const noiseResolution = textureSize( this._noiseNode, 0 );
-			let noiseUv = vec2( uvNode.x, uvNode.y.oneMinus() );
-			noiseUv = noiseUv.mul( this.resolution.div( noiseResolution ) );
+			const noiseUv = uvNode.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();
+			// Single-channel blue noise: take two samples with a half-texture UV offset
+			// for a decorrelated second channel. `noise1` → slice rotation, `noise2` →
+			// per-step phase jitter.
+			const noise1 = sampleNoise( noiseUv ).r;
+			const noise2 = sampleNoise( noiseUv.add( vec2( 0.5, 0.5 ) ) ).r;
+
+			// Random tangent direction from noise1, used to rotate the per-slice azimuth.
+			const tangentAngle = noise1.mul( PI ).mul( 2.0 );
+			const tangent = vec3( cos( tangentAngle ), sin( tangentAngle ), 0.0 );
 			const bitangent = vec3( tangent.y.mul( - 1.0 ), tangent.x, 0.0 );
 			const kernelMatrix = mat3( tangent, bitangent, vec3( 0.0, 0.0, 1.0 ) );
 
@@ -455,7 +464,7 @@ class GTAONode extends TempNode {
 
 			// Per-step phase jitter for spatio-temporal decorrelation.
 			// (Activision GTAO slides 86, 92–93 "Noise Distribution".)
-			const stepJitter = mul( 0.5, noiseTexel.w ).toVar();
+			const stepJitter = mul( 0.5, noise2 ).toVar();
 
 			Loop( { start: int( 0 ), end: DIRECTIONS, type: 'int', condition: '<' }, ( { i } ) => {
 
@@ -629,102 +638,6 @@ class GTAONode extends TempNode {
 
 export default GTAONode;
 
-/**
- * Generates the AO's noise texture for the given size.
- *
- * @param {number} [size=5] - The noise size.
- * @return {DataTexture} The generated noise texture.
- */
-function generateMagicSquareNoise( size = 5 ) {
-
-	const noiseSize = Math.floor( size ) % 2 === 0 ? Math.floor( size ) + 1 : Math.floor( size );
-	const magicSquare = generateMagicSquare( noiseSize );
-	const noiseSquareSize = magicSquare.length;
-	const data = new Uint8Array( noiseSquareSize * 4 );
-
-	for ( let inx = 0; inx < noiseSquareSize; ++ inx ) {
-
-		const iAng = magicSquare[ inx ];
-		const angle = ( 2 * Math.PI * iAng ) / noiseSquareSize;
-		const randomVec = new Vector3(
-			Math.cos( angle ),
-			Math.sin( angle ),
-			0
-		).normalize();
-		data[ inx * 4 ] = ( randomVec.x * 0.5 + 0.5 ) * 255;
-		data[ inx * 4 + 1 ] = ( randomVec.y * 0.5 + 0.5 ) * 255;
-		data[ inx * 4 + 2 ] = 127;
-		data[ inx * 4 + 3 ] = 255;
-
-	}
-
-	const noiseTexture = new DataTexture( data, noiseSize, noiseSize );
-	noiseTexture.wrapS = RepeatWrapping;
-	noiseTexture.wrapT = RepeatWrapping;
-	noiseTexture.needsUpdate = true;
-
-	return noiseTexture;
-
-}
-
-/**
- * Computes an array of magic square values required to generate the noise texture.
- *
- * @param {number} size - The noise size.
- * @return {Array<number>} The magic square values.
- */
-function generateMagicSquare( size ) {
-
-	const noiseSize = Math.floor( size ) % 2 === 0 ? Math.floor( size ) + 1 : Math.floor( size );
-	const noiseSquareSize = noiseSize * noiseSize;
-	const magicSquare = Array( noiseSquareSize ).fill( 0 );
-	let i = Math.floor( noiseSize / 2 );
-	let j = noiseSize - 1;
-
-	for ( let num = 1; num <= noiseSquareSize; ) {
-
-		if ( i === - 1 && j === noiseSize ) {
-
-			j = noiseSize - 2;
-			i = 0;
-
-		} else {
-
-			if ( j === noiseSize ) {
-
-				j = 0;
-
-			}
-
-			if ( i < 0 ) {
-
-				i = noiseSize - 1;
-
-			}
-
-		}
-
-		if ( magicSquare[ i * noiseSize + j ] !== 0 ) {
-
-			j -= 2;
-			i ++;
-			continue;
-
-		} else {
-
-			magicSquare[ i * noiseSize + j ] = num ++;
-
-		}
-
-		j ++;
-		i --;
-
-	}
-
-	return magicSquare;
-
-}
-
 /**
  * TSL function for creating a Ground Truth Ambient Occlusion (GTAO) effect.
  *

粤ICP备19079148号