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

TSL: Align API of blur filters and deprecated `premultipliedGaussianBlur()` (#31559)

* remove redundant nodeObject()

* add sample interface for option.mask

* introduce options to gaussianBlur

* add `option.resolution`

* add`uv` to `sample()` node

* fix check

* remove `mask`

* Update webgpu_reflection_blurred.html

* add more description
sunag 5 месяцев назад
Родитель
Сommit
93b9620c1b

+ 19 - 30
examples/jsm/tsl/display/GaussianBlurNode.js

@@ -25,8 +25,11 @@ class GaussianBlurNode extends TempNode {
 	 * @param {TextureNode} textureNode - The texture node that represents the input of the effect.
 	 * @param {Node<vec2|float>} directionNode - Defines the direction and radius of the blur.
 	 * @param {number} sigma - Controls the kernel of the blur filter. Higher values mean a wider blur radius.
+	 * @param {Object} [options={}] - Additional options for the gaussian blur effect.
+	 * @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
+	 * @param {Vector2} [options.resolution=new Vector2(1, 1)] - The resolution of the effect. 0.5 means half the resolution of the texture node.
 	 */
-	constructor( textureNode, directionNode = null, sigma = 4 ) {
+	constructor( textureNode, directionNode = null, sigma = 4, options = {} ) {
 
 		super( 'vec4' );
 
@@ -110,7 +113,7 @@ class GaussianBlurNode extends TempNode {
 		 * @type {Vector2}
 		 * @default (1,1)
 		 */
-		this.resolution = new Vector2( 1, 1 );
+		this.resolution = options.resolution || new Vector2( 1, 1 );
 
 		/**
 		 * Whether the effect should use premultiplied alpha or not. Set this to `true`
@@ -119,32 +122,7 @@ class GaussianBlurNode extends TempNode {
 		 * @type {boolean}
 		 * @default false
 		 */
-		this.premultipliedAlpha = false;
-
-	}
-
-	/**
-	 * Sets the given premultiplied alpha value.
-	 *
-	 * @param {boolean} value - Whether the effect should use premultiplied alpha or not.
-	 * @return {GaussianBlurNode} height - A reference to this node.
-	 */
-	setPremultipliedAlpha( value ) {
-
-		this.premultipliedAlpha = value;
-
-		return this;
-
-	}
-
-	/**
-	 * Returns the premultiplied alpha value.
-	 *
-	 * @return {boolean} Whether the effect should use premultiplied alpha or not.
-	 */
-	getPremultipliedAlpha() {
-
-		return this.premultipliedAlpha;
+		this.premultipliedAlpha = options.premultipliedAlpha || false;
 
 	}
 
@@ -280,6 +258,7 @@ class GaussianBlurNode extends TempNode {
 				const sample2 = sampleTexture( uvNode.sub( uvOffset ) );
 
 				diffuseSum.addAssign( sample1.add( sample2 ).mul( w ) );
+
 			}
 
 			return output( diffuseSum );
@@ -349,18 +328,28 @@ export default GaussianBlurNode;
  * @param {Node<vec4>} node - The node that represents the input of the effect.
  * @param {Node<vec2|float>} directionNode - Defines the direction and radius of the blur.
  * @param {number} sigma - Controls the kernel of the blur filter. Higher values mean a wider blur radius.
+ * @param {Object} [options={}] - Additional options for the gaussian blur effect.
+ * @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
+ * @param {Vector2} [options.resolution=new Vector2(1, 1)] - The resolution of the effect. 0.5 means half the resolution of the texture node.
  * @returns {GaussianBlurNode}
  */
-export const gaussianBlur = ( node, directionNode, sigma ) => nodeObject( new GaussianBlurNode( convertToTexture( node ), directionNode, sigma ) );
+export const gaussianBlur = ( node, directionNode, sigma, options = {} ) => nodeObject( new GaussianBlurNode( convertToTexture( node ), directionNode, sigma, options ) );
 
 /**
  * TSL function for creating a gaussian blur node for post processing with enabled premultiplied alpha.
  *
  * @tsl
  * @function
+ * @deprecated  since r180. Use `gaussianBlur()` with `premultipliedAlpha: true` option instead.
  * @param {Node<vec4>} node - The node that represents the input of the effect.
  * @param {Node<vec2|float>} directionNode - Defines the direction and radius of the blur.
  * @param {number} sigma - Controls the kernel of the blur filter. Higher values mean a wider blur radius.
  * @returns {GaussianBlurNode}
  */
-export const premultipliedGaussianBlur = ( node, directionNode, sigma ) => nodeObject( new GaussianBlurNode( convertToTexture( node ), directionNode, sigma ).setPremultipliedAlpha( true ) );
+export function premultipliedGaussianBlur( node, directionNode, sigma ) {
+
+	console.warn( 'THREE.TSL: "premultipliedGaussianBlur()" is deprecated. Use "gaussianBlur()" with "premultipliedAlpha: true" option instead.' ); // deprecated, r180
+
+	return gaussianBlur( node, directionNode, sigma, { premultipliedAlpha: true } );
+
+}

+ 2 - 11
examples/jsm/tsl/display/boxBlur.js

@@ -19,29 +19,20 @@ import { Fn, vec2, uv, Loop, vec4, premultiplyAlpha, unpremultiplyAlpha, max, in
  * @param {Object} [options={}] - Additional options for the hash blur effect.
  * @param {Node<int>} [options.size=int(1)] - Controls the blur's kernel. For performant results, the range should within [1, 3].
  * @param {Node<int>} [options.separation=int(1)] - Spreads out the blur without having to sample additional fragments. Ranges from [1, Infinity].
- * @param {Node<vec4>} [options.mask=null] - A mask node to control the alpha blending of the blur.
  * @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
  * @return {Node<vec4>} The blurred texture node.
  */
 export const boxBlur = /*#__PURE__*/ Fn( ( [ textureNode, options = {} ] ) => {
 
 	textureNode = convertToTexture( textureNode );
+
 	const size = nodeObject( options.size ) || int( 1 );
 	const separation = nodeObject( options.separation ) || int( 1 );
-	const mask = options.mask || null;
 	const premultipliedAlpha = options.premultipliedAlpha || false;
 
 	const tap = ( uv ) => {
 
-		let sample = textureNode.sample( uv );
-
-		if ( mask !== null ) {
-
-			const alpha = mask.sample( uv ).x;
-
-			sample = vec4( sample.rgb, sample.a.mul( alpha ) );
-
-		}
+		const sample = textureNode.sample( uv );
 
 		return premultipliedAlpha ? premultiplyAlpha( sample ) : sample;
 

+ 12 - 15
examples/jsm/tsl/display/hashBlur.js

@@ -3,6 +3,13 @@ import { float, Fn, vec2, uv, sin, rand, degrees, cos, Loop, vec4, premultiplyAl
 /**
  * Applies a hash blur effect to the given texture node.
  *
+ * The approach of this blur is different compared to Gaussian and box blur since
+ * it does not rely on a kernel to apply a convolution. Instead, it reads the base
+ * texture multiple times in a random pattern and then averages the samples. A
+ * typical artifact of this technique is a slightly noisy appearance of the blur which
+ * can be mitigated by increasing the number of iterations (see `repeats` parameter).
+ * Compared to Gaussian blur, hash blur requires just a single pass.
+ *
  * Reference: {@link https://www.shadertoy.com/view/4lXXWn}.
  *
  * @function
@@ -10,42 +17,32 @@ import { float, Fn, vec2, uv, sin, rand, degrees, cos, Loop, vec4, premultiplyAl
  * @param {Node<float>} [bluramount=float(0.1)] - This node determines the amount of blur.
  * @param {Object} [options={}] - Additional options for the hash blur effect.
  * @param {Node<float>} [options.repeats=float(45)] - The number of iterations for the blur effect.
- * @param {Node<vec4>} [options.mask=null] - A mask node to control the alpha blending of the blur.
  * @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
  * @return {Node<vec4>} The blurred texture node.
  */
 export const hashBlur = /*#__PURE__*/ Fn( ( [ textureNode, bluramount = float( 0.1 ), options = {} ] ) => {
 
 	textureNode = convertToTexture( textureNode );
-	bluramount = nodeObject( bluramount );
+
 	const repeats = nodeObject( options.size ) || float( 45 );
-	const mask = options.mask || null;
 	const premultipliedAlpha = options.premultipliedAlpha || false;
 
-	const draw = ( uv ) => {
-
-		let sample = textureNode.sample( uv );
-
-		if ( mask !== null ) {
-
-			const alpha = mask.sample( uv ).x;
-
-			sample = vec4( sample.rgb, sample.a.mul( alpha ) );
+	const tap = ( uv ) => {
 
-		}
+		const sample = textureNode.sample( uv );
 
 		return premultipliedAlpha ? premultiplyAlpha( sample ) : sample;
 
 	};
 
 	const targetUV = textureNode.uvNode || uv();
-	const blurred_image = vec4( 0. ).toVar();
+	const blurred_image = vec4( 0. );
 
 	Loop( { start: 0., end: repeats, type: 'float' }, ( { i } ) => {
 
 		const q = vec2( vec2( cos( degrees( i.div( repeats ).mul( 360. ) ) ), sin( degrees( i.div( repeats ).mul( 360. ) ) ) ).mul( rand( vec2( i, targetUV.x.add( targetUV.y ) ) ).add( bluramount ) ) );
 		const uv2 = vec2( targetUV.add( q.mul( bluramount ) ) );
-		blurred_image.addAssign( draw( uv2 ) );
+		blurred_image.addAssign( tap( uv2 ) );
 
 	} );
 

+ 13 - 3
examples/webgpu_reflection_blurred.html

@@ -26,7 +26,7 @@
 		<script type="module">
 
 			import * as THREE from 'three/webgpu';
-			import { Fn, vec4, fract, abs, uniform, pow, color, max, length, rangeFogFactor, sub, reflector, normalWorld, hue, time, mix, positionWorld } from 'three/tsl';
+			import { Fn, vec4, fract, sample, abs, uniform, pow, color, max, length, rangeFogFactor, sub, reflector, normalWorld, hue, time, mix, positionWorld } from 'three/tsl';
 
 			import { hashBlur } from 'three/addons/tsl/display/hashBlur.js';
 
@@ -138,11 +138,21 @@
 					const radiusRange = mix( 0.01, 0.1, radius ); // range [ 0.01, 0.1 ]
 					const roughnessRange = mix( 0.3, 0.03, roughness ); // range [ 0.03, 0.3 ]
 
+					// mask the sample
+
+					const maskReflection = sample( ( uv ) => {
+
+						const sample = reflection.sample( uv );
+						const mask = reflectionDepth.sample( uv );
+
+						return vec4( sample.rgb, sample.a.mul( mask.r ) );
+
+					}, reflection.uvNode );
+
 					// blur the reflection
 
-					const reflectionBlurred = hashBlur( reflection, radiusRange, {
+					const reflectionBlurred = hashBlur( maskReflection, radiusRange, {
 						repeats: 40,
-						mask: reflectionDepth,
 						premultipliedAlpha: true
 					} );
 

+ 1 - 1
src/nodes/utils/RTTNode.js

@@ -271,7 +271,7 @@ export const rtt = ( node, ...params ) => nodeObject( new RTTNode( nodeObject( n
  */
 export const convertToTexture = ( node, ...params ) => {
 
-	if ( node.isTextureNode ) return node;
+	if ( node.isSampleNode || node.isTextureNode ) return node;
 	if ( node.isPassNode ) return node.getTextureNode();
 
 	return rtt( node, ...params );

+ 12 - 2
src/nodes/utils/SampleNode.js

@@ -26,13 +26,22 @@ class SampleNode extends Node {
 	 * Creates an instance of SampleNode.
 	 *
 	 * @param {Function} callback - The function to be called when sampling. Should accept a UV node and return a value.
+	 * @param {?Node<vec2>} [uvNode=null] - The UV node to be used in the texture sampling.
 	 */
-	constructor( callback ) {
+	constructor( callback, uvNode = null ) {
 
 		super();
 
 		this.callback = callback;
 
+		/**
+		 * Represents the texture coordinates.
+		 *
+		 * @type {?Node<vec2|vec3>}
+		 * @default null
+		 */
+		this.uvNode = uvNode;
+
 		/**
 		 * This flag can be used for type testing.
 		 *
@@ -76,6 +85,7 @@ export default SampleNode;
  *
  * @function
  * @param {Function} callback - The function to be called when sampling. Should accept a UV node and return a value.
+ * @param {?Node<vec2>} [uv=null] - The UV node to be used in the texture sampling.
  * @returns {SampleNode} The created SampleNode instance wrapped as a node object.
  */
-export const sample = ( callback ) => nodeObject( new SampleNode( callback ) );
+export const sample = ( callback, uv = null ) => nodeObject( new SampleNode( callback, nodeObject( uv ) ) );

粤ICP备19079148号