Ver código fonte

Addons: Add `DepthOfFieldNode`. (#28740)

* Addons: Add `DepthOfFieldNode`.

* DepthOfFieldNode: Fix runtime error.

* E2E: Update screenshot.

* Examples: Align code to previous demo.

* Examples: Use instancing in DOF demo.
Michael Herzog 1 ano atrás
pai
commit
4feb1372ea

+ 1 - 0
examples/files.json

@@ -369,6 +369,7 @@
 		"webgpu_portal",
 		"webgpu_postprocessing_afterimage",
 		"webgpu_postprocessing_anamorphic",
+		"webgpu_postprocessing_dof",
 		"webgpu_postprocessing_sobel",
 		"webgpu_reflection",
 		"webgpu_rtt",

+ 1 - 0
examples/jsm/nodes/Nodes.js

@@ -126,6 +126,7 @@ export { default as GaussianBlurNode, gaussianBlur } from './display/GaussianBlu
 export { default as AfterImageNode, afterImage } from './display/AfterImageNode.js';
 export { default as AnamorphicNode, anamorphic } from './display/AnamorphicNode.js';
 export { default as SobelOperatorNode, sobel } from './display/SobelOperatorNode.js';
+export { default as DepthOfFieldNode, dof } from './display/DepthOfFieldNode.js';
 
 export { default as PassNode, pass, texturePass, depthPass } from './display/PassNode.js';
 

+ 119 - 0
examples/jsm/nodes/display/DepthOfFieldNode.js

@@ -0,0 +1,119 @@
+import TempNode from '../core/TempNode.js';
+import { uv } from '../accessors/UVNode.js';
+import { addNodeElement, tslFn, nodeObject, vec2, vec4 } from '../shadernode/ShaderNode.js';
+import { NodeUpdateType } from '../core/constants.js';
+import { uniform } from '../core/UniformNode.js';
+import { clamp } from '../math/MathNode.js';
+
+class DepthOfFieldNode extends TempNode {
+
+	constructor( textureNode, viewZNode, focus = 1, aperture = 0.025, maxblur = 1 ) {
+
+		super();
+
+		this.textureNode = textureNode;
+		this.viewZNode = viewZNode;
+
+		this.focus = uniform( focus );
+		this.aperture = uniform( aperture );
+		this.maxblur = uniform( maxblur );
+		this._aspect = uniform( 0 );
+
+		this.updateBeforeType = NodeUpdateType.RENDER;
+
+	}
+
+	updateBefore() {
+
+		const map = this.textureNode.value;
+
+		this._aspect.value = map.image.width / map.image.height;
+
+	}
+
+	setup() {
+
+		const { textureNode } = this;
+
+		const uvNode = textureNode.uvNode || uv();
+
+		const sampleTexture = ( uv ) => this.textureNode.uv( uv );
+
+		const dof = tslFn( () => {
+
+			const aspectcorrect = vec2( 1.0, this._aspect );
+
+			const factor = this.focus.add( this.viewZNode );
+
+			const dofblur = vec2( clamp( factor.mul( this.aperture ), this.maxblur.negate(), this.maxblur ) );
+
+			const dofblur9 = dofblur.mul( 0.9 );
+			const dofblur7 = dofblur.mul( 0.7 );
+			const dofblur4 = dofblur.mul( 0.4 );
+
+			let col = vec4( 0.0 );
+
+			col = col.add( sampleTexture( uvNode ) );
+
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.0, 0.4 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.15, 0.37 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.37, 0.15 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.40, 0.0 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.37, - 0.15 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.15, - 0.37 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.0, - 0.4 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.15, 0.37 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.37, 0.15 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.4, 0.0 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.37, - 0.15 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.15, - 0.37 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.15, 0.37 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.37, 0.15 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.37, - 0.15 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.15, - 0.37 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.15, 0.37 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.37, 0.15 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.37, - 0.15 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.15, - 0.37 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.40, 0.0 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.0, - 0.4 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.4, 0.0 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.0, 0.4 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.4, 0.0 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.0, - 0.4 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.4, 0.0 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
+			col = col.add( sampleTexture( uvNode.add( vec2( 0.0, 0.4 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
+
+			col = col.div( 41 );
+			col.a = 1;
+
+			return vec4( col );
+
+
+		} );
+
+		const outputNode = dof();
+
+		return outputNode;
+
+	}
+
+}
+
+export const dof = ( node, viewZNode, focus, apeture, maxblur ) => nodeObject( new DepthOfFieldNode( nodeObject( node ), nodeObject( viewZNode ), focus, apeture, maxblur ) );
+
+addNodeElement( 'dof', dof );
+
+export default DepthOfFieldNode;

BIN
examples/screenshots/webgpu_postprocessing_dof.jpg


+ 218 - 0
examples/webgpu_postprocessing_dof.html

@@ -0,0 +1,218 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgpu - postprocessing - dof</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.module.js",
+					"three/addons/": "./jsm/",
+					"three/nodes": "./jsm/nodes/Nodes.js"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+
+			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
+			import PostProcessing from 'three/addons/renderers/common/PostProcessing.js';
+			import { MeshBasicNodeMaterial, cubeTexture } from 'three/nodes';
+			import { pass } from 'three/nodes';
+
+			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
+			import Stats from 'three/addons/libs/stats.module.js';
+
+			//
+
+			let camera, scene, renderer, mesh, stats;
+
+			let mouseX = 0, mouseY = 0;
+
+			let windowHalfX = window.innerWidth / 2;
+			let windowHalfY = window.innerHeight / 2;
+
+			let width = window.innerWidth;
+			let height = window.innerHeight;
+
+			let postProcessing;
+
+			const color = new THREE.Color();
+
+			init();
+
+			function init() {
+
+				camera = new THREE.PerspectiveCamera( 70, width / height, 1, 3000 );
+				camera.position.z = 200;
+
+				scene = new THREE.Scene();
+
+				const path = 'textures/cube/SwedishRoyalCastle/';
+				const format = '.jpg';
+				const urls = [
+					path + 'px' + format, path + 'nx' + format,
+					path + 'py' + format, path + 'ny' + format,
+					path + 'pz' + format, path + 'nz' + format
+				];
+
+				const textureCube = new THREE.CubeTextureLoader().load( urls );
+				const cubeTextureNode = cubeTexture( textureCube );
+
+				const geometry = new THREE.SphereGeometry( 60, 20, 10 );
+				const material = new MeshBasicNodeMaterial();
+				material.colorNode = cubeTextureNode;
+
+				const xgrid = 14, ygrid = 9, zgrid = 14;
+				const count = xgrid * ygrid * zgrid;
+
+				mesh = new THREE.InstancedMesh( geometry, material, count );
+				scene.add( mesh );
+
+				const matrix = new THREE.Matrix4();
+
+				let index = 0;
+
+				for ( let i = 0; i < xgrid; i ++ ) {
+
+					for ( let j = 0; j < ygrid; j ++ ) {
+
+						for ( let k = 0; k < zgrid; k ++ ) {
+
+							const x = 200 * ( i - xgrid / 2 );
+							const y = 200 * ( j - ygrid / 2 );
+							const z = 200 * ( k - zgrid / 2 );
+
+							mesh.setMatrixAt( index, matrix.identity().setPosition( x, y, z ) );
+							mesh.setColorAt( index, color );
+							index ++;
+
+						}
+
+					}
+
+				}
+
+				// renderer
+
+				renderer = new WebGPURenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setAnimationLoop( animate );
+				document.body.appendChild( renderer.domElement );
+
+				// post processing
+
+				postProcessing = new PostProcessing( renderer );
+
+				const scenePass = pass( scene, camera );
+
+				const scenePassColor = scenePass.getTextureNode();
+				const scenePassViewZ = scenePass.getViewZNode();
+
+				const dofPass = scenePassColor.dof( scenePassViewZ );
+
+				postProcessing.outputNode = dofPass;
+
+				// controls
+
+				renderer.domElement.style.touchAction = 'none';
+				renderer.domElement.addEventListener( 'pointermove', onPointerMove );
+
+				window.addEventListener( 'resize', onWindowResize );
+
+				// stats
+
+				stats = new Stats();
+				document.body.appendChild( stats.dom );
+
+				// gui
+
+				const effectController = {
+
+					focus: 500.0,
+					aperture: 5,
+					maxblur: 0.01
+
+				};
+
+				function updateEffect( ) {
+
+					dofPass.focus.value = effectController.focus;
+					dofPass.aperture.value = effectController.aperture * 0.00001;
+					dofPass.maxblur.value = effectController.maxblur;
+
+				}
+
+				const gui = new GUI();
+				gui.add( effectController, 'focus', 10.0, 3000.0, 10 ).onChange( updateEffect );
+				gui.add( effectController, 'aperture', 0, 10, 0.1 ).onChange( updateEffect );
+				gui.add( effectController, 'maxblur', 0.0, 0.01, 0.001 ).onChange( updateEffect );
+
+				updateEffect();
+
+			}
+
+			function onPointerMove( event ) {
+
+				if ( event.isPrimary === false ) return;
+
+				mouseX = event.clientX - windowHalfX;
+				mouseY = event.clientY - windowHalfY;
+
+			}
+
+			function onWindowResize() {
+
+				windowHalfX = window.innerWidth / 2;
+				windowHalfY = window.innerHeight / 2;
+
+				width = window.innerWidth;
+				height = window.innerHeight;
+
+				camera.aspect = width / height;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( width, height );
+
+			}
+
+			function animate() {
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+
+				const time = Date.now() * 0.00005;
+
+				camera.position.x += ( mouseX - camera.position.x ) * 0.036;
+				camera.position.y += ( - ( mouseY ) - camera.position.y ) * 0.036;
+
+				camera.lookAt( scene.position );
+
+				for ( let i = 0; i < mesh.count; i ++ ) {
+
+					const h = ( 360 * ( i / mesh.count + time ) % 360 ) / 360;
+					color.setHSL( h, 1, 0.5 );
+					mesh.setColorAt( i, color );
+
+				}
+
+				mesh.instanceColor.needsUpdate = true;
+
+				postProcessing.render();
+
+			}
+
+		</script>
+	</body>
+</html>