Browse Source

Examples: Introduce `OutputPass`. (#26102)

* Examples: Introduce `OutputPass`.

* Examples: Simplify selective bloom demo.

* Examples: Improve GUI in bloom demos.
Michael Herzog 2 years ago
parent
commit
6c7ff7addf

+ 72 - 0
examples/jsm/postprocessing/OutputPass.js

@@ -0,0 +1,72 @@
+import {
+	ShaderMaterial,
+	UniformsUtils,
+	NoToneMapping,
+	LinearToneMapping,
+	ReinhardToneMapping,
+	CineonToneMapping,
+	ACESFilmicToneMapping
+} from 'three';
+import { Pass, FullScreenQuad } from './Pass.js';
+import { OutputShader } from '../shaders/OutputShader.js';
+
+class OutputPass extends Pass {
+
+	constructor( toneMapping = NoToneMapping, toneMappingExposure = 1 ) {
+
+		super();
+
+		this.toneMapping = toneMapping;
+		this.toneMappingExposure = toneMappingExposure;
+
+		//
+
+		const shader = OutputShader;
+
+		this.uniforms = UniformsUtils.clone( shader.uniforms );
+
+		this.material = new ShaderMaterial( {
+			uniforms: this.uniforms,
+			vertexShader: shader.vertexShader,
+			fragmentShader: shader.fragmentShader
+		} );
+
+		if ( toneMapping === LinearToneMapping ) this.material.defines.LINEAR_TONE_MAPPING = '';
+		else if ( toneMapping === ReinhardToneMapping ) this.material.defines.REINHARD_TONE_MAPPING = '';
+		else if ( toneMapping === CineonToneMapping ) this.material.defines.CINEON_TONE_MAPPING = '';
+		else if ( toneMapping === ACESFilmicToneMapping ) this.material.defines.ACES_FILMIC_TONE_MAPPING = '';
+
+		this.fsQuad = new FullScreenQuad( this.material );
+
+	}
+
+	render( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive */ ) {
+
+		this.uniforms[ 'tDiffuse' ].value = readBuffer.texture;
+		this.uniforms[ 'toneMappingExposure' ].value = this.toneMappingExposure;
+
+		if ( this.renderToScreen === true ) {
+
+			renderer.setRenderTarget( null );
+			this.fsQuad.render( renderer );
+
+		} else {
+
+			renderer.setRenderTarget( writeBuffer );
+			if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );
+			this.fsQuad.render( renderer );
+
+		}
+
+	}
+
+	dispose() {
+
+		this.material.dispose();
+		this.fsQuad.dispose();
+
+	}
+
+}
+
+export { OutputPass };

+ 61 - 0
examples/jsm/shaders/OutputShader.js

@@ -0,0 +1,61 @@
+const OutputShader = {
+
+	uniforms: {
+
+		'tDiffuse': { value: null },
+		'toneMappingExposure': { value: 1 }
+
+	},
+
+	vertexShader: /* glsl */`
+
+		varying vec2 vUv;
+
+		void main() {
+
+			vUv = uv;
+			gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
+
+		}`,
+
+	fragmentShader: /* glsl */`
+
+		uniform sampler2D tDiffuse;
+
+		#include <tonemapping_pars_fragment>
+
+		varying vec2 vUv;
+
+		void main() {
+
+			gl_FragColor = texture2D( tDiffuse, vUv );
+
+			// tone mapping
+
+			#ifdef LINEAR_TONE_MAPPING
+
+				gl_FragColor.rgb = LinearToneMapping( gl_FragColor.rgb );
+
+			#elif defined( REINHARD_TONE_MAPPING )
+
+				gl_FragColor.rgb = ReinhardToneMapping( gl_FragColor.rgb );
+
+			#elif defined( CINEON_TONE_MAPPING )
+
+				gl_FragColor.rgb = OptimizedCineonToneMapping( gl_FragColor.rgb );
+
+			#elif defined( ACES_FILMIC_TONE_MAPPING )
+
+				gl_FragColor.rgb = ACESFilmicToneMapping( gl_FragColor.rgb );
+
+			#endif
+
+			// color space
+
+			gl_FragColor = LinearTosRGB( gl_FragColor );
+
+		}`
+
+};
+
+export { OutputShader };

BIN
examples/screenshots/webgl_postprocessing_unreal_bloom.jpg


BIN
examples/screenshots/webgl_postprocessing_unreal_bloom_selective.jpg


