Skip to content

Commit 93b9620

Browse files
authored
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
1 parent 84aa697 commit 93b9620

File tree

6 files changed

+59
-62
lines changed

6 files changed

+59
-62
lines changed

examples/jsm/tsl/display/GaussianBlurNode.js

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ class GaussianBlurNode extends TempNode {
2525
* @param {TextureNode} textureNode - The texture node that represents the input of the effect.
2626
* @param {Node<vec2|float>} directionNode - Defines the direction and radius of the blur.
2727
* @param {number} sigma - Controls the kernel of the blur filter. Higher values mean a wider blur radius.
28+
* @param {Object} [options={}] - Additional options for the gaussian blur effect.
29+
* @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
30+
* @param {Vector2} [options.resolution=new Vector2(1, 1)] - The resolution of the effect. 0.5 means half the resolution of the texture node.
2831
*/
29-
constructor( textureNode, directionNode = null, sigma = 4 ) {
32+
constructor( textureNode, directionNode = null, sigma = 4, options = {} ) {
3033

3134
super( 'vec4' );
3235

@@ -110,7 +113,7 @@ class GaussianBlurNode extends TempNode {
110113
* @type {Vector2}
111114
* @default (1,1)
112115
*/
113-
this.resolution = new Vector2( 1, 1 );
116+
this.resolution = options.resolution || new Vector2( 1, 1 );
114117

115118
/**
116119
* Whether the effect should use premultiplied alpha or not. Set this to `true`
@@ -119,32 +122,7 @@ class GaussianBlurNode extends TempNode {
119122
* @type {boolean}
120123
* @default false
121124
*/
122-
this.premultipliedAlpha = false;
123-
124-
}
125-
126-
/**
127-
* Sets the given premultiplied alpha value.
128-
*
129-
* @param {boolean} value - Whether the effect should use premultiplied alpha or not.
130-
* @return {GaussianBlurNode} height - A reference to this node.
131-
*/
132-
setPremultipliedAlpha( value ) {
133-
134-
this.premultipliedAlpha = value;
135-
136-
return this;
137-
138-
}
139-
140-
/**
141-
* Returns the premultiplied alpha value.
142-
*
143-
* @return {boolean} Whether the effect should use premultiplied alpha or not.
144-
*/
145-
getPremultipliedAlpha() {
146-
147-
return this.premultipliedAlpha;
125+
this.premultipliedAlpha = options.premultipliedAlpha || false;
148126

149127
}
150128

@@ -280,6 +258,7 @@ class GaussianBlurNode extends TempNode {
280258
const sample2 = sampleTexture( uvNode.sub( uvOffset ) );
281259

282260
diffuseSum.addAssign( sample1.add( sample2 ).mul( w ) );
261+
283262
}
284263

285264
return output( diffuseSum );
@@ -349,18 +328,28 @@ export default GaussianBlurNode;
349328
* @param {Node<vec4>} node - The node that represents the input of the effect.
350329
* @param {Node<vec2|float>} directionNode - Defines the direction and radius of the blur.
351330
* @param {number} sigma - Controls the kernel of the blur filter. Higher values mean a wider blur radius.
331+
* @param {Object} [options={}] - Additional options for the gaussian blur effect.
332+
* @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
333+
* @param {Vector2} [options.resolution=new Vector2(1, 1)] - The resolution of the effect. 0.5 means half the resolution of the texture node.
352334
* @returns {GaussianBlurNode}
353335
*/
354-
export const gaussianBlur = ( node, directionNode, sigma ) => nodeObject( new GaussianBlurNode( convertToTexture( node ), directionNode, sigma ) );
336+
export const gaussianBlur = ( node, directionNode, sigma, options = {} ) => nodeObject( new GaussianBlurNode( convertToTexture( node ), directionNode, sigma, options ) );
355337

356338
/**
357339
* TSL function for creating a gaussian blur node for post processing with enabled premultiplied alpha.
358340
*
359341
* @tsl
360342
* @function
343+
* @deprecated since r180. Use `gaussianBlur()` with `premultipliedAlpha: true` option instead.
361344
* @param {Node<vec4>} node - The node that represents the input of the effect.
362345
* @param {Node<vec2|float>} directionNode - Defines the direction and radius of the blur.
363346
* @param {number} sigma - Controls the kernel of the blur filter. Higher values mean a wider blur radius.
364347
* @returns {GaussianBlurNode}
365348
*/
366-
export const premultipliedGaussianBlur = ( node, directionNode, sigma ) => nodeObject( new GaussianBlurNode( convertToTexture( node ), directionNode, sigma ).setPremultipliedAlpha( true ) );
349+
export function premultipliedGaussianBlur( node, directionNode, sigma ) {
350+
351+
console.warn( 'THREE.TSL: "premultipliedGaussianBlur()" is deprecated. Use "gaussianBlur()" with "premultipliedAlpha: true" option instead.' ); // deprecated, r180
352+
353+
return gaussianBlur( node, directionNode, sigma, { premultipliedAlpha: true } );
354+
355+
}

examples/jsm/tsl/display/boxBlur.js

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,20 @@ import { Fn, vec2, uv, Loop, vec4, premultiplyAlpha, unpremultiplyAlpha, max, in
1919
* @param {Object} [options={}] - Additional options for the hash blur effect.
2020
* @param {Node<int>} [options.size=int(1)] - Controls the blur's kernel. For performant results, the range should within [1, 3].
2121
* @param {Node<int>} [options.separation=int(1)] - Spreads out the blur without having to sample additional fragments. Ranges from [1, Infinity].
22-
* @param {Node<vec4>} [options.mask=null] - A mask node to control the alpha blending of the blur.
2322
* @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
2423
* @return {Node<vec4>} The blurred texture node.
2524
*/
2625
export const boxBlur = /*#__PURE__*/ Fn( ( [ textureNode, options = {} ] ) => {
2726

2827
textureNode = convertToTexture( textureNode );
28+
2929
const size = nodeObject( options.size ) || int( 1 );
3030
const separation = nodeObject( options.separation ) || int( 1 );
31-
const mask = options.mask || null;
3231
const premultipliedAlpha = options.premultipliedAlpha || false;
3332

3433
const tap = ( uv ) => {
3534

36-
let sample = textureNode.sample( uv );
37-
38-
if ( mask !== null ) {
39-
40-
const alpha = mask.sample( uv ).x;
41-
42-
sample = vec4( sample.rgb, sample.a.mul( alpha ) );
43-
44-
}
35+
const sample = textureNode.sample( uv );
4536

4637
return premultipliedAlpha ? premultiplyAlpha( sample ) : sample;
4738

examples/jsm/tsl/display/hashBlur.js

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,46 @@ import { float, Fn, vec2, uv, sin, rand, degrees, cos, Loop, vec4, premultiplyAl
33
/**
44
* Applies a hash blur effect to the given texture node.
55
*
6+
* The approach of this blur is different compared to Gaussian and box blur since
7+
* it does not rely on a kernel to apply a convolution. Instead, it reads the base
8+
* texture multiple times in a random pattern and then averages the samples. A
9+
* typical artifact of this technique is a slightly noisy appearance of the blur which
10+
* can be mitigated by increasing the number of iterations (see `repeats` parameter).
11+
* Compared to Gaussian blur, hash blur requires just a single pass.
12+
*
613
* Reference: {@link https://www.shadertoy.com/view/4lXXWn}.
714
*
815
* @function
916
* @param {Node<vec4>} textureNode - The texture node that should be blurred.
1017
* @param {Node<float>} [bluramount=float(0.1)] - This node determines the amount of blur.
1118
* @param {Object} [options={}] - Additional options for the hash blur effect.
1219
* @param {Node<float>} [options.repeats=float(45)] - The number of iterations for the blur effect.
13-
* @param {Node<vec4>} [options.mask=null] - A mask node to control the alpha blending of the blur.
1420
* @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
1521
* @return {Node<vec4>} The blurred texture node.
1622
*/
1723
export const hashBlur = /*#__PURE__*/ Fn( ( [ textureNode, bluramount = float( 0.1 ), options = {} ] ) => {
1824

1925
textureNode = convertToTexture( textureNode );
20-
bluramount = nodeObject( bluramount );
26+
2127
const repeats = nodeObject( options.size ) || float( 45 );
22-
const mask = options.mask || null;
2328
const premultipliedAlpha = options.premultipliedAlpha || false;
2429

25-
const draw = ( uv ) => {
26-
27-
let sample = textureNode.sample( uv );
28-
29-
if ( mask !== null ) {
30-
31-
const alpha = mask.sample( uv ).x;
32-
33-
sample = vec4( sample.rgb, sample.a.mul( alpha ) );
30+
const tap = ( uv ) => {
3431

35-
}
32+
const sample = textureNode.sample( uv );
3633

3734
return premultipliedAlpha ? premultiplyAlpha( sample ) : sample;
3835

3936
};
4037

4138
const targetUV = textureNode.uvNode || uv();
42-
const blurred_image = vec4( 0. ).toVar();
39+
const blurred_image = vec4( 0. );
4340

4441
Loop( { start: 0., end: repeats, type: 'float' }, ( { i } ) => {
4542

4643
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 ) ) );
4744
const uv2 = vec2( targetUV.add( q.mul( bluramount ) ) );
48-
blurred_image.addAssign( draw( uv2 ) );
45+
blurred_image.addAssign( tap( uv2 ) );
4946

5047
} );
5148

examples/webgpu_reflection_blurred.html

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
<script type="module">
2727

2828
import * as THREE from 'three/webgpu';
29-
import { Fn, vec4, fract, abs, uniform, pow, color, max, length, rangeFogFactor, sub, reflector, normalWorld, hue, time, mix, positionWorld } from 'three/tsl';
29+
import { Fn, vec4, fract, sample, abs, uniform, pow, color, max, length, rangeFogFactor, sub, reflector, normalWorld, hue, time, mix, positionWorld } from 'three/tsl';
3030

3131
import { hashBlur } from 'three/addons/tsl/display/hashBlur.js';
3232

@@ -138,11 +138,21 @@
138138
const radiusRange = mix( 0.01, 0.1, radius ); // range [ 0.01, 0.1 ]
139139
const roughnessRange = mix( 0.3, 0.03, roughness ); // range [ 0.03, 0.3 ]
140140

141+
// mask the sample
142+
143+
const maskReflection = sample( ( uv ) => {
144+
145+
const sample = reflection.sample( uv );
146+
const mask = reflectionDepth.sample( uv );
147+
148+
return vec4( sample.rgb, sample.a.mul( mask.r ) );
149+
150+
}, reflection.uvNode );
151+
141152
// blur the reflection
142153

143-
const reflectionBlurred = hashBlur( reflection, radiusRange, {
154+
const reflectionBlurred = hashBlur( maskReflection, radiusRange, {
144155
repeats: 40,
145-
mask: reflectionDepth,
146156
premultipliedAlpha: true
147157
} );
148158

src/nodes/utils/RTTNode.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ export const rtt = ( node, ...params ) => nodeObject( new RTTNode( nodeObject( n
271271
*/
272272
export const convertToTexture = ( node, ...params ) => {
273273

274-
if ( node.isTextureNode ) return node;
274+
if ( node.isSampleNode || node.isTextureNode ) return node;
275275
if ( node.isPassNode ) return node.getTextureNode();
276276

277277
return rtt( node, ...params );

src/nodes/utils/SampleNode.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,22 @@ class SampleNode extends Node {
2626
* Creates an instance of SampleNode.
2727
*
2828
* @param {Function} callback - The function to be called when sampling. Should accept a UV node and return a value.
29+
* @param {?Node<vec2>} [uvNode=null] - The UV node to be used in the texture sampling.
2930
*/
30-
constructor( callback ) {
31+
constructor( callback, uvNode = null ) {
3132

3233
super();
3334

3435
this.callback = callback;
3536

37+
/**
38+
* Represents the texture coordinates.
39+
*
40+
* @type {?Node<vec2|vec3>}
41+
* @default null
42+
*/
43+
this.uvNode = uvNode;
44+
3645
/**
3746
* This flag can be used for type testing.
3847
*
@@ -76,6 +85,7 @@ export default SampleNode;
7685
*
7786
* @function
7887
* @param {Function} callback - The function to be called when sampling. Should accept a UV node and return a value.
88+
* @param {?Node<vec2>} [uv=null] - The UV node to be used in the texture sampling.
7989
* @returns {SampleNode} The created SampleNode instance wrapped as a node object.
8090
*/
81-
export const sample = ( callback ) => nodeObject( new SampleNode( callback ) );
91+
export const sample = ( callback, uv = null ) => nodeObject( new SampleNode( callback, nodeObject( uv ) ) );

0 commit comments

Comments
 (0)