浏览代码

NodeMaterial: Tone Mapping backward compatibility and fixes (#25215)

* some fixes

* ToneMappingNode: ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping

* NodeMaterial: Tone Mapping backward compatibility

* transformedNormalView: Revert to VarNode approach

* Nodes: Fix deserialization

* WebGPURenderer: Add default values

* Fix and added missing StoreOp

* update examples
sunag 2 年之前
父节点
当前提交
be161dac76

+ 2 - 0
examples/jsm/nodes/accessors/Object3DNode.js

@@ -58,6 +58,8 @@ class Object3DNode extends Node {
 
 
 		} else if ( scope === Object3DNode.POSITION ) {
 		} else if ( scope === Object3DNode.POSITION ) {
 
 
+			uniformNode.value = uniformNode.value || new Vector3();
+
 			uniformNode.value.setFromMatrixPosition( object.matrixWorld );
 			uniformNode.value.setFromMatrixPosition( object.matrixWorld );
 
 
 		} else if ( scope === Object3DNode.DIRECTION ) {
 		} else if ( scope === Object3DNode.DIRECTION ) {

+ 1 - 2
examples/jsm/nodes/core/InputNode.js

@@ -49,8 +49,7 @@ class InputNode extends Node {
 		super.deserialize( data );
 		super.deserialize( data );
 
 
 		this.nodeType = data.nodeType;
 		this.nodeType = data.nodeType;
-		this.value = getValueFromType( data.valueType );
-		this.value = data.value;
+		this.value = Array.isArray( data.value ) ? getValueFromType( data.valueType, ...data.value ) : data.value;
 
 
 		if ( this.value && this.value.fromArray ) this.value = this.value.fromArray( data.value );
 		if ( this.value && this.value.fromArray ) this.value = this.value.fromArray( data.value );
 
 

+ 2 - 2
examples/jsm/nodes/core/NodeUtils.js

@@ -112,11 +112,11 @@ export const getValueFromType = ( type, ...params ) => {
 
 
 	} else if ( type === 'bool' ) {
 	} else if ( type === 'bool' ) {
 
 
-		return false;
+		return params[ 0 ] || false;
 
 
 	} else if ( ( type === 'float' ) || ( type === 'int' ) || ( type === 'uint' ) ) {
 	} else if ( ( type === 'float' ) || ( type === 'int' ) || ( type === 'uint' ) ) {
 
 
-		return 0;
+		return params[ 0 ] || 0;
 
 
 	}
 	}
 
 

+ 83 - 7
examples/jsm/nodes/display/ToneMappingNode.js

@@ -1,18 +1,89 @@
 import TempNode from '../core/Node.js';
 import TempNode from '../core/Node.js';
-import { ShaderNode, mul, float } from '../shadernode/ShaderNodeBaseElements.js';
+import { ShaderNode, vec3, mat3, float, clamp, max, pow } from '../shadernode/ShaderNodeBaseElements.js';
 
 
-import { LinearToneMapping } from 'three';
+import { NoToneMapping, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping } from 'three';
 
 
 // exposure only
 // exposure only
 export const LinearToneMappingNode = new ShaderNode( ( { color, exposure } ) => {
 export const LinearToneMappingNode = new ShaderNode( ( { color, exposure } ) => {
 
 
-	return mul( color, exposure );
+	return color.mul( exposure );
 
 
 } );
 } );
 
 
+// source: https://www.cs.utah.edu/docs/techreports/2002/pdf/UUCS-02-001.pdf
+export const ReinhardToneMappingNode = new ShaderNode( ( { color, exposure } ) => {
+
+	color = color.mul( exposure );
+
+	return clamp( color.div( vec3( 1.0 ).add( color ) ) );
+
+} );
+
+// source: http://filmicworlds.com/blog/filmic-tonemapping-operators/
+export const OptimizedCineonToneMappingNode = new ShaderNode( ( { color, exposure } ) => {
+
+	// optimized filmic operator by Jim Hejl and Richard Burgess-Dawson
+	color = color.mul( exposure );
+	color = max( vec3( 0.0 ), color.sub( 0.004 ) );
+
+	const a = color.mul( color.mul( 6.2 ).add( 0.5 ) );
+	const b = color.mul( color.mul( 6.2 ).add( 1.7 ) ).add( 0.06 );
+
+	return pow( a.div( b ), vec3( 2.2 ) );
+
+} );
+
+// source: https://github.com/selfshadow/ltc_code/blob/master/webgl/shaders/ltc/ltc_blit.fs
+const RRTAndODTFit = new ShaderNode( ( { color } ) => {
+
+	const a = color.mul( color.add( 0.0245786 ) ).sub( 0.000090537 );
+	const b = color.mul( color.add( 0.4329510 ).mul( 0.983729 ) ).add( 0.238081 );
+
+	return a.div( b );
+
+} );
+
+// source: https://github.com/selfshadow/ltc_code/blob/master/webgl/shaders/ltc/ltc_blit.fs
+const ACESFilmicToneMappingNode = new ShaderNode( ( { color, exposure } ) => {
+
+	// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT
+	const ACESInputMat = mat3(
+		vec3( 0.59719, 0.07600, 0.02840 ), // transposed from source
+		vec3( 0.35458, 0.90834, 0.13383 ),
+		vec3( 0.04823, 0.01566, 0.83777 )
+	);
+
+	// ODT_SAT => XYZ => D60_2_D65 => sRGB
+	const ACESOutputMat = mat3(
+		vec3( 1.60475, - 0.10208, - 0.00327 ), // transposed from source
+		vec3( - 0.53108, 1.10813, - 0.07276 ),
+		vec3( - 0.07367, - 0.00605, 1.07602 )
+	);
+
+	color = color.mul( exposure ).div( 0.6 );
+
+	color = ACESInputMat.mul( color );
+
+	// Apply RRT and ODT
+	color = RRTAndODTFit.call( { color } );
+
+	color = ACESOutputMat.mul( color );
+
+	// Clamp to [0, 1]
+	return clamp( color );
+
+} );
+
+const toneMappingLib = {
+	[ LinearToneMapping ]: LinearToneMappingNode,
+	[ ReinhardToneMapping ]: ReinhardToneMappingNode,
+	[ CineonToneMapping ]: OptimizedCineonToneMappingNode,
+	[ ACESFilmicToneMapping ]: ACESFilmicToneMappingNode
+};
+
 class ToneMappingNode extends TempNode {
 class ToneMappingNode extends TempNode {
 
 
-	constructor( toneMapping, exposureNode = float( 1 ), colorNode = null ) {
+	constructor( toneMapping = NoToneMapping, exposureNode = float( 1 ), colorNode = null ) {
 
 
 		super( 'vec3' );
 		super( 'vec3' );
 
 
@@ -26,18 +97,23 @@ class ToneMappingNode extends TempNode {
 	construct( builder ) {
 	construct( builder ) {
 
 
 		const colorNode = this.colorNode || builder.context.color;
 		const colorNode = this.colorNode || builder.context.color;
-
 		const toneMapping = this.toneMapping;
 		const toneMapping = this.toneMapping;
+
+		if ( toneMapping === NoToneMapping ) return colorNode;
+
 		const toneMappingParams = { exposure: this.exposureNode, color: colorNode };
 		const toneMappingParams = { exposure: this.exposureNode, color: colorNode };
+		const toneMappingNode = toneMappingLib[ toneMapping ];
 
 
 		let outputNode = null;
 		let outputNode = null;
 
 
-		if ( toneMapping === LinearToneMapping ) {
+		if ( toneMappingNode ) {
 
 
-			outputNode = LinearToneMappingNode.call( toneMappingParams );
+			outputNode = toneMappingNode.call( toneMappingParams );
 
 
 		} else {
 		} else {
 
 
+			console.error( 'ToneMappingNode: Unsupported Tone Mapping configuration.', toneMapping );
+
 			outputNode = colorNode;
 			outputNode = colorNode;
 
 
 		}
 		}

+ 14 - 3
examples/jsm/nodes/materials/NodeMaterial.js

@@ -1,8 +1,9 @@
-import { Material, ShaderMaterial } from 'three';
+import { Material, ShaderMaterial, NoToneMapping } from 'three';
 import { getNodesKeys, getCacheKey } from '../core/NodeUtils.js';
 import { getNodesKeys, getCacheKey } from '../core/NodeUtils.js';
 import StackNode from '../core/StackNode.js';
 import StackNode from '../core/StackNode.js';
 import LightsNode from '../lighting/LightsNode.js';
 import LightsNode from '../lighting/LightsNode.js';
 import EnvironmentNode from '../lighting/EnvironmentNode.js';
 import EnvironmentNode from '../lighting/EnvironmentNode.js';
+import ToneMappingNode from '../display/ToneMappingNode.js';
 import AONode from '../lighting/AONode.js';
 import AONode from '../lighting/AONode.js';
 import {
 import {
 	float, vec3, vec4,
 	float, vec3, vec4,
@@ -223,12 +224,22 @@ class NodeMaterial extends ShaderMaterial {
 
 
 		// TONE MAPPING
 		// TONE MAPPING
 
 
-		if ( renderer.toneMappingNode && renderer.toneMappingNode.isNode === true ) {
+		let toneMappingNode = renderer.toneMappingNode;
 
 
-			outgoingLight = context( renderer.toneMappingNode, { color: outgoingLight } );
+		if ( ! toneMappingNode && renderer.toneMapping !== NoToneMapping ) {
+
+			toneMappingNode = new ToneMappingNode( renderer.toneMapping, reference( 'toneMappingExposure', 'float', renderer ), outgoingLight );
+
+		}
+
+		if ( toneMappingNode && toneMappingNode.isNode === true ) {
+
+			outgoingLight = context( toneMappingNode, { color: outgoingLight } );
 
 
 		}
 		}
 
 
+		// @TODO: Optimize outputNode to vec3.
+
 		let outputNode = vec4( outgoingLight, opacity );
 		let outputNode = vec4( outgoingLight, opacity );
 
 
 		// ENCODING
 		// ENCODING

+ 1 - 1
examples/jsm/nodes/shadernode/ShaderNodeBaseElements.js

@@ -250,7 +250,7 @@ export const normalGeometry = nodeImmutable( NormalNode, NormalNode.GEOMETRY );
 export const normalLocal = nodeImmutable( NormalNode, NormalNode.LOCAL );
 export const normalLocal = nodeImmutable( NormalNode, NormalNode.LOCAL );
 export const normalView = nodeImmutable( NormalNode, NormalNode.VIEW );
 export const normalView = nodeImmutable( NormalNode, NormalNode.VIEW );
 export const normalWorld = nodeImmutable( NormalNode, NormalNode.WORLD );
 export const normalWorld = nodeImmutable( NormalNode, NormalNode.WORLD );
-export const transformedNormalView = nodeImmutable( PropertyNode, 'vec3', 'TransformedNormalView' );
+export const transformedNormalView = nodeImmutable( VarNode, normalView, 'TransformedNormalView' );
 export const transformedNormalWorld = normalize( transformDirection( transformedNormalView, cameraViewMatrix ) );
 export const transformedNormalWorld = normalize( transformDirection( transformedNormalView, cameraViewMatrix ) );
 
 
 export const tangentGeometry = nodeImmutable( TangentNode, TangentNode.GEOMETRY );
 export const tangentGeometry = nodeImmutable( TangentNode, TangentNode.GEOMETRY );

+ 5 - 0
examples/jsm/renderers/webgpu/WebGPUBackground.js

@@ -132,6 +132,7 @@ class WebGPUBackground {
 			} else {
 			} else {
 
 
 				colorAttachment.loadOp = GPULoadOp.Load;
 				colorAttachment.loadOp = GPULoadOp.Load;
+				colorAttachment.storeOp = GPUStoreOp.Store;
 
 
 			}
 			}
 
 
@@ -139,10 +140,12 @@ class WebGPUBackground {
 
 
 				depthStencilAttachment.depthClearValue = renderer._clearDepth;
 				depthStencilAttachment.depthClearValue = renderer._clearDepth;
 				depthStencilAttachment.depthLoadOp = GPULoadOp.Clear;
 				depthStencilAttachment.depthLoadOp = GPULoadOp.Clear;
+				depthStencilAttachment.depthStoreOp = GPUStoreOp.Store;
 
 
 			} else {
 			} else {
 
 
 				depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
 				depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
+				depthStencilAttachment.depthStoreOp = GPUStoreOp.Store;
 
 
 			}
 			}
 
 
@@ -150,10 +153,12 @@ class WebGPUBackground {
 
 
 				depthStencilAttachment.stencilClearValue = renderer._clearStencil;
 				depthStencilAttachment.stencilClearValue = renderer._clearStencil;
 				depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear;
 				depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear;
+				depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store;
 
 
 			} else {
 			} else {
 
 
 				depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
 				depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
+				depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store;
 
 
 			}
 			}
 
 

+ 4 - 1
examples/jsm/renderers/webgpu/WebGPURenderer.js

@@ -15,7 +15,7 @@ import WebGPUBackground from './WebGPUBackground.js';
 import WebGPUNodes from './nodes/WebGPUNodes.js';
 import WebGPUNodes from './nodes/WebGPUNodes.js';
 import WebGPUUtils from './WebGPUUtils.js';
 import WebGPUUtils from './WebGPUUtils.js';
 
 
-import { Frustum, Matrix4, Vector3, Color, LinearEncoding } from 'three';
+import { Frustum, Matrix4, Vector3, Color, LinearEncoding, NoToneMapping } from 'three';
 
 
 console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' );
 console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' );
 
 
@@ -100,6 +100,9 @@ class WebGPURenderer {
 
 
 		this.outputEncoding = LinearEncoding;
 		this.outputEncoding = LinearEncoding;
 
 
+		this.toneMapping = NoToneMapping;
+		this.toneMappingExposure = 1.0;
+
 		this.sortObjects = true;
 		this.sortObjects = true;
 
 
 		// internals
 		// internals

+ 2 - 1
examples/webgpu_lights_phong.html

@@ -140,7 +140,8 @@
 				renderer.setAnimationLoop( animate );
 				renderer.setAnimationLoop( animate );
 				document.body.appendChild( renderer.domElement );
 				document.body.appendChild( renderer.domElement );
 				renderer.outputEncoding = THREE.sRGBEncoding;
 				renderer.outputEncoding = THREE.sRGBEncoding;
-				renderer.toneMappingNode = new Nodes.ToneMappingNode( THREE.LinearToneMapping, .2 );
+				renderer.toneMapping = THREE.ACESFilmicToneMapping;
+				renderer.toneMappingExposure = .2;
 
 
 				// controls
 				// controls
 
 

+ 2 - 1
examples/webgpu_lights_selective.html

@@ -142,7 +142,8 @@
 				renderer.setAnimationLoop( animate );
 				renderer.setAnimationLoop( animate );
 				document.body.appendChild( renderer.domElement );
 				document.body.appendChild( renderer.domElement );
 				renderer.outputEncoding = THREE.sRGBEncoding;
 				renderer.outputEncoding = THREE.sRGBEncoding;
-				renderer.toneMappingNode = new Nodes.ToneMappingNode( THREE.LinearToneMapping, .2 );
+				renderer.toneMapping = THREE.ACESFilmicToneMapping;
+				renderer.toneMappingExposure = .2;
 
 
 				//controls
 				//controls