Przeglądaj źródła

Node: Add `FilmNode`. (#28770)

* Node: Add `FilmNode`.

* E2E: Update screenshot.

* FilmNode: Fix grayscale, use `intensityNode`.

* E2E: Add example to exception list.

* Update FilmNode.js

* cleanup

* E2E: Update screenshot.

* Update FilmNode.js

---------

Co-authored-by: sunag <[email protected]>
Michael Herzog 1 rok temu
rodzic
commit
af4898d83d

+ 14 - 24
examples/misc_controls_fly.html

@@ -30,7 +30,8 @@
 		<script type="importmap">
 			{
 				"imports": {
-					"three": "../build/three.module.js",
+					"three": "../build/three.webgpu.js",
+					"three/tsl": "../build/three.webgpu.js",
 					"three/addons/": "./jsm/"
 				}
 			}
@@ -39,14 +40,10 @@
 		<script type="module">
 
 			import * as THREE from 'three';
+			import { pass } from 'three/tsl';
 
 			import Stats from 'three/addons/libs/stats.module.js';
-
 			import { FlyControls } from 'three/addons/controls/FlyControls.js';
-			import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
-			import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
-			import { FilmPass } from 'three/addons/postprocessing/FilmPass.js';
-			import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
 
 			const radius = 6371;
 			const tilt = 0.41;
@@ -63,7 +60,7 @@
 			let geometry, meshPlanet, meshClouds, meshMoon;
 			let dirLight;
 
-			let composer;
+			let postProcessing;
 
 			const textureLoader = new THREE.TextureLoader();
 
@@ -173,17 +170,14 @@
 				starsGeometry[ 1 ].setAttribute( 'position', new THREE.Float32BufferAttribute( vertices2, 3 ) );
 
 				const starsMaterials = [
-					new THREE.PointsMaterial( { color: 0x9c9c9c, size: 2, sizeAttenuation: false } ),
-					new THREE.PointsMaterial( { color: 0x9c9c9c, size: 1, sizeAttenuation: false } ),
-					new THREE.PointsMaterial( { color: 0x7c7c7c, size: 2, sizeAttenuation: false } ),
-					new THREE.PointsMaterial( { color: 0x838383, size: 1, sizeAttenuation: false } ),
-					new THREE.PointsMaterial( { color: 0x5a5a5a, size: 2, sizeAttenuation: false } ),
-					new THREE.PointsMaterial( { color: 0x5a5a5a, size: 1, sizeAttenuation: false } )
+					new THREE.PointsMaterial( { color: 0x9c9c9c } ),
+					new THREE.PointsMaterial( { color: 0x838383 } ),
+					new THREE.PointsMaterial( { color: 0x5a5a5a } )
 				];
 
 				for ( let i = 10; i < 30; i ++ ) {
 
-					const stars = new THREE.Points( starsGeometry[ i % 2 ], starsMaterials[ i % 6 ] );
+					const stars = new THREE.Points( starsGeometry[ i % 2 ], starsMaterials[ i % 3 ] );
 
 					stars.rotation.x = Math.random() * 6;
 					stars.rotation.y = Math.random() * 6;
@@ -197,7 +191,7 @@
 
 				}
 
-				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer = new THREE.WebGPURenderer( { antialias: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
 				renderer.setAnimationLoop( animate );
@@ -222,15 +216,12 @@
 
 				// postprocessing
 
-				const renderModel = new RenderPass( scene, camera );
-				const effectFilm = new FilmPass( 0.35 );
-				const outputPass = new OutputPass();
+				postProcessing = new THREE.PostProcessing( renderer );
 
-				composer = new EffectComposer( renderer );
+				const scenePass = pass( scene, camera );
+				const scenePassColor = scenePass.getTextureNode();
 
-				composer.addPass( renderModel );
-				composer.addPass( effectFilm );
-				composer.addPass( outputPass );
+				postProcessing.outputNode = scenePassColor.film();
 
 			}
 
@@ -243,7 +234,6 @@
 				camera.updateProjectionMatrix();
 
 				renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
-				composer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
 
 			}
 
@@ -283,7 +273,7 @@
 				controls.movementSpeed = 0.33 * d;
 				controls.update( delta );
 
-				composer.render( delta );
+				postProcessing.render();
 
 			}
 

BIN
examples/screenshots/misc_controls_fly.jpg


+ 1 - 0
src/nodes/Nodes.js

@@ -130,6 +130,7 @@ export { default as SobelOperatorNode, sobel } from './display/SobelOperatorNode
 export { default as DepthOfFieldNode, dof } from './display/DepthOfFieldNode.js';
 export { default as DotScreenNode, dotScreen } from './display/DotScreenNode.js';
 export { default as RGBShiftNode, rgbShift } from './display/RGBShiftNode.js';
+export { default as FilmNode, film } from './display/FilmNode.js';
 
 export { default as PassNode, pass, texturePass, depthPass } from './display/PassNode.js';
 

+ 52 - 0
src/nodes/display/FilmNode.js

@@ -0,0 +1,52 @@
+import TempNode from '../core/TempNode.js';
+import { uv } from '../accessors/UVNode.js';
+import { addNodeElement, tslFn, nodeProxy, vec4 } from '../shadernode/ShaderNode.js';
+import { mix, fract, clamp, rand } from '../math/MathNode.js';
+import { timerLocal } from '../utils/TimerNode.js';
+
+class FilmNode extends TempNode {
+
+	constructor( inputNode, intensityNode = null, uvNode = null ) {
+
+		super();
+
+		this.inputNode = inputNode;
+		this.intensityNode = intensityNode;
+		this.uvNode = uvNode;
+
+	}
+
+	setup() {
+
+		const uvNode = this.uvNode || uv();
+
+		const film = tslFn( () => {
+
+			const base = this.inputNode.rgb;
+			const noise = rand( fract( uvNode.add( timerLocal() ) ) );
+
+			let color = base.add( base.mul( clamp( noise.add( 0.1 ), 0, 1 ) ) );
+
+			if ( this.intensityNode !== null ) {
+
+				color = mix( base, color, this.intensityNode );
+
+			}
+
+			return vec4( color, this.inputNode.a );
+
+		} );
+
+		const outputNode = film();
+
+		return outputNode;
+
+	}
+
+}
+
+export const film = nodeProxy( FilmNode );
+
+addNodeElement( 'film', film );
+
+export default FilmNode;

+ 1 - 0
test/e2e/puppeteer.js

@@ -149,6 +149,7 @@ const exceptionList = [
 	'webgpu_performance_renderbundle',
 	'webgpu_lights_rectarealight',
 	'webgpu_postprocessing',
+	'misc_controls_fly',
 
 	// WebGPU idleTime and parseTime too low
 	'webgpu_compute_particles',