Browse Source

Fix Adaptive Tone Map issue on very dark (initial) scene

When last luminance is zero and time delta also very small,
chances are that the output luminance is still zero on the
next run, even when current luminance is rather high. The
reason behind is that luminance value is stored on the red
channel of a 1x1 texture. Therefore computed values below
~0.002 get rounded down to zero again (1/255 => ~0.004).

This could lead to a division by zero in the ToneMapShader.
Added `minLuminance` option to avoid this edge case.

There is also no reason to write to all color channels when
only the red channel is used (ToneMapShader uses red too).

Additionally implements `renderToScreen` composer flag.
Marcel Greter 8 years ago
parent
commit
2deb7637c7

+ 26 - 4
examples/js/postprocessing/AdaptiveToneMappingPass.js

@@ -54,6 +54,7 @@ THREE.AdaptiveToneMappingPass = function ( adaptive, resolution ) {
 		uniforms: {
 			"lastLum": { value: null },
 			"currentLum": { value: null },
+			"minLuminance": { value: 0.01 },
 			"delta": { value: 0.016 },
 			"tau": { value: 1.0 }
 		},
@@ -72,6 +73,7 @@ THREE.AdaptiveToneMappingPass = function ( adaptive, resolution ) {
 
 			"uniform sampler2D lastLum;",
 			"uniform sampler2D currentLum;",
+			"uniform float minLuminance;",
 			"uniform float delta;",
 			"uniform float tau;",
 
@@ -80,8 +82,8 @@ THREE.AdaptiveToneMappingPass = function ( adaptive, resolution ) {
 				"vec4 lastLum = texture2D( lastLum, vUv, MIP_LEVEL_1X1 );",
 				"vec4 currentLum = texture2D( currentLum, vUv, MIP_LEVEL_1X1 );",
 
-				"float fLastLum = lastLum.r;",
-				"float fCurrentLum = currentLum.r;",
+				"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.
@@ -90,7 +92,7 @@ THREE.AdaptiveToneMappingPass = function ( adaptive, resolution ) {
 				// Adapt the luminance using Pattanaik's technique
 				"float fAdaptedLum = fLastLum + (fCurrentLum - fLastLum) * (1.0 - exp(-delta * tau));",
 				// "fAdaptedLum = sqrt(fAdaptedLum);",
-				"gl_FragColor = vec4( vec3( fAdaptedLum ), 1.0 );",
+				"gl_FragColor.r = fAdaptedLum;",
 			"}"
 		].join( '\n' )
 	};
@@ -165,7 +167,16 @@ THREE.AdaptiveToneMappingPass.prototype = Object.assign( Object.create( THREE.Pa
 
 		this.quad.material = this.materialToneMap;
 		this.materialToneMap.uniforms.tDiffuse.value = readBuffer.texture;
-		renderer.render( this.scene, this.camera, writeBuffer, this.clear );
+
+		if ( this.renderToScreen ) {
+
+			renderer.render( this.scene, this.camera );
+
+		} else {
+
+			renderer.render( this.scene, this.camera, writeBuffer, this.clear );
+
+		}
 
 	},
 
@@ -249,6 +260,17 @@ THREE.AdaptiveToneMappingPass.prototype = Object.assign( Object.create( THREE.Pa
 
 	},
 
+	setMinLuminance: function( minLum ) {
+
+		if ( minLum ) {
+
+			this.materialToneMap.uniforms.minLuminance.value = minLum;
+			this.materialAdaptiveLum.uniforms.minLuminance.value = minLum;
+
+		}
+
+	},
+
 	setMaxLuminance: function( maxLum ) {
 
 		if ( maxLum ) {

+ 4 - 2
examples/js/shaders/ToneMapShader.js

@@ -1,7 +1,7 @@
 /**
  * @author miibond
  *
- * Full-screen tone-mapping shader based on http://www.graphics.cornell.edu/~jaf/publications/sig02_paper.pdf
+ * Full-screen tone-mapping shader based on http://www.cis.rit.edu/people/faculty/ferwerda/publications/sig02_paper.pdf
  */
 
 THREE.ToneMapShader = {
@@ -12,6 +12,7 @@ THREE.ToneMapShader = {
 		"averageLuminance":  { value: 1.0 },
 		"luminanceMap":  { value: null },
 		"maxLuminance":  { value: 16.0 },
+		"minLuminance":  { value: 0.01 },
 		"middleGrey":  { value: 0.6 }
 	},
 
@@ -35,6 +36,7 @@ THREE.ToneMapShader = {
 		"varying vec2 vUv;",
 
 		"uniform float middleGrey;",
+		"uniform float minLuminance;",
 		"uniform float maxLuminance;",
 		"#ifdef ADAPTED_LUMINANCE",
 			"uniform sampler2D luminanceMap;",
@@ -56,7 +58,7 @@ THREE.ToneMapShader = {
 			"float fLumPixel = dot(vColor, LUM_CONVERT);",
 
 			// Apply the modified operator (Eq. 4)
-			"float fLumScaled = (fLumPixel * middleGrey) / fLumAvg;",
+			"float fLumScaled = (fLumPixel * middleGrey) / max( minLuminance, fLumAvg );",
 
 			"float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (maxLuminance * maxLuminance)))) / (1.0 + fLumScaled);",
 			"return fLumCompressed * vColor;",