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

Examples: Improve `webgpu_postprocessing_ao`. (#29843)

Michael Herzog 1 год назад
Родитель
Сommit
296440a71f

+ 39 - 5
examples/jsm/tsl/display/DenoiseNode.js

@@ -1,5 +1,6 @@
-import { Vector2, Vector3 } from 'three';
-import { getNormalFromDepth, getViewPosition, convertToTexture, TempNode, nodeObject, Fn, float, NodeUpdateType, uv, uniform, Loop, luminance, vec2, vec3, vec4, uniformArray, int, dot, max, pow, abs, If, textureSize, sin, cos, mat2, PI } from 'three/tsl';
+import { DataTexture, RepeatWrapping, Vector2, Vector3 } from 'three';
+import { texture, getNormalFromDepth, getViewPosition, convertToTexture, TempNode, nodeObject, Fn, float, NodeUpdateType, uv, uniform, Loop, luminance, vec2, vec3, vec4, uniformArray, int, dot, max, pow, abs, If, textureSize, sin, cos, mat2, PI } from 'three/tsl';
+import { SimplexNoise } from '../../math/SimplexNoise.js';
 
 class DenoiseNode extends TempNode {
 
@@ -9,14 +10,15 @@ class DenoiseNode extends TempNode {
 
 	}
 
-	constructor( textureNode, depthNode, normalNode, noiseNode, camera ) {
+	constructor( textureNode, depthNode, normalNode, camera ) {
 
 		super( 'vec4' );
 
 		this.textureNode = textureNode;
 		this.depthNode = depthNode;
 		this.normalNode = normalNode;
-		this.noiseNode = noiseNode;
+
+		this.noiseNode = texture( generateDefaultNoise() );
 
 		this.lumaPhi = uniform( 5 );
 		this.depthPhi = uniform( 5 );
@@ -180,4 +182,36 @@ function generateDenoiseSamples( numSamples, numRings, radiusExponent ) {
 
 }
 
-export const denoise = ( node, depthNode, normalNode, noiseNode, camera ) => nodeObject( new DenoiseNode( convertToTexture( node ), nodeObject( depthNode ), nodeObject( normalNode ), nodeObject( noiseNode ), camera ) );
+function generateDefaultNoise( size = 64 ) {
+
+	const simplex = new SimplexNoise();
+
+	const arraySize = size * size * 4;
+	const data = new Uint8Array( arraySize );
+
+	for ( let i = 0; i < size; i ++ ) {
+
+		for ( let j = 0; j < size; j ++ ) {
+
+			const x = i;
+			const y = j;
+
+			data[ ( i * size + j ) * 4 ] = ( simplex.noise( x, y ) * 0.5 + 0.5 ) * 255;
+			data[ ( i * size + j ) * 4 + 1 ] = ( simplex.noise( x + size, y ) * 0.5 + 0.5 ) * 255;
+			data[ ( i * size + j ) * 4 + 2 ] = ( simplex.noise( x, y + size ) * 0.5 + 0.5 ) * 255;
+			data[ ( i * size + j ) * 4 + 3 ] = ( simplex.noise( x + size, y + size ) * 0.5 + 0.5 ) * 255;
+
+		}
+
+	}
+
+	const noiseTexture = new DataTexture( data, size, size );
+	noiseTexture.wrapS = RepeatWrapping;
+	noiseTexture.wrapT = RepeatWrapping;
+	noiseTexture.needsUpdate = true;
+
+	return noiseTexture;
+
+}
+
+export const denoise = ( node, depthNode, normalNode, camera ) => nodeObject( new DenoiseNode( convertToTexture( node ), nodeObject( depthNode ), nodeObject( normalNode ), camera ) );

BIN
examples/models/gltf/minimalistic_modern_bedroom.glb


BIN
examples/screenshots/webgpu_postprocessing_ao.jpg


+ 48 - 67
examples/webgpu_postprocessing_ao.html

@@ -7,6 +7,13 @@
 		<link type="text/css" rel="stylesheet" href="main.css">
 	</head>
 	<body>
+
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgpu - postprocessing - ambient occlusion<br />
+			<a href="https://skfb.ly/oCnNx" target="_blank" rel="noopener">Minimalistic Modern Bedroom</a> by 
+			<a href="https://sketchfab.com/dylanheyes" target="_blank" rel="noopener">dylanheyes</a> is licensed under <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noopener">Creative Commons Attribution</a>.<br />
+		</div>
+
 		<script type="importmap">
 			{
 				"imports": {
@@ -20,20 +27,19 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { pass, mrt, output, transformedNormalView, texture } from 'three/tsl';
+			import { pass, mrt, output, normalView } from 'three/tsl';
 			import { ao } from 'three/addons/tsl/display/GTAONode.js';
 			import { denoise } from 'three/addons/tsl/display/DenoiseNode.js';
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
-			import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
+			import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
 			import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
-			import { SimplexNoise } from 'three/addons/math/SimplexNoise.js';
 
 			import Stats from 'three/addons/libs/stats.module.js';
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 
-			let camera, scene, renderer, postProcessing, controls, clock, stats, mixer;
+			let camera, scene, renderer, postProcessing, controls, stats;
 
 			let aoPass, denoisePass, blendPassAO, blendPassDenoise, scenePassColor;
 
@@ -43,7 +49,7 @@
 				radius: 0.25,
 				scale: 1,
 				thickness: 1,
-				denoised: true,
+				denoised: false,
 				enabled: true,
 				denoiseRadius: 5,
 				lumaPhi: 5,
@@ -55,30 +61,36 @@
 
 			async function init() {
 
-				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 100 );
-				camera.position.set( 5, 2, 8 );
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 50 );
+				camera.position.set( 1, 1.3, 5 );
 
 				scene = new THREE.Scene();
-				scene.background = new THREE.Color( 0xbfe3dd );
-
-				clock = new THREE.Clock();
-
-				const hdrloader = new RGBELoader();
-				const envMap = await hdrloader.loadAsync( 'textures/equirectangular/quarry_01_1k.hdr' );
-				envMap.mapping = THREE.EquirectangularReflectionMapping;
-
-				scene.environment = envMap;
 
 				renderer = new THREE.WebGPURenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setAnimationLoop( animate );
 				document.body.appendChild( renderer.domElement );
 
+				await renderer.init();
+
+				const environment = new RoomEnvironment();
+				const pmremGenerator = new THREE.PMREMGenerator( renderer );
+
+				scene.background = new THREE.Color( 0x666666 );
+				scene.environment = pmremGenerator.fromScene( environment ).texture;
+				environment.dispose();
+				pmremGenerator.dispose();
+
+				//
+
 				controls = new OrbitControls( camera, renderer.domElement );
-				controls.target.set( 0, 0.5, 0 );
+				controls.target.set( 0, 0.5, - 1 );
 				controls.update();
 				controls.enablePan = false;
 				controls.enableDamping = true;
+				controls.minDistance = 2;
+				controls.maxDistance = 8;
 
 				stats = new Stats();
 				document.body.appendChild( stats.dom );
@@ -90,7 +102,7 @@
 				const scenePass = pass( scene, camera );
 				scenePass.setMRT( mrt( {
 					output: output,
-					normal: transformedNormalView
+					normal: normalView
 				} ) );
 
 				scenePassColor = scenePass.getTextureNode( 'output' );
@@ -100,15 +112,15 @@
 				// ao
 
 				aoPass = ao( scenePassDepth, scenePassNormal, camera );
+				aoPass.resolutionScale = 0.5;
 				blendPassAO = aoPass.getTextureNode().mul( scenePassColor );
 
 				// denoise (optional)
 
-				const noiseTexture = texture( generateNoise() );
-				denoisePass = denoise( aoPass.getTextureNode(), scenePassDepth, scenePassNormal, noiseTexture, camera );
+				denoisePass = denoise( aoPass.getTextureNode(), scenePassDepth, scenePassNormal, camera );
 				blendPassDenoise = denoisePass.mul( scenePassColor );
 
-				postProcessing.outputNode = blendPassDenoise;
+				postProcessing.outputNode = blendPassAO;
 
 				//
 
@@ -119,15 +131,24 @@
 				loader.setDRACOLoader( dracoLoader );
 				loader.setPath( 'models/gltf/' );
 
-				const gltf = await loader.loadAsync( 'LittlestTokyo.glb' );
+				const gltf = await loader.loadAsync( 'minimalistic_modern_bedroom.glb' );
 
 				const model = gltf.scene;
-				model.position.set( 1, 1, 0 );
-				model.scale.set( 0.01, 0.01, 0.01 );
+				model.position.set( 0, 1, 0 );
 				scene.add( model );
 
-				mixer = new THREE.AnimationMixer( model );
-				mixer.clipAction( gltf.animations[ 0 ] ).play();
+				model.traverse( ( o ) => {
+
+					// Transparent objects (e.g. loaded via GLTFLoader) might have "depthWrite" set to "false".
+					// This is wanted when rendering the beauty pass however it produces wrong results when computing
+					// AO since depth and normal data are out of sync. Computing normals from depth by not using MRT
+					// can mitigate the issue although the depth information (and thus the normals) are not correct in
+					// first place. Besides, normal estimation is computationally more expensive than just sampling a
+					// normal texture. So depending on your scene, consider to enable "depthWrite" for all transparent objects.
+
+					if ( o.material ) o.material.depthWrite = true;
+
+				} );
 
 				window.addEventListener( 'resize', onWindowResize );
 
@@ -140,13 +161,13 @@
 				gui.add( params, 'radius' ).min( 0.01 ).max( 1 ).onChange( updateParameters );
 				gui.add( params, 'scale' ).min( 0.01 ).max( 2 ).onChange( updateParameters );
 				gui.add( params, 'thickness' ).min( 0.01 ).max( 2 ).onChange( updateParameters );
-				gui.add( params, 'denoised' ).onChange( updatePassChain );
 				gui.add( params, 'enabled' ).onChange( updatePassChain );
 				const folder = gui.addFolder( 'Denoise settings' );
 				folder.add( params, 'denoiseRadius' ).min( 0.01 ).max( 10 ).name( 'radius' ).onChange( updateParameters );
 				folder.add( params, 'lumaPhi' ).min( 0.01 ).max( 10 ).onChange( updateParameters );
 				folder.add( params, 'depthPhi' ).min( 0.01 ).max( 10 ).onChange( updateParameters );
 				folder.add( params, 'normalPhi' ).min( 0.01 ).max( 10 ).onChange( updateParameters );
+				folder.add( params, 'denoised' ).name( 'enabled' ).onChange( updatePassChain );
 
 			}
 
@@ -190,38 +211,6 @@
 
 			}
 
-			function generateNoise( size = 64 ) {
-
-				const simplex = new SimplexNoise();
-
-				const arraySize = size * size * 4;
-				const data = new Uint8Array( arraySize );
-
-				for ( let i = 0; i < size; i ++ ) {
-
-					for ( let j = 0; j < size; j ++ ) {
-
-						const x = i;
-						const y = j;
-
-						data[ ( i * size + j ) * 4 ] = ( simplex.noise( x, y ) * 0.5 + 0.5 ) * 255;
-						data[ ( i * size + j ) * 4 + 1 ] = ( simplex.noise( x + size, y ) * 0.5 + 0.5 ) * 255;
-						data[ ( i * size + j ) * 4 + 2 ] = ( simplex.noise( x, y + size ) * 0.5 + 0.5 ) * 255;
-						data[ ( i * size + j ) * 4 + 3 ] = ( simplex.noise( x + size, y + size ) * 0.5 + 0.5 ) * 255;
-
-					}
-
-				}
-
-				const noiseTexture = new THREE.DataTexture( data, size, size );
-				noiseTexture.wrapS = THREE.RepeatWrapping;
-				noiseTexture.wrapT = THREE.RepeatWrapping;
-				noiseTexture.needsUpdate = true;
-
-				return noiseTexture;
-
-			}
-
 			function onWindowResize() {
 
 				const width = window.innerWidth;
@@ -236,14 +225,6 @@
 
 			function animate() {
 
-				const delta = clock.getDelta();
-
-				if ( mixer ) {
-
-					mixer.update( delta );
-
-				}
-
 				controls.update();
 
 				postProcessing.render();

粤ICP备19079148号