Просмотр исходного кода

Matrix4: Introduce faster version of `determinant()`. (#33814)

Michael Herzog 3 недель назад
Родитель
Сommit
f3b7968c86

+ 22 - 0
src/math/Matrix4.js

@@ -649,6 +649,28 @@ class Matrix4 {
 
 	}
 
+	/**
+	 * Computes and returns the determinant of the upper-left 3x3 submatrix.
+	 *
+	 * For affine matrices (like an object's world matrix), this value equals the
+	 * full 4x4 {@link Matrix4#determinant} but is cheaper to compute.
+	 *
+	 * @return {number} The determinant of the upper-left 3x3 submatrix.
+	 */
+	determinant3x3() {
+
+		const te = this.elements;
+
+		const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ];
+		const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ];
+		const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ];
+
+		return n11 * ( n22 * n33 - n23 * n32 ) -
+			n12 * ( n21 * n33 - n23 * n31 ) +
+			n13 * ( n21 * n32 - n22 * n31 );
+
+	}
+
 	/**
 	 * Transposes this matrix in place.
 	 *

+ 1 - 1
src/renderers/WebGLRenderer.js

@@ -1185,7 +1185,7 @@ class WebGLRenderer {
 
 			if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null)
 
-			const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
+			const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant3x3() < 0 );
 
 			const program = setProgram( camera, scene, geometry, material, object );
 

+ 1 - 1
src/renderers/webgl-fallback/WebGLBackend.js

@@ -1080,7 +1080,7 @@ class WebGLBackend extends Backend {
 
 		this._bindUniforms( renderObject.getBindings() );
 
-		const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
+		const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant3x3() < 0 );
 
 		state.setMaterial( material, frontFaceCW, hardwareClippingPlanes );
 

+ 2 - 2
src/renderers/webgpu/WebGPUBackend.js

@@ -2058,7 +2058,7 @@ class WebGPUBackend extends Backend {
 		const colorFormat = utils.getCurrentColorFormat( renderObject.context );
 		const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
 		const primitiveTopology = utils.getPrimitiveTopology( object, material );
-		const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
+		const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant3x3() < 0 );
 
 		let needsUpdate = false;
 
@@ -2120,7 +2120,7 @@ class WebGPUBackend extends Backend {
 		// meshes with negative scale have a different frontFace render pipeline
 		// descriptor value so the following must be honored in the cache key
 
-		const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
+		const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant3x3() < 0 );
 
 		return [
 			material.transparent, material.blending, material.premultipliedAlpha,

+ 1 - 1
src/renderers/webgpu/utils/WebGPUPipelineUtils.js

@@ -847,7 +847,7 @@ class WebGPUPipelineUtils {
 
 		let flipSided = ( material.side === BackSide );
 
-		if ( object.isMesh && object.matrixWorld.determinant() < 0 ) flipSided = ! flipSided;
+		if ( object.isMesh && object.matrixWorld.determinant3x3() < 0 ) flipSided = ! flipSided;
 
 		descriptor.frontFace = ( flipSided === true ) ? GPUFrontFace.CW : GPUFrontFace.CCW;
 

+ 37 - 0
test/unit/src/math/Matrix4.tests.js

@@ -467,6 +467,43 @@ export default QUnit.module( 'Maths', () => {
 
 		} );
 
+		QUnit.test( 'determinant3x3', ( assert ) => {
+
+			// for affine matrices (the typical object world matrix), the 3x3 result
+			// equals the full 4x4 determinant since the bottom row is [ 0, 0, 0, 1 ]
+
+			const a = new Matrix4();
+			const position = new Vector3( 5, - 2, 3 );
+			const quaternion = new Quaternion().setFromEuler( new Euler( 0.1, - 0.7, 1.3 ) );
+
+			// translation + rotation + non-uniform scale
+
+			a.compose( position, quaternion, new Vector3( 2, 3, 0.5 ) );
+			assert.ok( Math.abs( a.determinant3x3() - a.determinant() ) <= eps, 'Affine matrix: Passed!' );
+
+			// reflection (negative scale on one axis flips the winding order)
+
+			a.compose( position, quaternion, new Vector3( 2, 3, - 0.5 ) );
+			assert.ok( a.determinant3x3() < 0, 'Reflection produces a negative determinant!' );
+			assert.ok( Math.abs( a.determinant3x3() - a.determinant() ) <= eps, 'Reflection matrix: Passed!' );
+
+			// shear
+
+			a.multiply( new Matrix4().makeShear( 0.5, 0, 0.2, 0, 0.7, 0 ) );
+			assert.ok( Math.abs( a.determinant3x3() - a.determinant() ) <= eps, 'Shear matrix: Passed!' );
+
+		} );
+
+		QUnit.test( 'determinant3x3 (projective matrix)', ( assert ) => {
+
+			// for non-affine (projective) matrices, the bottom row is not [ 0, 0, 0, 1 ]
+			// and so the 3x3 result generally differs from the full 4x4 determinant
+
+			const a = new Matrix4().makePerspective( - 1, 1, 1, - 1, 1, 100 );
+			assert.ok( Math.abs( a.determinant3x3() - a.determinant() ) > eps, 'Passed!' );
+
+		} );
+
 		QUnit.test( 'transpose', ( assert ) => {
 
 			const a = new Matrix4();

粤ICP备19079148号