Browse Source

WebGLRenderer: Add Support for Khronos Neutral Tone Mapping (#27668)

* added Commerce tone mapping

* fix test

* Update OutputPass.js

Add support for `CommereceToneMapping`.

* Update OutputShader.js

Add support for `CommerceToneMapping`.

* Update tonemapping_pars_fragment.glsl.js

Add reference for `CommerceToneMapping`.

* rename

* fix typo

---------

Co-authored-by: Michael Herzog <[email protected]>
Emmett Lalish 1 year ago
parent
commit
996903f37a

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

@@ -7,6 +7,7 @@ import {
 	CineonToneMapping,
 	AgXToneMapping,
 	ACESFilmicToneMapping,
+	KhronosNeutralToneMapping,
 	SRGBTransfer
 } from 'three';
 import { Pass, FullScreenQuad } from './Pass.js';
@@ -61,6 +62,7 @@ class OutputPass extends Pass {
 			else if ( this._toneMapping === CineonToneMapping ) this.material.defines.CINEON_TONE_MAPPING = '';
 			else if ( this._toneMapping === ACESFilmicToneMapping ) this.material.defines.ACES_FILMIC_TONE_MAPPING = '';
 			else if ( this._toneMapping === AgXToneMapping ) this.material.defines.AGX_TONE_MAPPING = '';
+			else if ( this._toneMapping === KhronosNeutralToneMapping ) this.material.defines.KHRONOS_NEUTRAL_TONE_MAPPING = '';
 
 			this.material.needsUpdate = true;
 

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

@@ -64,6 +64,10 @@ const OutputShader = {
 
 				gl_FragColor.rgb = AgXToneMapping( gl_FragColor.rgb );
 
+			#elif defined( KHRONOS_NEUTRAL_TONE_MAPPING )
+
+				gl_FragColor.rgb = KhronosNeutralToneMapping( gl_FragColor.rgb );
+
 			#endif
 
 			// color space

+ 1 - 0
src/constants.js

@@ -57,6 +57,7 @@ export const CineonToneMapping = 3;
 export const ACESFilmicToneMapping = 4;
 export const CustomToneMapping = 5;
 export const AgXToneMapping = 6;
+export const KhronosNeutralToneMapping = 7;
 export const AttachedBindMode = 'attached';
 export const DetachedBindMode = 'detached';
 

+ 23 - 0
src/renderers/shaders/ShaderChunk/tonemapping_pars_fragment.glsl.js

@@ -165,5 +165,28 @@ vec3 AgXToneMapping( vec3 color ) {
 
 }
 
+// https://modelviewer.dev/examples/tone-mapping
+
+vec3 KhronosNeutralToneMapping( vec3 color ) {
+	float startCompression = 0.8 - 0.04;
+	float desaturation = 0.15;
+
+	color *= toneMappingExposure;
+
+	float x = min(color.r, min(color.g, color.b));
+	float offset = x < 0.08 ? x - 6.25 * x * x : 0.04;
+	color -= offset;
+
+	float peak = max(color.r, max(color.g, color.b));
+	if (peak < startCompression) return color;
+
+	float d = 1. - startCompression;
+	float newPeak = 1. - d * d / (peak + d - startCompression);
+	color *= newPeak / peak;
+
+	float g = 1. - 1. / (desaturation * (peak - newPeak) + 1.);
+	return mix(color, vec3(1, 1, 1), g);
+}
+
 vec3 CustomToneMapping( vec3 color ) { return color; }
 `;

+ 5 - 1
src/renderers/webgl/WebGLProgram.js

@@ -1,7 +1,7 @@
 import { WebGLUniforms } from './WebGLUniforms.js';
 import { WebGLShader } from './WebGLShader.js';
 import { ShaderChunk } from '../shaders/ShaderChunk.js';
-import { NoToneMapping, AddOperation, MixOperation, MultiplyOperation, CubeRefractionMapping, CubeUVReflectionMapping, CubeReflectionMapping, PCFSoftShadowMap, PCFShadowMap, VSMShadowMap, AgXToneMapping, ACESFilmicToneMapping, CineonToneMapping, CustomToneMapping, ReinhardToneMapping, LinearToneMapping, GLSL3, LinearSRGBColorSpace, SRGBColorSpace, LinearDisplayP3ColorSpace, DisplayP3ColorSpace, P3Primaries, Rec709Primaries } from '../../constants.js';
+import { NoToneMapping, AddOperation, MixOperation, MultiplyOperation, CubeRefractionMapping, CubeUVReflectionMapping, CubeReflectionMapping, PCFSoftShadowMap, PCFShadowMap, VSMShadowMap, AgXToneMapping, ACESFilmicToneMapping, KhronosNeutralToneMapping, CineonToneMapping, CustomToneMapping, ReinhardToneMapping, LinearToneMapping, GLSL3, LinearSRGBColorSpace, SRGBColorSpace, LinearDisplayP3ColorSpace, DisplayP3ColorSpace, P3Primaries, Rec709Primaries } from '../../constants.js';
 import { ColorManagement } from '../../math/ColorManagement.js';
 
 // From https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/
@@ -124,6 +124,10 @@ function getToneMappingFunction( functionName, toneMapping ) {
 			toneMappingName = 'AgX';
 			break;
 
+		case KhronosNeutralToneMapping:
+			toneMappingName = 'KhronosNeutral';
+			break;
+
 		case CustomToneMapping:
 			toneMappingName = 'Custom';
 			break;

+ 1 - 0
test/unit/src/constants.tests.js

@@ -72,6 +72,7 @@ export default QUnit.module( 'Constants', () => {
 		assert.equal( Constants.ACESFilmicToneMapping, 4, 'ACESFilmicToneMapping is equal to 4' );
 		assert.equal( Constants.CustomToneMapping, 5, 'CustomToneMapping is equal to 5' );
 		assert.equal( Constants.AgXToneMapping, 6, 'AgXToneMapping is equal to 6' );
+		assert.equal( Constants.KhronosNeutralToneMapping, 7, 'KhronosNeutralToneMapping is equal to 7' );
 
 		assert.equal( Constants.AttachedBindMode, 'attached', 'AttachedBindMode is equal to attached' );
 		assert.equal( Constants.DetachedBindMode, 'detached', 'DetachedBindMode is equal to detached' );