Skip to content

Commit ff9b259

Browse files
authored
TSL: Add boxBlur(). (#31556)
* TSL: Add `boxBlur()`. * TSL: Use `convertToTexture()` in `boxBlur()`. * Update boxBlur.js Fix typos.
1 parent c50e257 commit ff9b259

File tree

3 files changed

+71
-5
lines changed

3 files changed

+71
-5
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { Fn, vec2, uv, Loop, vec4, premultiplyAlpha, unpremultiplyAlpha, max, int, textureSize, nodeObject, convertToTexture } from 'three/tsl';
2+
3+
/**
4+
* Applies a box blur effect to the given texture node.
5+
*
6+
* Compared to Gaussian blur, box blur produces a more blocky result but with better performance when correctly
7+
* configured. It is intended for mobile devices or performance restricted use cases where Gaussian is too heavy.
8+
*
9+
* The (kernel) `size` parameter should be small (1, 2 or 3) since it determines the number of samples based on (size * 2 + 1)^2.
10+
* This implementation uses a single pass approach so the kernel is not applied as a seprabable filter. That means larger
11+
* kernels won't perform well. Use Gaussian instead if you need a more high-quality blur.
12+
*
13+
* To produce wider blurs, increase the `separation` parameter instead which has no influence on the performance.
14+
*
15+
* Reference: {@link https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/box-blur.frag}.
16+
*
17+
* @function
18+
* @param {Node<vec4>} textureNode - The texture node that should be blurred.
19+
* @param {Object} [options={}] - Additional options for the hash blur effect.
20+
* @param {Node<int>} [options.size=int(1)] - Controls the blur's kernel. For performant results, the range should within [1, 3].
21+
* @param {Node<int>} [options.separation=int(1)] - Spreads out the blur without having to sample additional fragments. Ranges from [1, Infinity].
22+
* @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
23+
* @return {Node<vec4>} The blurred texture node.
24+
*/
25+
export const boxBlur = /*#__PURE__*/ Fn( ( [ textureNode, options = {} ] ) => {
26+
27+
textureNode = convertToTexture( textureNode );
28+
const size = nodeObject( options.size ) || int( 1 );
29+
const separation = nodeObject( options.separation ) || int( 1 );
30+
const premultipliedAlpha = options.premultipliedAlpha || false;
31+
32+
const tap = ( uv ) => {
33+
34+
const sample = textureNode.sample( uv );
35+
return premultipliedAlpha ? premultiplyAlpha( sample ) : sample;
36+
37+
};
38+
39+
const targetUV = textureNode.uvNode || uv();
40+
41+
const result = vec4( 0 );
42+
const sep = max( separation, 1 );
43+
const count = int( 0 );
44+
const pixelStep = vec2( 1 ).div( textureSize( textureNode ) );
45+
46+
Loop( { start: size.negate(), end: size, name: 'i', condition: '<=' }, ( { i } ) => {
47+
48+
Loop( { start: size.negate(), end: size, name: 'j', condition: '<=' }, ( { j } ) => {
49+
50+
const uvs = targetUV.add( vec2( i, j ).mul( pixelStep ).mul( sep ) );
51+
result.addAssign( tap( uvs ) );
52+
count.addAssign( 1 );
53+
54+
} );
55+
56+
} );
57+
58+
result.divAssign( count );
59+
60+
return premultipliedAlpha ? unpremultiplyAlpha( result ) : result;
61+
62+
} );
935 Bytes
Loading

examples/webgpu_postprocessing_dof_basic.html

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
import * as THREE from 'three/webgpu';
4242
import { mix, pass, renderOutput, smoothstep, uniform, vec3 } from 'three/tsl';
43-
import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js';
43+
import { boxBlur } from 'three/addons/tsl/display/boxBlur.js';
4444
import { fxaa } from 'three/addons/tsl/display/FXAANode.js';
4545

4646
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
@@ -115,20 +115,23 @@
115115

116116
// DOF uniforms
117117

118-
const maxBlur = uniform( 2 ); // maximum amount of blur
118+
const blurSize = uniform( 2 ); // determines the kernel size of the blur
119+
const blurSpread = uniform( 4 ); // determines how far the blur is spread
119120
const minDistance = uniform( 1 ); // all positions at or below minDistance will be completely in focus.
120121
const maxDistance = uniform( 3 ); // all positions at or beyond maxDistance will be completely out of focus.
121122

122123
// beauty and blur/out-of-focus pass
123124

124125
const scenePass = pass( scene, camera );
126+
127+
const scenePassColor = scenePass.getTextureNode();
125128
const scenePassViewZ = scenePass.getViewZNode();
126-
const scenePassBlurred = gaussianBlur( scenePass, maxBlur );
129+
const scenePassBlurred = boxBlur( scenePassColor, { size: blurSize, separation: blurSpread } );
127130

128131
// simple DOF from https://lettier.github.io/3d-game-shaders-for-beginners/depth-of-field.html
129132

130133
const blur = smoothstep( minDistance, maxDistance, scenePassViewZ.sub( focusPointView.z ).abs() );
131-
const dofPass = mix( scenePass, scenePassBlurred, blur );
134+
const dofPass = mix( scenePassColor, scenePassBlurred, blur );
132135

133136
const outputPass = renderOutput( dofPass );
134137
const fxaaPass = fxaa( outputPass );
@@ -140,7 +143,8 @@
140143
const gui = new GUI();
141144
gui.add( minDistance, 'value', 0, 3 ).name( 'min distance' );
142145
gui.add( maxDistance, 'value', 0, 5 ).name( 'max distance' );
143-
gui.add( maxBlur, 'value', 0, 5 ).name( 'max blur' );
146+
gui.add( blurSize, 'value', 1, 3, 1 ).name( 'blur size' );
147+
gui.add( blurSpread, 'value', 1, 7, 1 ).name( 'blur spread' );
144148

145149
//
146150

0 commit comments

Comments
 (0)