+ 21 - 19
examples/webgl_postprocessing_unreal_bloom.html

@@ -49,17 +49,16 @@
 			import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
 			import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
 			import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
-			import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
-			import { GammaCorrectionShader } from 'three/addons/shaders/GammaCorrectionShader.js';
+			import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
 
 			let camera, stats;
 			let composer, renderer, mixer, clock;
 
 			const params = {
-				exposure: 1,
-				bloomStrength: 1.5,
-				bloomThreshold: 0,
-				bloomRadius: 0
+				threshold: 0,
+				strength: 1.5,
+				radius: 0,
+				exposure: 1
 			};
 
 			init();
@@ -76,7 +75,6 @@
 				renderer = new THREE.WebGLRenderer( { antialias: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
-				renderer.toneMapping = THREE.ReinhardToneMapping;
 				container.appendChild( renderer.domElement );
 
 				const scene = new THREE.Scene();
@@ -98,11 +96,11 @@
 				const renderScene = new RenderPass( scene, camera );
 
 				const bloomPass = new UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 1.5, 0.4, 0.85 );
-				bloomPass.threshold = params.bloomThreshold;
-				bloomPass.strength = params.bloomStrength;
-				bloomPass.radius = params.bloomRadius;
+				bloomPass.threshold = params.threshold;
+				bloomPass.strength = params.strength;
+				bloomPass.radius = params.radius;
 
-				const outputPass = new ShaderPass( GammaCorrectionShader );
+				const outputPass = new OutputPass( THREE.ReinhardToneMapping );
 
 				composer = new EffectComposer( renderer );
 				composer.addPass( renderScene );
@@ -125,30 +123,34 @@
 
 				const gui = new GUI();
 
-				gui.add( params, 'exposure', 0.1, 2 ).onChange( function ( value ) {
+				const bloomFolder = gui.addFolder( 'bloom' );
 
-					renderer.toneMappingExposure = Math.pow( value, 4.0 );
-
-				} );
-
-				gui.add( params, 'bloomThreshold', 0.0, 1.0 ).onChange( function ( value ) {
+				bloomFolder.add( params, 'threshold', 0.0, 1.0 ).onChange( function ( value ) {
 
 					bloomPass.threshold = Number( value );
 
 				} );
 
-				gui.add( params, 'bloomStrength', 0.0, 3.0 ).onChange( function ( value ) {
+				bloomFolder.add( params, 'strength', 0.0, 3.0 ).onChange( function ( value ) {
 
 					bloomPass.strength = Number( value );
 
 				} );
 
-				gui.add( params, 'bloomRadius', 0.0, 1.0 ).step( 0.01 ).onChange( function ( value ) {
+				gui.add( params, 'radius', 0.0, 1.0 ).step( 0.01 ).onChange( function ( value ) {
 
 					bloomPass.radius = Number( value );
 
 				} );
 
+				const toneMappingFolder = gui.addFolder( 'tone mapping' );
+
+				toneMappingFolder.add( params, 'exposure', 0.1, 2 ).onChange( function ( value ) {
+
+					outputPass.toneMappingExposure = Math.pow( value, 4.0 );
+
+				} );
+
 				window.addEventListener( 'resize', onWindowResize );
 
 			}

+ 25 - 75
examples/webgl_postprocessing_unreal_bloom_selective.html

@@ -65,19 +65,18 @@
 			import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
 			import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
 			import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
-			import { GammaCorrectionShader } from 'three/addons/shaders/GammaCorrectionShader.js';
+			import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
 
-			const ENTIRE_SCENE = 0, BLOOM_SCENE = 1;
+			const BLOOM_SCENE = 1;
 
 			const bloomLayer = new THREE.Layers();
 			bloomLayer.set( BLOOM_SCENE );
 
 			const params = {
-				exposure: 1,
-				bloomStrength: 5,
-				bloomThreshold: 0,
-				bloomRadius: 0,
-				scene: 'Scene with Glow'
+				threshold: 0,
+				strength: 3,
+				radius: 0.5,
+				exposure: 1
 			};
 
 			const darkMaterial = new THREE.MeshBasicMaterial( { color: 'black' } );
@@ -86,7 +85,6 @@
 			const renderer = new THREE.WebGLRenderer( { antialias: true } );
 			renderer.setPixelRatio( window.devicePixelRatio );
 			renderer.setSize( window.innerWidth, window.innerHeight );
-			renderer.toneMapping = THREE.ReinhardToneMapping;
 			document.body.appendChild( renderer.domElement );
 
 			const scene = new THREE.Scene();
@@ -106,9 +104,9 @@
 			const renderScene = new RenderPass( scene, camera );
 
 			const bloomPass = new UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 1.5, 0.4, 0.85 );
-			bloomPass.threshold = params.bloomThreshold;
-			bloomPass.strength = params.bloomStrength;
-			bloomPass.radius = params.bloomRadius;
+			bloomPass.threshold = params.threshold;
+			bloomPass.strength = params.strength;
+			bloomPass.radius = params.radius;
 
 			const bloomComposer = new EffectComposer( renderer );
 			bloomComposer.renderToScreen = false;
@@ -128,7 +126,7 @@
 			);
 			mixPass.needsSwap = true;
 
-			const outputPass = new ShaderPass( GammaCorrectionShader );
+			const outputPass = new OutputPass( THREE.ReinhardToneMapping );
 
 			const finalComposer = new EffectComposer( renderer );
 			finalComposer.addPass( renderScene );
@@ -143,52 +141,34 @@
 
 			const gui = new GUI();
 
-			gui.add( params, 'scene', [ 'Scene with Glow', 'Glow only', 'Scene only' ] ).onChange( function ( value ) {
+			const bloomFolder = gui.addFolder( 'bloom' );
 
-				switch ( value ) 	{
-
-					case 'Scene with Glow':
-						bloomComposer.renderToScreen = false;
-						break;
-					case 'Glow only':
-						bloomComposer.renderToScreen = true;
-						break;
-					case 'Scene only':
-						// nothing to do
-						break;
-
-				}
+			bloomFolder.add( params, 'threshold', 0.0, 1.0 ).onChange( function ( value ) {
 
+				bloomPass.threshold = Number( value );
 				render();
 
 			} );
 
-			const folder = gui.addFolder( 'Bloom Parameters' );
+			bloomFolder.add( params, 'strength', 0.0, 10.0 ).onChange( function ( value ) {
 
-			folder.add( params, 'exposure', 0.1, 2 ).onChange( function ( value ) {
-
-				renderer.toneMappingExposure = Math.pow( value, 4.0 );
+				bloomPass.strength = Number( value );
 				render();
 
 			} );
 
-			folder.add( params, 'bloomThreshold', 0.0, 1.0 ).onChange( function ( value ) {
+			bloomFolder.add( params, 'radius', 0.0, 1.0 ).step( 0.01 ).onChange( function ( value ) {
 
-				bloomPass.threshold = Number( value );
+				bloomPass.radius = Number( value );
 				render();
 
 			} );
 
-			folder.add( params, 'bloomStrength', 0.0, 10.0 ).onChange( function ( value ) {
-
-				bloomPass.strength = Number( value );
-				render();
-
-			} );
+			const toneMappingFolder = gui.addFolder( 'tone mapping' );
 
-			folder.add( params, 'bloomRadius', 0.0, 1.0 ).step( 0.01 ).onChange( function ( value ) {
+			toneMappingFolder.add( params, 'exposure', 0.1, 2 ).onChange( function ( value ) {
 
-				bloomPass.radius = Number( value );
+				outputPass.toneMappingExposure = Math.pow( value, 4.0 );
 				render();
 
 			} );
@@ -270,42 +250,12 @@
 
 			function render() {
 
-				switch ( params.scene ) {
-
-					case 'Scene only':
-						renderer.render( scene, camera );
-						break;
-					case 'Glow only':
-						renderBloom( false );
-						break;
-					case 'Scene with Glow':
-					default:
-						// render scene with bloom
-						renderBloom( true );
-
-						// render the entire scene, then render bloom scene on top
-						finalComposer.render();
-						break;
-
-				}
-
-			}
+				scene.traverse( darkenNonBloomed );
+				bloomComposer.render();
+				scene.traverse( restoreMaterial );
 
-			function renderBloom( mask ) {
-
-				if ( mask === true ) {
-
-					scene.traverse( darkenNonBloomed );
-					bloomComposer.render();
-					scene.traverse( restoreMaterial );
-
-				} else {
-
-					camera.layers.set( BLOOM_SCENE );
-					bloomComposer.render();
-					camera.layers.set( ENTIRE_SCENE );
-
-				}
+				// render the entire scene, then render bloom scene on top
+				finalComposer.render();
 
 			}