Browse Source

WebGPURenderer: Add support for `NeutralToneMapping`. (#28609)

Michael Herzog 1 year ago
parent
commit
63a76802cb
1 changed files with 44 additions and 5 deletions
  1. 44 5
      examples/jsm/nodes/display/ToneMappingNode.js

+ 44 - 5
examples/jsm/nodes/display/ToneMappingNode.js

@@ -1,11 +1,12 @@
 import TempNode from '../core/TempNode.js';
 import { addNodeClass } from '../core/Node.js';
-import { addNodeElement, tslFn, nodeObject, float, mat3, vec3 } from '../shadernode/ShaderNode.js';
+import { addNodeElement, tslFn, nodeObject, float, mat3, vec3, If } from '../shadernode/ShaderNode.js';
 import { rendererReference } from '../accessors/RendererReferenceNode.js';
-import { clamp, log2, max, pow } from '../math/MathNode.js';
-import { mul } from '../math/OperatorNode.js';
+import { cond } from '../math/CondNode.js';
+import { clamp, log2, max, min, pow, mix } from '../math/MathNode.js';
+import { mul, sub, div } from '../math/OperatorNode.js';
 
-import { NoToneMapping, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, AgXToneMapping } from 'three';
+import { NoToneMapping, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, AgXToneMapping, NeutralToneMapping } from 'three';
 
 // exposure only
 const LinearToneMappingNode = tslFn( ( { color, exposure } ) => {
@@ -117,13 +118,51 @@ const AGXToneMappingNode = tslFn( ( { color, exposure } ) => {
 
 } );
 
+// https://modelviewer.dev/examples/tone-mapping
+
+const NeutralToneMappingNode = tslFn( ( { color, exposure } ) => {
+
+	const StartCompression = float( 0.8 - 0.04 );
+	const Desaturation = float( 0.15 );
+
+	color = color.mul( exposure );
+
+	const x = min( color.r, min( color.g, color.b ) );
+	const offset = cond( x.lessThan( 0.08 ), x.sub( mul( 6.25, x.mul( x ) ) ), 0.04 );
+
+	color.subAssign( offset );
+
+	const peak = max( color.r, max( color.g, color.b ) );
+
+	If( peak.lessThan( StartCompression ), () => {
+
+		return color;
+
+	} );
+
+	const d = sub( 1, StartCompression );
+	const newPeak = sub( 1, d.mul( d ).div( peak.add( d.sub( StartCompression ) ) ) );
+	color.mulAssign( newPeak.div( peak ) );
+	const g = sub( 1, div( 1, Desaturation.mul( peak.sub( newPeak ) ).add( 1 ) ) );
+
+	return mix( color, vec3( newPeak ), g );
+
+} ).setLayout( {
+	name: 'NeutralToneMapping',
+	type: 'vec3',
+	inputs: [
+		{ name: 'color', type: 'vec3' },
+		{ name: 'exposure', type: 'float' }
+	]
+} );
 
 const toneMappingLib = {
 	[ LinearToneMapping ]: LinearToneMappingNode,
 	[ ReinhardToneMapping ]: ReinhardToneMappingNode,
 	[ CineonToneMapping ]: OptimizedCineonToneMappingNode,
 	[ ACESFilmicToneMapping ]: ACESFilmicToneMappingNode,
-	[ AgXToneMapping ]: AGXToneMappingNode
+	[ AgXToneMapping ]: AGXToneMappingNode,
+	[ NeutralToneMapping ]: NeutralToneMappingNode
 };
 
 class ToneMappingNode extends TempNode {