Переглянути джерело

RemapNode + BlendModeNode (#24649)

* use construct) instead of generate() to improve cache

* add RemapNode

* add BlendModeNode

* add missing functions

* cleanup

* cleanup

* add settings argument

* add remapClamp

* cleanup
sunag 2 роки тому
батько
коміт
d678ff9841

+ 7 - 1
examples/jsm/nodes/Nodes.js

@@ -52,6 +52,7 @@ import RangeNode from './geometry/RangeNode.js';
 import ComputeNode from './gpgpu/ComputeNode.js';
 import ComputeNode from './gpgpu/ComputeNode.js';
 
 
 // display
 // display
+import BlendModeNode from './display/BlendModeNode.js';
 import ColorAdjustmentNode from './display/ColorAdjustmentNode.js';
 import ColorAdjustmentNode from './display/ColorAdjustmentNode.js';
 import ColorSpaceNode from './display/ColorSpaceNode.js';
 import ColorSpaceNode from './display/ColorSpaceNode.js';
 import FrontFacingNode from './display/FrontFacingNode.js';
 import FrontFacingNode from './display/FrontFacingNode.js';
@@ -80,6 +81,7 @@ import JoinNode from './utils/JoinNode.js';
 import MatcapUVNode from './utils/MatcapUVNode.js';
 import MatcapUVNode from './utils/MatcapUVNode.js';
 import MaxMipLevelNode from './utils/MaxMipLevelNode.js';
 import MaxMipLevelNode from './utils/MaxMipLevelNode.js';
 import OscNode from './utils/OscNode.js';
 import OscNode from './utils/OscNode.js';
+import RemapNode from './utils/RemapNode.js';
 import RotateUVNode from './utils/RotateUVNode.js';
 import RotateUVNode from './utils/RotateUVNode.js';
 import SplitNode from './utils/SplitNode.js';
 import SplitNode from './utils/SplitNode.js';
 import SpriteSheetUVNode from './utils/SpriteSheetUVNode.js';
 import SpriteSheetUVNode from './utils/SpriteSheetUVNode.js';
@@ -111,7 +113,7 @@ export * from './materials/Materials.js';
 export * from './shadernode/ShaderNodeElements.js';
 export * from './shadernode/ShaderNodeElements.js';
 
 
 // shader stages
 // shader stages
