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

TSL: Add `boxBlur()`. (#31556)

* TSL: Add `boxBlur()`.

* TSL: Use `convertToTexture()` in `boxBlur()`.

* Update boxBlur.js

Fix typos.
Michael Herzog 8 месяцев назад
Родитель
Сommit
ff9b259010

+ 62 - 0
examples/jsm/tsl/display/boxBlur.js

@@ -0,0 +1,62 @@
+import { Fn, vec2, uv, Loop, vec4, premultiplyAlpha, unpremultiplyAlpha, max, int, textureSize, nodeObject, convertToTexture } from 'three/tsl';
+
+/**
+ * Applies a box blur effect to the given texture node.
+ *
+ * Compared to Gaussian blur, box blur produces a more blocky result but with better performance when correctly
+ * configured. It is intended for mobile devices or performance restricted use cases where Gaussian is too heavy.
+ *
+ * The (kernel) `size` parameter should be small (1, 2 or 3) since it determines the number of samples based on (size * 2 + 1)^2.
+ * This implementation uses a single pass approach so the kernel is not applied as a seprabable filter. That means larger
+ * kernels won't perform well. Use Gaussian instead if you need a more high-quality blur.
+ *
+ * To produce wider blurs, increase the `separation` parameter instead which has no influence on the performance.
+ *
+ * Reference: {@link https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/box-blur.frag}.
+ *
+ * @function
+ * @param {Node<vec4>} textureNode - The texture node that should be blurred.
+ * @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 {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 premultipliedAlpha = options.premultipliedAlpha || false;
+
+	const tap = ( uv ) => {
+
+		const sample = textureNode.sample( uv );
+		return premultipliedAlpha ? premultiplyAlpha( sample ) : sample;
+
+	};
+
+	const targetUV = textureNode.uvNode || uv();
+
+	const result = vec4( 0 );
+	const sep = max( separation, 1 );
+	const count = int( 0 );
+	const pixelStep = vec2( 1 ).div( textureSize( textureNode ) );
+
+	Loop( { start: size.negate(), end: size, name: 'i', condition: '<=' }, ( { i } ) => {
+
+		Loop( { start: size.negate(), end: size, name: 'j', condition: '<=' }, ( { j } ) => {
+
+			const uvs = targetUV.add( vec2( i, j ).mul( pixelStep ).mul( sep ) );
+			result.addAssign( tap( uvs ) );
+			count.addAssign( 1 );
+
+		} );
+
+	} );
+
+	result.divAssign( count );
+
+	return premultipliedAlpha ? unpremultiplyAlpha( result ) : result;
+
+} );

BIN
examples/screenshots/webgpu_postprocessing_dof_basic.jpg


+ 9 - 5
examples/webgpu_postprocessing_dof_basic.html

@@ -40,7 +40,7 @@
 
 			import * as THREE from 'three/webgpu';
 			import { mix, pass, renderOutput, smoothstep, uniform, vec3 } from 'three/tsl';
-			import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js';
+			import { boxBlur } from 'three/addons/tsl/display/boxBlur.js';
 			import { fxaa } from 'three/addons/tsl/display/FXAANode.js';
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
@@ -115,20 +115,23 @@
 
 				// DOF uniforms
 
-				const maxBlur = uniform( 2 ); // maximum amount of blur
+				const blurSize = uniform( 2 ); // determines the kernel size of the blur
+				const blurSpread = uniform( 4 ); // determines how far the blur is spread
 				const minDistance = uniform( 1 ); // all positions at or below minDistance will be completely in focus.
 				const maxDistance = uniform( 3 ); // all positions at or beyond maxDistance will be completely out of focus.
 
 				// beauty and blur/out-of-focus pass
 
 				const scenePass = pass( scene, camera );
+			
+				const scenePassColor = scenePass.getTextureNode();
 				const scenePassViewZ = scenePass.getViewZNode();
-				const scenePassBlurred = gaussianBlur( scenePass, maxBlur );
+				const scenePassBlurred = boxBlur( scenePassColor, { size: blurSize, separation: blurSpread } );
 
 				// simple DOF from https://lettier.github.io/3d-game-shaders-for-beginners/depth-of-field.html
 
 				const blur = smoothstep( minDistance, maxDistance, scenePassViewZ.sub( focusPointView.z ).abs() );
-				const dofPass = mix( scenePass, scenePassBlurred, blur );
+				const dofPass = mix( scenePassColor, scenePassBlurred, blur );
 
 				const outputPass = renderOutput( dofPass );
 				const fxaaPass = fxaa( outputPass );
@@ -140,7 +143,8 @@
 				const gui = new GUI();
 				gui.add( minDistance, 'value', 0, 3 ).name( 'min distance' );
 				gui.add( maxDistance, 'value', 0, 5 ).name( 'max distance' );
-				gui.add( maxBlur, 'value', 0, 5 ).name( 'max blur' );
+				gui.add( blurSize, 'value', 1, 3, 1 ).name( 'blur size' );
+				gui.add( blurSpread, 'value', 1, 7, 1 ).name( 'blur spread' );
 
 				//
 

粤ICP备19079148号