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

Reduce bluenoise taps from 2 to 1

Marco Fugaro 1 месяц назад
Родитель
Сommit
c84994d66f
2 измененных файлов с 38 добавлено и 17 удалено
  1. 28 10
      examples/jsm/math/BlueNoise.js
  2. 10 7
      examples/jsm/tsl/display/GTAONode.js

+ 28 - 10
examples/jsm/math/BlueNoise.js

@@ -1,4 +1,4 @@
-import { DataTexture, RedFormat, UnsignedByteType, RepeatWrapping } from 'three';
+import { DataTexture, RedFormat, RGFormat, RGBAFormat, UnsignedByteType, RepeatWrapping } from 'three';
 
 /**
  * Generates tileable blue-noise dither arrays via Ulichney's void-and-cluster method.
@@ -247,28 +247,46 @@ class BlueNoiseGenerator {
 
 /**
  * Generate a blue noise DataTexture.
- * Returns a single-channel (Red) 8-bit texture with RepeatWrapping,
- * suitable for sampling in shaders as a tileable noise source.
+ * Returns an 8-bit texture with RepeatWrapping, suitable for sampling in shaders
+ * as a tileable noise source. Each channel is an independent blue-noise pattern,
+ * generated with a distinct seed so consumers can read decorrelated values from
+ * a single texture fetch.
  *
  * @param {number} [size=64] Texture dimension in pixels (the noise is square).
+ * @param {number} [channels=1] Number of independent noise channels. Must be `1`
+ * (RedFormat), `2` (RGFormat), or `4` (RGBAFormat). Generation cost scales linearly.
  * @return {DataTexture} The generated blue-noise DataTexture.
  */
-export function generateBlueNoiseTexture( size = 64 ) {
+export function generateBlueNoiseTexture( size = 64, channels = 1 ) {
+
+	if ( channels !== 1 && channels !== 2 && channels !== 4 ) {
+
+		throw new Error( 'generateBlueNoiseTexture: channels must be 1, 2, or 4.' );
+
+	}
+
+	const format = channels === 1 ? RedFormat : channels === 2 ? RGFormat : RGBAFormat;
 
 	const generator = new BlueNoiseGenerator();
 	generator.size = size;
 
-	const { data, maxValue } = generator.generate();
+	const pixels = new Uint8Array( size * size * channels );
 
-	// Normalize Uint32 ranks to Uint8 [0, 255]
-	const pixels = new Uint8Array( size * size );
-	for ( let i = 0, l = data.length; i < l; i ++ ) {
+	// Each channel is regenerated with a distinct seed for an independent pattern.
+	for ( let c = 0; c < channels; c ++ ) {
 
-		pixels[ i ] = ( data[ i ] / maxValue ) * 255;
+		generator.seed = c + 1;
+		const { data, maxValue } = generator.generate();
+
+		for ( let i = 0, l = data.length; i < l; i ++ ) {
+
+			pixels[ i * channels + c ] = ( data[ i ] / maxValue ) * 255;
+
+		}
 
 	}
 
-	const texture = new DataTexture( pixels, size, size, RedFormat, UnsignedByteType );
+	const texture = new DataTexture( pixels, size, size, format, UnsignedByteType );
 	texture.wrapS = RepeatWrapping;
 	texture.wrapT = RepeatWrapping;
 	texture.needsUpdate = true;

+ 10 - 7
examples/jsm/tsl/display/GTAONode.js

@@ -189,12 +189,14 @@ class GTAONode extends TempNode {
 		/**
 		 * 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.
+		 * {@link generateBlueNoiseTexture}); 64×64 two-channel, tileable. Channel
+		 * R drives the slice rotation, G drives the per-step phase jitter — both
+		 * read from a single texture fetch.
 		 *
 		 * @private
 		 * @type {TextureNode}
 		 */
-		this._noiseNode = texture( generateBlueNoiseTexture( 64 ) );
+		this._noiseNode = texture( generateBlueNoiseTexture( 64, 2 ) );
 
 		/**
 		 * Represents the projection matrix of the scene's camera.
@@ -443,11 +445,12 @@ class GTAONode extends TempNode {
 			const noiseResolution = textureSize( this._noiseNode, 0 );
 			const noiseUv = uvNode.mul( this.resolution.div( noiseResolution ) );
 
-			// 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;
+			// R and G are independent blue-noise patterns generated with different
+			// seeds, so one fetch gives a decorrelated pair. `noise1` → slice
+			// rotation, `noise2` → per-step phase jitter.
+			const noiseSample = sampleNoise( noiseUv );
+			const noise1 = noiseSample.r;
+			const noise2 = noiseSample.g;
 
 			// Random tangent direction from noise1, used to rotate the per-slice azimuth.
 			const tangentAngle = noise1.mul( PI ).mul( 2.0 );

粤ICP备19079148号