瀏覽代碼

JSM: Added module and TS file for AdaptiveToneMappingPass.

Mugen87 6 年之前
父節點
當前提交
45a1b50c99

+ 3 - 0
docs/manual/en/introduction/Import-via-modules.html

@@ -158,6 +158,7 @@
 				</li>
 				<li>postprocessing
 					<ul>
+						<li>AdaptiveToneMappingPass</li>
 						<li>AfterimagePass</li>
 						<li>BloomPass</li>
 						<li>BokehPass</li>
@@ -225,6 +226,8 @@
 						<li>SobelOperatorShader</li>
 						<li>SSAOShader</li>
 						<li>TechnicolorShader</li>
+						<li>ToneMapShader</li>
+						<li>TriangleBlurShader</li>
 						<li>UnpackDepthRGBAShader</li>
 						<li>VerticalBlurShader</li>
 						<li>VerticalTiltShiftShader</li>

+ 2 - 2
examples/js/postprocessing/AdaptiveToneMappingPass.js

@@ -125,7 +125,7 @@ THREE.AdaptiveToneMappingPass.prototype = Object.assign( Object.create( THREE.Pa
 
 	constructor: THREE.AdaptiveToneMappingPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime/*, maskActive*/ ) {
 
 		if ( this.needsInit ) {
 
@@ -183,7 +183,7 @@ THREE.AdaptiveToneMappingPass.prototype = Object.assign( Object.create( THREE.Pa
 
 	},
 
-	reset: function ( renderer ) {
+	reset: function () {
 
 		// render targets
 		if ( this.luminanceRT ) {

+ 5 - 5
examples/js/shaders/ToneMapShader.js

@@ -9,11 +9,11 @@ THREE.ToneMapShader = {
 	uniforms: {
 
 		"tDiffuse": { value: null },
-		"averageLuminance":  { value: 1.0 },
-		"luminanceMap":  { value: null },
-		"maxLuminance":  { value: 16.0 },
-		"minLuminance":  { value: 0.01 },
-		"middleGrey":  { value: 0.6 }
+		"averageLuminance": { value: 1.0 },
+		"luminanceMap": { value: null },
+		"maxLuminance": { value: 16.0 },
+		"minLuminance": { value: 0.01 },
+		"middleGrey": { value: 0.6 }
 	},
 
 	vertexShader: [

+ 2 - 2
examples/js/shaders/TriangleBlurShader.js

@@ -12,10 +12,10 @@
 
 THREE.TriangleBlurShader = {
 
-	uniforms : {
+	uniforms: {
 
 		"texture": { value: null },
-		"delta":   { value: new THREE.Vector2( 1, 1 ) }
+		"delta": { value: new THREE.Vector2( 1, 1 ) }
 
 	},
 

+ 32 - 0
examples/jsm/postprocessing/AdaptiveToneMappingPass.d.ts

@@ -0,0 +1,32 @@
+import {
+  WebGLRenderTarget,
+  ShaderMaterial
+} from '../../../src/Three';
+
+import { Pass } from './Pass';
+
+export class AdaptiveToneMappingPass extends Pass {
+  constructor(adaptive?: boolean, resolution?: number);
+  adaptive: boolean;
+  resolution: number;
+  needsInit: number;
+  luminanceRT: WebGLRenderTarget;
+  previousLuminanceRT: WebGLRenderTarget;
+  currentLuminanceRT: WebGLRenderTarget;
+  copyUniforms: object;
+  materialCopy: ShaderMaterial;
+  materialLuminance: ShaderMaterial;
+  adaptLuminanceShader: object;
+  materialAdaptiveLum: ShaderMaterial;
+  materialToneMap: ShaderMaterial;
+  fsQuad: object;
+
+  reset(): void;
+  setAdaptive(adaptive: boolean): void;
+  setAdaptionRate(rate: number): void;
+  setMinLuminance(minLum: number): void;
+  setMaxLuminance(maxLum: number): void;
+  setAverageLuminance(avgLum: number): void;
+  setMiddleGrey(middleGrey: number): void;
+  dispose(): void;
+}

+ 365 - 0
examples/jsm/postprocessing/AdaptiveToneMappingPass.js

@@ -0,0 +1,365 @@
+/**
+ * @author miibond
+ * Generate a texture that represents the luminosity of the current scene, adapted over time
+ * to simulate the optic nerve responding to the amount of light it is receiving.
+ * Based on a GDC2007 presentation by Wolfgang Engel titled "Post-Processing Pipeline"
+ *
+ * Full-screen tone-mapping shader based on http://www.graphics.cornell.edu/~jaf/publications/sig02_paper.pdf
+ */
+
+import {
+	LinearFilter,
+	LinearMipMapLinearFilter,
+	MeshBasicMaterial,
+	NoBlending,
+	RGBAFormat,
+	ShaderMaterial,
+	UniformsUtils,
+	WebGLRenderTarget
+} from "../../../build/three.module.js";
+import { Pass } from "../postprocessing/Pass.js";
+import { CopyShader } from "../shaders/CopyShader.js";
+import { LuminosityShader } from "../shaders/LuminosityShader.js";
+import { ToneMapShader } from "../shaders/ToneMapShader.js";
+
+var AdaptiveToneMappingPass = function ( adaptive, resolution ) {
+
+	Pass.call( this );
+
+	this.resolution = ( resolution !== undefined ) ? resolution : 256;
+	this.needsInit = true;
+	this.adaptive = adaptive !== undefined ? !! adaptive : true;
+
+	this.luminanceRT = null;
+	this.previousLuminanceRT = null;
+	this.currentLuminanceRT = null;
+
+	if ( CopyShader === undefined )
+		console.error( "AdaptiveToneMappingPass relies on CopyShader" );
+
+	var copyShader = CopyShader;
+
+	this.copyUniforms = UniformsUtils.clone( copyShader.uniforms );
+
+	this.materialCopy = new ShaderMaterial( {
+
+		uniforms: this.copyUniforms,
+		vertexShader: copyShader.vertexShader,
+		fragmentShader: copyShader.fragmentShader,
+		blending: NoBlending,
+		depthTest: false
+
+	} );
+
+	if ( LuminosityShader === undefined )
+		console.error( "AdaptiveToneMappingPass relies on LuminosityShader" );
+
+	this.materialLuminance = new ShaderMaterial( {
+
+		uniforms: UniformsUtils.clone( LuminosityShader.uniforms ),
+		vertexShader: LuminosityShader.vertexShader,
+		fragmentShader: LuminosityShader.fragmentShader,
+		blending: NoBlending
+	} );
+
+	this.adaptLuminanceShader = {
+		defines: {
+			"MIP_LEVEL_1X1": ( Math.log( this.resolution ) / Math.log( 2.0 ) ).toFixed( 1 )
+		},
+		uniforms: {
+			"lastLum": { value: null },
+			"currentLum": { value: null },
+			"minLuminance": { value: 0.01 },
+			"delta": { value: 0.016 },
+			"tau": { value: 1.0 }
+		},
+		vertexShader: [
+			"varying vec2 vUv;",
+
+			"void main() {",
+
+			"	vUv = uv;",
+			"	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+			"}"
+		].join( '\n' ),
+		fragmentShader: [
+			"varying vec2 vUv;",
+
+			"uniform sampler2D lastLum;",
+			"uniform sampler2D currentLum;",
+			"uniform float minLuminance;",
+			"uniform float delta;",
+			"uniform float tau;",
+
+			"void main() {",
+
+			"	vec4 lastLum = texture2D( lastLum, vUv, MIP_LEVEL_1X1 );",
+			"	vec4 currentLum = texture2D( currentLum, vUv, MIP_LEVEL_1X1 );",
+
+			"	float fLastLum = max( minLuminance, lastLum.r );",
+			"	float fCurrentLum = max( minLuminance, currentLum.r );",
+
+			//The adaption seems to work better in extreme lighting differences
+			//if the input luminance is squared.
+			"	fCurrentLum *= fCurrentLum;",
+
+			// Adapt the luminance using Pattanaik's technique
+			"	float fAdaptedLum = fLastLum + (fCurrentLum - fLastLum) * (1.0 - exp(-delta * tau));",
+			// "fAdaptedLum = sqrt(fAdaptedLum);",
+			"	gl_FragColor.r = fAdaptedLum;",
+			"}"
+		].join( '\n' )
+	};
+
+	this.materialAdaptiveLum = new ShaderMaterial( {
+
+		uniforms: UniformsUtils.clone( this.adaptLuminanceShader.uniforms ),
+		vertexShader: this.adaptLuminanceShader.vertexShader,
+		fragmentShader: this.adaptLuminanceShader.fragmentShader,
+		defines: Object.assign( {}, this.adaptLuminanceShader.defines ),
+		blending: NoBlending
+	} );
+
+	if ( ToneMapShader === undefined )
+		console.error( "AdaptiveToneMappingPass relies on ToneMapShader" );
+
+	this.materialToneMap = new ShaderMaterial( {
+
+		uniforms: UniformsUtils.clone( ToneMapShader.uniforms ),
+		vertexShader: ToneMapShader.vertexShader,
+		fragmentShader: ToneMapShader.fragmentShader,
+		blending: NoBlending
+	} );
+
+	this.fsQuad = new Pass.FullScreenQuad( null );
+
+};
+
+AdaptiveToneMappingPass.prototype = Object.assign( Object.create( Pass.prototype ), {
+
+	constructor: AdaptiveToneMappingPass,
+
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime/*, maskActive*/ ) {
+
+		if ( this.needsInit ) {
+
+			this.reset( renderer );
+
+			this.luminanceRT.texture.type = readBuffer.texture.type;
+			this.previousLuminanceRT.texture.type = readBuffer.texture.type;
+			this.currentLuminanceRT.texture.type = readBuffer.texture.type;
+			this.needsInit = false;
+
+		}
+
+		if ( this.adaptive ) {
+
+			//Render the luminance of the current scene into a render target with mipmapping enabled
+			this.fsQuad.material = this.materialLuminance;
+			this.materialLuminance.uniforms.tDiffuse.value = readBuffer.texture;
+			renderer.setRenderTarget( this.currentLuminanceRT );
+			this.fsQuad.render( renderer );
+
+			//Use the new luminance values, the previous luminance and the frame delta to
+			//adapt the luminance over time.
+			this.fsQuad.material = this.materialAdaptiveLum;
+			this.materialAdaptiveLum.uniforms.delta.value = deltaTime;
+			this.materialAdaptiveLum.uniforms.lastLum.value = this.previousLuminanceRT.texture;
+			this.materialAdaptiveLum.uniforms.currentLum.value = this.currentLuminanceRT.texture;
+			renderer.setRenderTarget( this.luminanceRT );
+			this.fsQuad.render( renderer );
+
+			//Copy the new adapted luminance value so that it can be used by the next frame.
+			this.fsQuad.material = this.materialCopy;
+			this.copyUniforms.tDiffuse.value = this.luminanceRT.texture;
+			renderer.setRenderTarget( this.previousLuminanceRT );
+			this.fsQuad.render( renderer );
+
+		}
+
+		this.fsQuad.material = this.materialToneMap;
+		this.materialToneMap.uniforms.tDiffuse.value = readBuffer.texture;
+
+		if ( this.renderToScreen ) {
+
+			renderer.setRenderTarget( null );
+			this.fsQuad.render( renderer );
+
+		} else {
+
+			renderer.setRenderTarget( writeBuffer );
+
+			if ( this.clear ) renderer.clear();
+
+			this.fsQuad.render( renderer );
+
+		}
+
+	},
+
+	reset: function () {
+
+		// render targets
+		if ( this.luminanceRT ) {
+
+			this.luminanceRT.dispose();
+
+		}
+		if ( this.currentLuminanceRT ) {
+
+			this.currentLuminanceRT.dispose();
+
+		}
+		if ( this.previousLuminanceRT ) {
+
+			this.previousLuminanceRT.dispose();
+
+		}
+
+		var pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; // was RGB format. changed to RGBA format. see discussion in #8415 / #8450
+
+		this.luminanceRT = new WebGLRenderTarget( this.resolution, this.resolution, pars );
+		this.luminanceRT.texture.name = "AdaptiveToneMappingPass.l";
+		this.luminanceRT.texture.generateMipmaps = false;
+
+		this.previousLuminanceRT = new WebGLRenderTarget( this.resolution, this.resolution, pars );
+		this.previousLuminanceRT.texture.name = "AdaptiveToneMappingPass.pl";
+		this.previousLuminanceRT.texture.generateMipmaps = false;
+
+		// We only need mipmapping for the current luminosity because we want a down-sampled version to sample in our adaptive shader
+		pars.minFilter = LinearMipMapLinearFilter;
+		pars.generateMipmaps = true;
+		this.currentLuminanceRT = new WebGLRenderTarget( this.resolution, this.resolution, pars );
+		this.currentLuminanceRT.texture.name = "AdaptiveToneMappingPass.cl";
+
+		if ( this.adaptive ) {
+
+			this.materialToneMap.defines[ "ADAPTED_LUMINANCE" ] = "";
+			this.materialToneMap.uniforms.luminanceMap.value = this.luminanceRT.texture;
+
+		}
+		//Put something in the adaptive luminance texture so that the scene can render initially
+		this.fsQuad.material = new MeshBasicMaterial( { color: 0x777777 } );
+		this.materialLuminance.needsUpdate = true;
+		this.materialAdaptiveLum.needsUpdate = true;
+		this.materialToneMap.needsUpdate = true;
+		// renderer.render( this.scene, this.camera, this.luminanceRT );
+		// renderer.render( this.scene, this.camera, this.previousLuminanceRT );
+		// renderer.render( this.scene, this.camera, this.currentLuminanceRT );
+
+	},
+
+	setAdaptive: function ( adaptive ) {
+
+		if ( adaptive ) {
+
+			this.adaptive = true;
+			this.materialToneMap.defines[ "ADAPTED_LUMINANCE" ] = "";
+			this.materialToneMap.uniforms.luminanceMap.value = this.luminanceRT.texture;
+
+		} else {
+
+			this.adaptive = false;
+			delete this.materialToneMap.defines[ "ADAPTED_LUMINANCE" ];
+			this.materialToneMap.uniforms.luminanceMap.value = null;
+
+		}
+		this.materialToneMap.needsUpdate = true;
+
+	},
+
+	setAdaptionRate: function ( rate ) {
+
+		if ( rate ) {
+
+			this.materialAdaptiveLum.uniforms.tau.value = Math.abs( rate );
+
+		}
+
+	},
+
+	setMinLuminance: function ( minLum ) {
+
+		if ( minLum ) {
+
+			this.materialToneMap.uniforms.minLuminance.value = minLum;
+			this.materialAdaptiveLum.uniforms.minLuminance.value = minLum;
+
+		}
+
+	},
+
+	setMaxLuminance: function ( maxLum ) {
+
+		if ( maxLum ) {
+
+			this.materialToneMap.uniforms.maxLuminance.value = maxLum;
+
+		}
+
+	},
+
+	setAverageLuminance: function ( avgLum ) {
+
+		if ( avgLum ) {
+
+			this.materialToneMap.uniforms.averageLuminance.value = avgLum;
+
+		}
+
+	},
+
+	setMiddleGrey: function ( middleGrey ) {
+
+		if ( middleGrey ) {
+
+			this.materialToneMap.uniforms.middleGrey.value = middleGrey;
+
+		}
+
+	},
+
+	dispose: function () {
+
+		if ( this.luminanceRT ) {
+
+			this.luminanceRT.dispose();
+
+		}
+		if ( this.previousLuminanceRT ) {
+
+			this.previousLuminanceRT.dispose();
+
+		}
+		if ( this.currentLuminanceRT ) {
+
+			this.currentLuminanceRT.dispose();
+
+		}
+		if ( this.materialLuminance ) {
+
+			this.materialLuminance.dispose();
+
+		}
+		if ( this.materialAdaptiveLum ) {
+
+			this.materialAdaptiveLum.dispose();
+
+		}
+		if ( this.materialCopy ) {
+
+			this.materialCopy.dispose();
+
+		}
+		if ( this.materialToneMap ) {
+
+			this.materialToneMap.dispose();
+
+		}
+
+	}
+
+} );
+
+export { AdaptiveToneMappingPass };

+ 16 - 0
examples/jsm/shaders/ToneMapShader.d.ts

@@ -0,0 +1,16 @@
+import {
+  Uniform
+} from '../../../src/Three';
+
+export interface ToneMapShader {
+  uniforms: {
+    tDiffuse: Uniform;
+    averageLuminance: Uniform;
+    luminanceMap: Uniform;
+    maxLuminance: Uniform;
+    minLuminance: Uniform;
+    middleGrey: Uniform;
+  };
+  vertexShader: string;
+  fragmentShader: string;
+}

+ 81 - 0
examples/jsm/shaders/ToneMapShader.js

@@ -0,0 +1,81 @@
+/**
+ * @author miibond
+ *
+ * Full-screen tone-mapping shader based on http://www.cis.rit.edu/people/faculty/ferwerda/publications/sig02_paper.pdf
+ */
+
+
+
+var ToneMapShader = {
+
+	uniforms: {
+
+		"tDiffuse": { value: null },
+		"averageLuminance": { value: 1.0 },
+		"luminanceMap": { value: null },
+		"maxLuminance": { value: 16.0 },
+		"minLuminance": { value: 0.01 },
+		"middleGrey": { value: 0.6 }
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vUv = uv;",
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"#include <common>",
+
+		"uniform sampler2D tDiffuse;",
+
+		"varying vec2 vUv;",
+
+		"uniform float middleGrey;",
+		"uniform float minLuminance;",
+		"uniform float maxLuminance;",
+		"#ifdef ADAPTED_LUMINANCE",
+			"uniform sampler2D luminanceMap;",
+		"#else",
+			"uniform float averageLuminance;",
+		"#endif",
+
+		"vec3 ToneMap( vec3 vColor ) {",
+			"#ifdef ADAPTED_LUMINANCE",
+				// Get the calculated average luminance
+				"float fLumAvg = texture2D(luminanceMap, vec2(0.5, 0.5)).r;",
+			"#else",
+				"float fLumAvg = averageLuminance;",
+			"#endif",
+
+			// Calculate the luminance of the current pixel
+			"float fLumPixel = linearToRelativeLuminance( vColor );",
+
+			// Apply the modified operator (Eq. 4)
+			"float fLumScaled = (fLumPixel * middleGrey) / max( minLuminance, fLumAvg );",
+
+			"float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (maxLuminance * maxLuminance)))) / (1.0 + fLumScaled);",
+			"return fLumCompressed * vColor;",
+		"}",
+
+		"void main() {",
+
+			"vec4 texel = texture2D( tDiffuse, vUv );",
+
+			"gl_FragColor = vec4( ToneMap( texel.xyz ), texel.w );",
+
+		"}"
+
+	].join( "\n" )
+
+};
+
+export { ToneMapShader };

+ 12 - 0
examples/jsm/shaders/TriangleBlurShader.d.ts

@@ -0,0 +1,12 @@
+import {
+  Uniform
+} from '../../../src/Three';
+
+export interface TriangleBlurShader {
+  uniforms: {
+    texture: Uniform;
+    delta: Uniform;
+  };
+  vertexShader: string;
+  fragmentShader: string;
+}

+ 78 - 0
examples/jsm/shaders/TriangleBlurShader.js

@@ -0,0 +1,78 @@
+/**
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ *
+ * Triangle blur shader
+ * based on glfx.js triangle blur shader
+ * https://github.com/evanw/glfx.js
+ *
+ * A basic blur filter, which convolves the image with a
+ * pyramid filter. The pyramid filter is separable and is applied as two
+ * perpendicular triangle filters.
+ */
+
+import {
+	Vector2
+} from "../../../build/three.module.js";
+
+var TriangleBlurShader = {
+
+	uniforms: {
+
+		"texture": { value: null },
+		"delta": { value: new Vector2( 1, 1 ) }
+
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vUv = uv;",
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"#include <common>",
+
+		"#define ITERATIONS 10.0",
+
+		"uniform sampler2D texture;",
+		"uniform vec2 delta;",
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vec4 color = vec4( 0.0 );",
+
+			"float total = 0.0;",
+
+			// randomize the lookup values to hide the fixed number of samples
+
+			"float offset = rand( vUv );",
+
+			"for ( float t = -ITERATIONS; t <= ITERATIONS; t ++ ) {",
+
+				"float percent = ( t + offset - 0.5 ) / ITERATIONS;",
+				"float weight = 1.0 - abs( percent );",
+
+				"color += texture2D( texture, vUv + delta * percent ) * weight;",
+				"total += weight;",
+
+			"}",
+
+			"gl_FragColor = color / total;",
+
+		"}"
+
+	].join( "\n" )
+
+};
+
+export { TriangleBlurShader };

+ 3 - 0
utils/modularize.js

@@ -67,6 +67,7 @@ var files = [
 	{ path: 'pmrem/PMREMCubeUVPacker.js', dependencies: [], ignoreList: [] },
 	{ path: 'pmrem/PMREMGenerator.js', dependencies: [], ignoreList: [] },
 
+	{ path: 'postprocessing/AdaptiveToneMappingPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' }, { name: 'LuminosityShader', path: 'shaders/LuminosityShader.js' }, { name: 'ToneMapShader', path: 'shaders/ToneMapShader.js' } ], ignoreList: [] },
 	{ path: 'postprocessing/AfterimagePass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'AfterimageShader', path: 'shaders/AfterimageShader.js' } ], ignoreList: [] },
 	{ path: 'postprocessing/BloomPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' }, { name: 'ConvolutionShader', path: 'shaders/ConvolutionShader.js' } ], ignoreList: [] },
 	{ path: 'postprocessing/BokehPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'BokehShader', path: 'shaders/BokehShader.js' } ], ignoreList: [] },
@@ -128,6 +129,8 @@ var files = [
 	{ path: 'shaders/SobelOperatorShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/SSAOShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/TechnicolorShader.js', dependencies: [], ignoreList: [] },
+	{ path: 'shaders/ToneMapShader.js', dependencies: [], ignoreList: [] },
+	{ path: 'shaders/TriangleBlurShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/UnpackDepthRGBAShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/VerticalBlurShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/VerticalTiltShiftShader.js', dependencies: [], ignoreList: [] },