Kaynağa Gözat

TSL: Introduce `tangentViewFrame` and `bitangentViewFrame` (#31282)

* introduce tangentFrame

* improve code style approach
sunag 10 ay önce
ebeveyn
işleme
cde19cf0bc

+ 25 - 1
src/nodes/accessors/Bitangent.js

@@ -1,6 +1,8 @@
 import { Fn } from '../tsl/TSLCore.js';
 import { normalGeometry, normalLocal, normalView, normalWorld } from './Normal.js';
 import { tangentGeometry, tangentLocal, tangentView, tangentWorld } from './Tangent.js';
+import { bitangentViewFrame } from './TangentUtils.js';
+import { directionToFaceDirection } from '../display/FrontFacingNode.js';
 
 /**
  * Returns the bitangent node and assigns it to a varying if the material is not flat shaded.
@@ -47,7 +49,29 @@ export const bitangentLocal = /*@__PURE__*/ getBitangent( normalLocal.cross( tan
  * @tsl
  * @type {Node<vec3>}
  */
-export const bitangentView = getBitangent( normalView.cross( tangentView ), 'v_bitangentView' ).normalize().toVar( 'bitangentView' );
+export const bitangentView = /*@__PURE__*/ ( Fn( ( { subBuildFn, geometry, material } ) => {
+
+	let node;
+
+	if ( subBuildFn === 'VERTEX' || geometry.hasAttribute( 'tangent' ) ) {
+
+		node = getBitangent( normalView.cross( tangentView ), 'v_bitangentView' ).normalize();
+
+	} else {
+
+		node = bitangentViewFrame;
+
+	}
+
+	if ( material.flatShading !== true ) {
+
+		node = directionToFaceDirection( node );
+
+	}
+
+	return node;
+
+}, 'vec3' ).once( [ 'NORMAL', 'VERTEX' ] ) )().toVar( 'bitangentView' );
 
 /**
  * TSL object that represents the vertex bitangent in world space of the current rendered object.

+ 25 - 1
src/nodes/accessors/Tangent.js

@@ -2,6 +2,8 @@ import { attribute } from '../core/AttributeNode.js';
 import { cameraViewMatrix } from './Camera.js';
 import { modelViewMatrix } from './ModelNode.js';
 import { Fn, vec4 } from '../tsl/TSLBase.js';
+import { tangentViewFrame } from './TangentUtils.js';
+import { directionToFaceDirection } from '../display/FrontFacingNode.js';
 
 /**
  * TSL object that represents the tangent attribute of the current rendered object.
@@ -35,7 +37,29 @@ export const tangentLocal = /*@__PURE__*/ tangentGeometry.xyz.toVar( 'tangentLoc
  * @tsl
  * @type {Node<vec3>}
  */
-export const tangentView = /*@__PURE__*/ modelViewMatrix.mul( vec4( tangentLocal, 0 ) ).xyz.toVarying( 'v_tangentView' ).normalize().toVar( 'tangentView' );
+export const tangentView = /*@__PURE__*/ ( Fn( ( { subBuildFn, geometry, material } ) => {
+
+	let node;
+
+	if ( subBuildFn === 'VERTEX' || geometry.hasAttribute( 'tangent' ) ) {
+
+		node = modelViewMatrix.mul( vec4( tangentLocal, 0 ) ).xyz.toVarying( 'v_tangentView' ).normalize();
+
+	} else {
+
+		node = tangentViewFrame;
+
+	}
+
+	if ( material.flatShading !== true ) {
+
+		node = directionToFaceDirection( node );
+
+	}
+
+	return node;
+
+}, 'vec3' ).once( [ 'NORMAL', 'VERTEX' ] ) )().toVar( 'tangentView' );
 
 /**
  * TSL object that represents the vertex tangent in world space of the current rendered object.

+ 46 - 0
src/nodes/accessors/TangentUtils.js

@@ -0,0 +1,46 @@
+import { uv as getUV } from './UV.js';
+import { positionView } from './Position.js';
+import { normalView } from './Normal.js';
+
+// Normal Mapping Without Precomputed Tangents
+// http://www.thetenthplanet.de/archives/1180
+
+const uv = getUV();
+
+const q0 = positionView.dFdx();
+const q1 = positionView.dFdy();
+const st0 = uv.dFdx();
+const st1 = uv.dFdy();
+
+const N = normalView;
+
+const q1perp = q1.cross( N );
+const q0perp = N.cross( q0 );
+
+const T = q1perp.mul( st0.x ).add( q0perp.mul( st1.x ) );
+const B = q1perp.mul( st0.y ).add( q0perp.mul( st1.y ) );
+
+const det = T.dot( T ).max( B.dot( B ) );
+const scale = det.equal( 0.0 ).select( 0.0, det.inverseSqrt() );
+
+/**
+ * Tangent vector in view space, computed dynamically from geometry and UV derivatives.
+ * Useful for normal mapping without precomputed tangents.
+ *
+ * Reference: http://www.thetenthplanet.de/archives/1180
+ *
+ * @tsl
+ * @type {Node<vec3>}
+ */
+export const tangentViewFrame = /*@__PURE__*/ T.mul( scale ).toVar( 'tangentViewFrame' );
+
+/**
+ * Bitangent vector in view space, computed dynamically from geometry and UV derivatives.
+ * Complements the tangentViewFrame for constructing the tangent space basis.
+ *
+ * Reference: http://www.thetenthplanet.de/archives/1180
+ *
+ * @tsl
+ * @type {Node<vec3>}
+ */
+export const bitangentViewFrame = /*@__PURE__*/ B.mul( scale ).toVar( 'bitangentViewFrame' );

+ 16 - 53
src/nodes/display/NormalMapNode.js

@@ -1,41 +1,11 @@
 import TempNode from '../core/TempNode.js';
-import { add } from '../math/OperatorNode.js';
 
 import { normalView, transformNormalToView } from '../accessors/Normal.js';
-import { positionView } from '../accessors/Position.js';
 import { TBNViewMatrix } from '../accessors/AccessorsUtils.js';
-import { uv } from '../accessors/UV.js';
-import { faceDirection } from './FrontFacingNode.js';
-import { Fn, nodeProxy, vec3 } from '../tsl/TSLBase.js';
+import { nodeProxy, vec3 } from '../tsl/TSLBase.js';
 
 import { TangentSpaceNormalMap, ObjectSpaceNormalMap } from '../../constants.js';
-
-// Normal Mapping Without Precomputed Tangents
-// http://www.thetenthplanet.de/archives/1180
-
-const perturbNormal2Arb = /*@__PURE__*/ Fn( ( inputs ) => {
-
-	const { eye_pos, surf_norm, mapN, uv } = inputs;
-
-	const q0 = eye_pos.dFdx();
-	const q1 = eye_pos.dFdy();
-	const st0 = uv.dFdx();
-	const st1 = uv.dFdy();
-
-	const N = surf_norm; // normalized
-
-	const q1perp = q1.cross( N );
-	const q0perp = N.cross( q0 );
-
-	const T = q1perp.mul( st0.x ).add( q0perp.mul( st1.x ) );
-	const B = q1perp.mul( st0.y ).add( q0perp.mul( st1.y ) );
-
-	const det = T.dot( T ).max( B.dot( B ) );
-	const scale = faceDirection.mul( det.inverseSqrt() );
-
-	return add( T.mul( mapN.x, scale ), B.mul( mapN.y, scale ), N.mul( mapN.z ) ).normalize();
-
-} );
+import { directionToFaceDirection } from './FrontFacingNode.js';
 
 /**
  * This class can be used for applying normals maps to materials.
@@ -89,7 +59,7 @@ class NormalMapNode extends TempNode {
 
 	}
 
-	setup( builder ) {
+	setup( { material } ) {
 
 		const { normalMapType, scaleNode } = this;
 
@@ -97,44 +67,37 @@ class NormalMapNode extends TempNode {
 
 		if ( scaleNode !== null ) {
 
-			normalMap = vec3( normalMap.xy.mul( scaleNode ), normalMap.z );
+			let scale = scaleNode;
 
-		}
-
-		let outputNode = null;
+			if ( material.flatShading === true ) {
 
-		if ( normalMapType === ObjectSpaceNormalMap ) {
+				scale = directionToFaceDirection( scale );
 
-			outputNode = transformNormalToView( normalMap );
+			}
 
-		} else if ( normalMapType === TangentSpaceNormalMap ) {
+			normalMap = vec3( normalMap.xy.mul( scale ), normalMap.z );
 
-			const tangent = builder.hasGeometryAttribute( 'tangent' );
+		}
 
-			if ( tangent === true ) {
+		let output = null;
 
-				outputNode = TBNViewMatrix.mul( normalMap ).normalize();
+		if ( normalMapType === ObjectSpaceNormalMap ) {
 
-			} else {
+			output = transformNormalToView( normalMap );
 
-				outputNode = perturbNormal2Arb( {
-					eye_pos: positionView,
-					surf_norm: normalView,
-					mapN: normalMap,
-					uv: uv()
-				} );
+		} else if ( normalMapType === TangentSpaceNormalMap ) {
 
-			}
+			output = TBNViewMatrix.mul( normalMap ).normalize();
 
 		} else {
 
 			console.error( `THREE.NodeMaterial: Unsupported normal map type: ${ normalMapType }` );
 
-			outputNode = normalView; // Fallback to default normal view
+			output = normalView; // Fallback to default normal view
 
 		}
 
-		return outputNode;
+		return output;
 
 	}
 

粤ICP备19079148号