diff --git a/examples/jsm/nodes/display/ToneMappingNode.js b/examples/jsm/nodes/display/ToneMappingNode.js index 357133a010b7f6..40bcd5b99379df 100644 --- a/examples/jsm/nodes/display/ToneMappingNode.js +++ b/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 {