-export { defaultShaderStages }  from './core/NodeBuilder.js';
+export { defaultShaderStages } from './core/NodeBuilder.js';
 
 
 const nodeLib = {
 const nodeLib = {
 	// core
 	// core
@@ -168,6 +170,7 @@ const nodeLib = {
 	UserDataNode,
 	UserDataNode,
 
 
 	// display
 	// display
+	BlendModeNode,
 	ColorAdjustmentNode,
 	ColorAdjustmentNode,
 	ColorSpaceNode,
 	ColorSpaceNode,
 	FrontFacingNode,
 	FrontFacingNode,
@@ -196,6 +199,7 @@ const nodeLib = {
 	MatcapUVNode,
 	MatcapUVNode,
 	MaxMipLevelNode,
 	MaxMipLevelNode,
 	OscNode,
 	OscNode,
+	RemapNode,
 	RotateUVNode,
 	RotateUVNode,
 	SplitNode,
 	SplitNode,
 	SpriteSheetUVNode,
 	SpriteSheetUVNode,
@@ -280,6 +284,7 @@ export {
 	UserDataNode,
 	UserDataNode,
 
 
 	// display
 	// display
+	BlendModeNode,
 	ColorAdjustmentNode,
 	ColorAdjustmentNode,
 	ColorSpaceNode,
 	ColorSpaceNode,
 	FrontFacingNode,
 	FrontFacingNode,
@@ -308,6 +313,7 @@ export {
 	MatcapUVNode,
 	MatcapUVNode,
 	MaxMipLevelNode,
 	MaxMipLevelNode,
 	OscNode,
 	OscNode,
+	RemapNode,
 	RotateUVNode,
 	RotateUVNode,
 	SplitNode,
 	SplitNode,
 	SpriteSheetUVNode,
 	SpriteSheetUVNode,

+ 85 - 0
examples/jsm/nodes/display/BlendModeNode.js

@@ -0,0 +1,85 @@
+import TempNode from '../core/Node.js';
+import { ShaderNode, EPSILON, vec3, sub, mul, div, cond, lessThan, equal, max } from '../shadernode/ShaderNodeBaseElements.js';
+
+export const BurnNode = new ShaderNode( ( { base, blend } ) => {
+
+	const fn = ( c ) => cond( lessThan( blend[ c ], EPSILON ), blend[ c ], max( sub( 1.0, div( sub( 1.0, base[ c ] ), blend[ c ] ) ), 0 ) );
+
+	return vec3( fn( 'x' ), fn( 'y' ), fn( 'z' ) );
+
+} );
+
+export const DodgeNode = new ShaderNode( ( { base, blend } ) => {
+
+	const fn = ( c ) => cond( equal( blend[ c ], 1.0 ), blend[ c ], max( div( base[ c ], sub( 1.0, blend[ c ] ) ), 0 ) );
+
+	return vec3( fn( 'x' ), fn( 'y' ), fn( 'z' ) );
+
+} );
+
+export const ScreenNode = new ShaderNode( ( { base, blend } ) => {
+
+	const fn = ( c ) => sub( 1.0, mul( sub( 1.0, base[ c ] ), sub( 1.0, blend[ c ] ) ) );
+
+	return vec3( fn( 'x' ), fn( 'y' ), fn( 'z' ) );
+
+} );
+
+export const OverlayNode = new ShaderNode( ( { base, blend } ) => {
+
+	const fn = ( c ) => cond( lessThan( base[ c ], 0.5 ), mul( 2.0, base[ c ], blend[ c ] ), sub( 1.0, mul( sub( 1.0, base[ c ] ), sub( 1.0, blend[ c ] ) ) ) );
+
+	return vec3( fn( 'x' ), fn( 'y' ), fn( 'z' ) );
+
+} );
+
+class BlendModeNode extends TempNode {
+
+	static BURN = 'burn';
+	static DODGE = 'dodge';
+	static SCREEN = 'screen';
+	static OVERLAY = 'overlay';
+
+	constructor( blendMode, baseNode, blendNode ) {
+
+		super();
+
+		this.blendMode = blendMode;
+
+		this.baseNode = baseNode;
+		this.blendNode = blendNode;
+
+	}
+
+	construct() {
+
+		const { blendMode, baseNode, blendNode } = this;
+		const params = { base: baseNode, blend: blendNode };
+
+		let outputNode = null;
+
+		if ( blendMode === BlendModeNode.BURN ) {
+
+			outputNode = BurnNode.call( params );
+
+		} else if ( blendMode === BlendModeNode.DODGE ) {
+
+			outputNode = DodgeNode.call( params );
+
+		} else if ( blendMode === BlendModeNode.SCREEN ) {
+
+			outputNode = ScreenNode.call( params );
+
+		} else if ( blendMode === BlendModeNode.OVERLAY ) {
+
+			outputNode = OverlayNode.call( params );
+
+		}
+
+		return outputNode;
+
+	}
+
+}
+
+export default BlendModeNode;

+ 10 - 2
examples/jsm/nodes/math/OperatorNode.js

@@ -163,14 +163,22 @@ class OperatorNode extends TempNode {
 
 
 				return a;
 				return a;
 
 
-			} else if ( op === '>' && outputLength > 1 ) {
+			} else if ( op === '<' && outputLength > 1 ) {
 
 
-				return builder.format( `${ builder.getMethod( 'greaterThan' ) }( ${a}, ${b} )`, type, output );
+				return builder.format( `${ builder.getMethod( 'lessThan' ) }( ${a}, ${b} )`, type, output );
 
 
 			} else if ( op === '<=' && outputLength > 1 ) {
 			} else if ( op === '<=' && outputLength > 1 ) {
 
 
 				return builder.format( `${ builder.getMethod( 'lessThanEqual' ) }( ${a}, ${b} )`, type, output );
 				return builder.format( `${ builder.getMethod( 'lessThanEqual' ) }( ${a}, ${b} )`, type, output );
 
 
+			} else if ( op === '>' && outputLength > 1 ) {
+
+				return builder.format( `${ builder.getMethod( 'greaterThan' ) }( ${a}, ${b} )`, type, output );
+
+			} else if ( op === '>=' && outputLength > 1 ) {
+
+				return builder.format( `${ builder.getMethod( 'greaterThanEqual' ) }( ${a}, ${b} )`, type, output );
+
 			} else {
 			} else {
 
 
 				return builder.format( `( ${a} ${this.op} ${b} )`, type, output );
 				return builder.format( `( ${a} ${this.op} ${b} )`, type, output );

+ 9 - 7
examples/jsm/nodes/shadernode/ShaderNode.js

@@ -107,31 +107,33 @@ const ShaderNodeArray = function ( array ) {
 
 
 };
 };
 
 
-const ShaderNodeProxy = function ( NodeClass, scope = null, factor = null ) {
+const ShaderNodeProxy = function ( NodeClass, scope = null, factor = null, settings = null ) {
+
+	const assignNode = ( node ) => nodeObject( settings !== null ? Object.assign( node, settings ) : node );
 
 
 	if ( scope === null ) {
 	if ( scope === null ) {
 
 
 		return ( ...params ) => {
 		return ( ...params ) => {
 
 
-			return nodeObject( new NodeClass( ...nodeArray( params ) ) );
+			return assignNode( new NodeClass( ...nodeArray( params ) ) );
 
 
 		};
 		};
 
 
-	} else if ( factor === null ) {
+	} else if ( factor !== null ) {
+
+		factor = nodeObject( factor );
 
 
 		return ( ...params ) => {
 		return ( ...params ) => {
 
 
-			return nodeObject( new NodeClass( scope, ...nodeArray( params ) ) );
+			return assignNode( new NodeClass( scope, ...nodeArray( params ), factor ) );
 
 
 		};
 		};
 
 
 	} else {
 	} else {
 
 
-		factor = nodeObject( factor );
-
 		return ( ...params ) => {
 		return ( ...params ) => {
 
 
-			return nodeObject( new NodeClass( scope, ...nodeArray( params ), factor ) );
+			return assignNode( new NodeClass( scope, ...nodeArray( params ) ) );
 
 
 		};
 		};
 
 

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

@@ -273,5 +273,6 @@ export const element = nodeProxy( ArrayElementNode );
 
 
 // miscellaneous
 // miscellaneous
 
 
+export const difference = ( a, b ) => nodeObject( abs( sub( a, b ) ) );
 export const dotNV = saturate( dot( transformedNormalView, positionViewDirection ) );
 export const dotNV = saturate( dot( transformedNormalView, positionViewDirection ) );
 export const transformedNormalWorld = normalize( transformDirection( transformedNormalView, cameraViewMatrix ) );
 export const transformedNormalWorld = normalize( transformDirection( transformedNormalView, cameraViewMatrix ) );

+ 10 - 0
examples/jsm/nodes/shadernode/ShaderNodeElements.js

@@ -5,6 +5,7 @@ import ReflectVectorNode from '../accessors/ReflectVectorNode.js';
 import SkinningNode from '../accessors/SkinningNode.js';
 import SkinningNode from '../accessors/SkinningNode.js';
 
 
 // display
 // display
+import BlendModeNode from '../display/BlendModeNode.js';
 import ColorAdjustmentNode from '../display/ColorAdjustmentNode.js';
 import ColorAdjustmentNode from '../display/ColorAdjustmentNode.js';
 import ColorSpaceNode from '../display/ColorSpaceNode.js';
 import ColorSpaceNode from '../display/ColorSpaceNode.js';
 import NormalMapNode from '../display/NormalMapNode.js';
 import NormalMapNode from '../display/NormalMapNode.js';
@@ -19,6 +20,7 @@ import LightingContextNode from '../lighting/LightingContextNode.js';
 import MatcapUVNode from '../utils/MatcapUVNode.js';
 import MatcapUVNode from '../utils/MatcapUVNode.js';
 import MaxMipLevelNode from '../utils/MaxMipLevelNode.js';
 import MaxMipLevelNode from '../utils/MaxMipLevelNode.js';
 import OscNode from '../utils/OscNode.js';
 import OscNode from '../utils/OscNode.js';
+import RemapNode from '../utils/RemapNode.js';
 import RotateUVNode from '../utils/RotateUVNode.js';
 import RotateUVNode from '../utils/RotateUVNode.js';
 import SpriteSheetUVNode from '../utils/SpriteSheetUVNode.js';
 import SpriteSheetUVNode from '../utils/SpriteSheetUVNode.js';
 import TimerNode from '../utils/TimerNode.js';
 import TimerNode from '../utils/TimerNode.js';
@@ -72,6 +74,11 @@ export const skinning = nodeProxy( SkinningNode );
 
 
 // display
 // display
 
 
+export const burn = nodeProxy( BlendModeNode, BlendModeNode.BURN );
+export const dodge = nodeProxy( BlendModeNode, BlendModeNode.DODGE );
+export const overlay = nodeProxy( BlendModeNode, BlendModeNode.OVERLAY );
+export const screen = nodeProxy( BlendModeNode, BlendModeNode.SCREEN );
+
 export const saturation = nodeProxy( ColorAdjustmentNode, ColorAdjustmentNode.SATURATION );
 export const saturation = nodeProxy( ColorAdjustmentNode, ColorAdjustmentNode.SATURATION );
 export const vibrance = nodeProxy( ColorAdjustmentNode, ColorAdjustmentNode.VIBRANCE );
 export const vibrance = nodeProxy( ColorAdjustmentNode, ColorAdjustmentNode.VIBRANCE );
 export const hue = nodeProxy( ColorAdjustmentNode, ColorAdjustmentNode.HUE );
 export const hue = nodeProxy( ColorAdjustmentNode, ColorAdjustmentNode.HUE );
@@ -97,6 +104,9 @@ export const oscSquare = nodeProxy( OscNode, OscNode.SQUARE );
 export const oscTriangle = nodeProxy( OscNode, OscNode.TRIANGLE );
 export const oscTriangle = nodeProxy( OscNode, OscNode.TRIANGLE );
 export const oscSawtooth = nodeProxy( OscNode, OscNode.SAWTOOTH );
 export const oscSawtooth = nodeProxy( OscNode, OscNode.SAWTOOTH );
 
 
+export const remap = nodeProxy( RemapNode, null, null, { doClamp: false } );
+export const remapClamp = nodeProxy( RemapNode );
+
 export const rotateUV = nodeProxy( RotateUVNode );
 export const rotateUV = nodeProxy( RotateUVNode );
 
 
 export const spritesheetUV = nodeProxy( SpriteSheetUVNode );
 export const spritesheetUV = nodeProxy( SpriteSheetUVNode );

+ 2 - 2
examples/jsm/nodes/utils/OscNode.js

@@ -24,7 +24,7 @@ class OscNode extends Node {
 
 
 	}
 	}
 
 
-	generate( builder ) {
+	construct() {
 
 
 		const method = this.method;
 		const method = this.method;
 		const timeNode = this.timeNode;
 		const timeNode = this.timeNode;
@@ -49,7 +49,7 @@ class OscNode extends Node {
 
 
 		}
 		}
 
 
-		return outputNode.build( builder );
+		return outputNode;
 
 
 	}
 	}
 
 

+ 34 - 0
examples/jsm/nodes/utils/RemapNode.js

@@ -0,0 +1,34 @@
+import Node from '../core/Node.js';
+import { add, sub, div, mul, saturate } from '../shadernode/ShaderNodeBaseElements.js';
+
+class RemapNode extends Node {
+
+	constructor( node, inLowNode, inHighNode, outLowNode, outHighNode ) {
+
+		super();
+
+		this.node = node;
+		this.inLowNode = inLowNode;
+		this.inHighNode = inHighNode;
+		this.outLowNode = outLowNode;
+		this.outHighNode = outHighNode;
+
+		this.doClamp = true;
+
+	}
+
+	construct() {
+
+		const { node, inLowNode, inHighNode, outLowNode, outHighNode, doClamp } = this;
+
+		let t = div( sub( node, inLowNode ), sub( inHighNode, inLowNode ) );
+
+		if ( doClamp === true ) t = saturate( t );
+
+		return add( mul( sub( outHighNode, outLowNode ), t ), outLowNode );
+
+	}
+
+}
+
+export default RemapNode;