|
|
@@ -10063,7 +10063,7 @@ class Matrix4 {
|
|
|
*/
|
|
|
extractBasis( xAxis, yAxis, zAxis ) {
|
|
|
|
|
|
- if ( this.determinant() === 0 ) {
|
|
|
+ if ( this.determinantAffine() === 0 ) {
|
|
|
|
|
|
xAxis.set( 1, 0, 0 );
|
|
|
yAxis.set( 0, 1, 0 );
|
|
|
@@ -10113,7 +10113,7 @@ class Matrix4 {
|
|
|
*/
|
|
|
extractRotation( m ) {
|
|
|
|
|
|
- if ( m.determinant() === 0 ) {
|
|
|
+ if ( m.determinantAffine() === 0 ) {
|
|
|
|
|
|
return this.identity();
|
|
|
|
|
|
@@ -10474,6 +10474,31 @@ class Matrix4 {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Computes and returns the determinant of the 4x4 matrix, but assumes the
|
|
|
+ * matrix is affine, saving some computations.
|
|
|
+ *
|
|
|
+ * For affine matrices (like an object's world matrix), this value equals the
|
|
|
+ * full 4x4 {@link Matrix4#determinant} but is cheaper to compute.
|
|
|
+ *
|
|
|
+ * Assumes the bottom row is [0, 0, 0, 1].
|
|
|
+ *
|
|
|
+ * @return {number} The determinant of the matrix.
|
|
|
+ */
|
|
|
+ determinantAffine() {
|
|
|
+
|
|
|
+ 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.
|
|
|
*
|
|
|
@@ -10883,7 +10908,7 @@ class Matrix4 {
|
|
|
position.y = te[ 13 ];
|
|
|
position.z = te[ 14 ];
|
|
|
|
|
|
- const det = this.determinant();
|
|
|
+ const det = this.determinantAffine();
|
|
|
|
|
|
if ( det === 0 ) {
|
|
|
|
|
|
@@ -25700,7 +25725,6 @@ class Frustum {
|
|
|
}
|
|
|
|
|
|
const _projScreenMatrix$2 = /*@__PURE__*/ new Matrix4();
|
|
|
-const _frustum$1 = /*@__PURE__*/ new Frustum();
|
|
|
|
|
|
/**
|
|
|
* FrustumArray is used to determine if an object is visible in at least one camera
|
|
|
@@ -25722,220 +25746,191 @@ class FrustumArray {
|
|
|
*/
|
|
|
this.coordinateSystem = WebGLCoordinateSystem;
|
|
|
|
|
|
+ /**
|
|
|
+ * A pool of frustum instances. It may hold more entries than are
|
|
|
+ * currently in use; surplus instances are kept for reuse to avoid
|
|
|
+ * reallocating when array cameras of different lengths are rendered.
|
|
|
+ *
|
|
|
+ * @private
|
|
|
+ * @type {Array<Frustum>}
|
|
|
+ */
|
|
|
+ this._frustums = [];
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The number of frustums in {@link FrustumArray#_frustums} that are currently
|
|
|
+ * in use.
|
|
|
+ *
|
|
|
+ * @private
|
|
|
+ * @type {number}
|
|
|
+ * @default 0
|
|
|
+ */
|
|
|
+ this._count = 0;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Returns `true` if the 3D object's bounding sphere is intersecting any frustum
|
|
|
- * from the camera array.
|
|
|
+ * Computes and caches a frustum for each camera of the given array camera.
|
|
|
*
|
|
|
- * @param {Object3D} object - The 3D object to test.
|
|
|
- * @param {Object} cameraArray - An object with a cameras property containing an array of cameras.
|
|
|
- * @return {boolean} Whether the 3D object is visible in any camera.
|
|
|
+ * @param {ArrayCamera} cameraArray - The array camera whose sub-cameras define the frustums.
|
|
|
+ * @return {FrustumArray} A reference to this frustum array.
|
|
|
*/
|
|
|
- intersectsObject( object, cameraArray ) {
|
|
|
+ setFromArrayCamera( cameraArray ) {
|
|
|
|
|
|
- if ( ! cameraArray.isArrayCamera || cameraArray.cameras.length === 0 ) {
|
|
|
+ const cameras = cameraArray.cameras;
|
|
|
+ const frustums = this._frustums;
|
|
|
|
|
|
- return false;
|
|
|
+ for ( let i = 0; i < cameras.length; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ const camera = cameras[ i ];
|
|
|
|
|
|
- for ( let i = 0; i < cameraArray.cameras.length; i ++ ) {
|
|
|
+ _projScreenMatrix$2.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
|
|
|
|
|
|
- const camera = cameraArray.cameras[ i ];
|
|
|
+ if ( frustums[ i ] === undefined ) frustums[ i ] = new Frustum();
|
|
|
|
|
|
- _projScreenMatrix$2.multiplyMatrices(
|
|
|
- camera.projectionMatrix,
|
|
|
- camera.matrixWorldInverse
|
|
|
- );
|
|
|
-
|
|
|
- _frustum$1.setFromProjectionMatrix(
|
|
|
- _projScreenMatrix$2,
|
|
|
- camera.coordinateSystem,
|
|
|
- camera.reversedDepth
|
|
|
- );
|
|
|
-
|
|
|
- if ( _frustum$1.intersectsObject( object ) ) {
|
|
|
-
|
|
|
- return true; // Object is visible in at least one camera
|
|
|
-
|
|
|
- }
|
|
|
+ frustums[ i ].setFromProjectionMatrix( _projScreenMatrix$2, camera.coordinateSystem, camera.reversedDepth );
|
|
|
|
|
|
}
|
|
|
|
|
|
- return false; // Not visible in any camera
|
|
|
+ this._count = cameras.length;
|
|
|
+
|
|
|
+ return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Returns `true` if the given sprite is intersecting any frustum
|
|
|
- * from the camera array.
|
|
|
+ * Returns `true` if the 3D object's bounding sphere is intersecting any cached frustum.
|
|
|
*
|
|
|
- * @param {Sprite} sprite - The sprite to test.
|
|
|
- * @param {Object} cameraArray - An object with a cameras property containing an array of cameras.
|
|
|
- * @return {boolean} Whether the sprite is visible in any camera.
|
|
|
+ * {@link FrustumArray#setFromArrayCamera} must be called once per render before this method.
|
|
|
+ *
|
|
|
+ * @param {Object3D} object - The 3D object to test.
|
|
|
+ * @return {boolean} Whether the 3D object is visible in any camera.
|
|
|
*/
|
|
|
- intersectsSprite( sprite, cameraArray ) {
|
|
|
+ intersectsObject( object ) {
|
|
|
|
|
|
- if ( ! cameraArray || ! cameraArray.cameras || cameraArray.cameras.length === 0 ) {
|
|
|
+ const frustums = this._frustums;
|
|
|
|
|
|
- return false;
|
|
|
+ for ( let i = 0; i < this._count; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ if ( frustums[ i ].intersectsObject( object ) ) return true;
|
|
|
|
|
|
- for ( let i = 0; i < cameraArray.cameras.length; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- const camera = cameraArray.cameras[ i ];
|
|
|
+ return false;
|
|
|
|
|
|
- _projScreenMatrix$2.multiplyMatrices(
|
|
|
- camera.projectionMatrix,
|
|
|
- camera.matrixWorldInverse
|
|
|
- );
|
|
|
+ }
|
|
|
|
|
|
- _frustum$1.setFromProjectionMatrix(
|
|
|
- _projScreenMatrix$2,
|
|
|
- camera.coordinateSystem,
|
|
|
- camera.reversedDepth
|
|
|
- );
|
|
|
+ /**
|
|
|
+ * Returns `true` if the given sprite is intersecting any cached frustum.
|
|
|
+ *
|
|
|
+ * {@link FrustumArray#setFromArrayCamera} must be called once per render before this method.
|
|
|
+ *
|
|
|
+ * @param {Sprite} sprite - The sprite to test.
|
|
|
+ * @return {boolean} Whether the sprite is visible in any camera.
|
|
|
+ */
|
|
|
+ intersectsSprite( sprite ) {
|
|
|
|
|
|
- if ( _frustum$1.intersectsSprite( sprite ) ) {
|
|
|
+ const frustums = this._frustums;
|
|
|
|
|
|
- return true; // Sprite is visible in at least one camera
|
|
|
+ for ( let i = 0; i < this._count; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ if ( frustums[ i ].intersectsSprite( sprite ) ) return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
- return false; // Not visible in any camera
|
|
|
+ return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Returns `true` if the given bounding sphere is intersecting any frustum
|
|
|
- * from the camera array.
|
|
|
+ * Returns `true` if the given bounding sphere is intersecting any cached frustum.
|
|
|
+ *
|
|
|
+ * {@link FrustumArray#setFromArrayCamera} must be called once per render before this method.
|
|
|
*
|
|
|
* @param {Sphere} sphere - The bounding sphere to test.
|
|
|
- * @param {Object} cameraArray - An object with a cameras property containing an array of cameras.
|
|
|
* @return {boolean} Whether the sphere is visible in any camera.
|
|
|
*/
|
|
|
- intersectsSphere( sphere, cameraArray ) {
|
|
|
-
|
|
|
- if ( ! cameraArray || ! cameraArray.cameras || cameraArray.cameras.length === 0 ) {
|
|
|
-
|
|
|
- return false;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- for ( let i = 0; i < cameraArray.cameras.length; i ++ ) {
|
|
|
-
|
|
|
- const camera = cameraArray.cameras[ i ];
|
|
|
-
|
|
|
- _projScreenMatrix$2.multiplyMatrices(
|
|
|
- camera.projectionMatrix,
|
|
|
- camera.matrixWorldInverse
|
|
|
- );
|
|
|
-
|
|
|
- _frustum$1.setFromProjectionMatrix(
|
|
|
- _projScreenMatrix$2,
|
|
|
- camera.coordinateSystem,
|
|
|
- camera.reversedDepth
|
|
|
- );
|
|
|
+ intersectsSphere( sphere ) {
|
|
|
|
|
|
- if ( _frustum$1.intersectsSphere( sphere ) ) {
|
|
|
+ const frustums = this._frustums;
|
|
|
|
|
|
- return true; // Sphere is visible in at least one camera
|
|
|
+ for ( let i = 0; i < this._count; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ if ( frustums[ i ].intersectsSphere( sphere ) ) return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
- return false; // Not visible in any camera
|
|
|
+ return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Returns `true` if the given bounding box is intersecting any frustum
|
|
|
- * from the camera array.
|
|
|
+ * Returns `true` if the given bounding box is intersecting any cached frustum.
|
|
|
+ *
|
|
|
+ * {@link FrustumArray#setFromArrayCamera} must be called once per render before this method.
|
|
|
*
|
|
|
* @param {Box3} box - The bounding box to test.
|
|
|
- * @param {Object} cameraArray - An object with a cameras property containing an array of cameras.
|
|
|
* @return {boolean} Whether the box is visible in any camera.
|
|
|
*/
|
|
|
- intersectsBox( box, cameraArray ) {
|
|
|
-
|
|
|
- if ( ! cameraArray || ! cameraArray.cameras || cameraArray.cameras.length === 0 ) {
|
|
|
-
|
|
|
- return false;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- for ( let i = 0; i < cameraArray.cameras.length; i ++ ) {
|
|
|
-
|
|
|
- const camera = cameraArray.cameras[ i ];
|
|
|
-
|
|
|
- _projScreenMatrix$2.multiplyMatrices(
|
|
|
- camera.projectionMatrix,
|
|
|
- camera.matrixWorldInverse
|
|
|
- );
|
|
|
-
|
|
|
- _frustum$1.setFromProjectionMatrix(
|
|
|
- _projScreenMatrix$2,
|
|
|
- camera.coordinateSystem,
|
|
|
- camera.reversedDepth
|
|
|
- );
|
|
|
+ intersectsBox( box ) {
|
|
|
|
|
|
- if ( _frustum$1.intersectsBox( box ) ) {
|
|
|
+ const frustums = this._frustums;
|
|
|
|
|
|
- return true; // Box is visible in at least one camera
|
|
|
+ for ( let i = 0; i < this._count; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ if ( frustums[ i ].intersectsBox( box ) ) return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
- return false; // Not visible in any camera
|
|
|
+ return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Returns `true` if the given point lies within any frustum
|
|
|
- * from the camera array.
|
|
|
+ * Returns `true` if the given point lies within any cached frustum.
|
|
|
+ *
|
|
|
+ * {@link FrustumArray#setFromArrayCamera} must be called once per render before this method.
|
|
|
*
|
|
|
* @param {Vector3} point - The point to test.
|
|
|
- * @param {Object} cameraArray - An object with a cameras property containing an array of cameras.
|
|
|
* @return {boolean} Whether the point is visible in any camera.
|
|
|
*/
|
|
|
- containsPoint( point, cameraArray ) {
|
|
|
+ containsPoint( point ) {
|
|
|
|
|
|
- if ( ! cameraArray || ! cameraArray.cameras || cameraArray.cameras.length === 0 ) {
|
|
|
+ const frustums = this._frustums;
|
|
|
|
|
|
- return false;
|
|
|
+ for ( let i = 0; i < this._count; i ++ ) {
|
|
|
+
|
|
|
+ if ( frustums[ i ].containsPoint( point ) ) return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
- for ( let i = 0; i < cameraArray.cameras.length; i ++ ) {
|
|
|
+ return false;
|
|
|
|
|
|
- const camera = cameraArray.cameras[ i ];
|
|
|
+ }
|
|
|
|
|
|
- _projScreenMatrix$2.multiplyMatrices(
|
|
|
- camera.projectionMatrix,
|
|
|
- camera.matrixWorldInverse
|
|
|
- );
|
|
|
+ /**
|
|
|
+ * Copies the values of the given frustum array to this instance.
|
|
|
+ *
|
|
|
+ * @param {FrustumArray} frustumArray - The frustum array to copy.
|
|
|
+ * @return {FrustumArray} A reference to this frustum array.
|
|
|
+ */
|
|
|
+ copy( source ) {
|
|
|
|
|
|
- _frustum$1.setFromProjectionMatrix(
|
|
|
- _projScreenMatrix$2,
|
|
|
- camera.coordinateSystem,
|
|
|
- camera.reversedDepth
|
|
|
- );
|
|
|
+ this.coordinateSystem = source.coordinateSystem;
|
|
|
|
|
|
- if ( _frustum$1.containsPoint( point ) ) {
|
|
|
+ const frustums = this._frustums;
|
|
|
+ const sourceFrustums = source._frustums;
|
|
|
|
|
|
- return true; // Point is visible in at least one camera
|
|
|
+ for ( let i = 0; i < source._count; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
+ if ( frustums[ i ] === undefined ) frustums[ i ] = new Frustum();
|
|
|
+
|
|
|
+ frustums[ i ].copy( sourceFrustums[ i ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
- return false; // Not visible in any camera
|
|
|
+ this._count = source._count;
|
|
|
+
|
|
|
+ return this;
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -25946,7 +25941,7 @@ class FrustumArray {
|
|
|
*/
|
|
|
clone() {
|
|
|
|
|
|
- return new FrustumArray();
|
|
|
+ return new FrustumArray().copy( this );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -27492,17 +27487,25 @@ class BatchedMesh extends Mesh {
|
|
|
|
|
|
const frustum = camera.isArrayCamera ? _frustumArray : _frustum;
|
|
|
// prepare the frustum in the local frame
|
|
|
- if ( perObjectFrustumCulled && ! camera.isArrayCamera ) {
|
|
|
+ if ( perObjectFrustumCulled ) {
|
|
|
|
|
|
- _matrix$1
|
|
|
- .multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse )
|
|
|
- .multiply( this.matrixWorld );
|
|
|
+ if ( camera.isArrayCamera ) {
|
|
|
|
|
|
- _frustum.setFromProjectionMatrix(
|
|
|
- _matrix$1,
|
|
|
- camera.coordinateSystem,
|
|
|
- camera.reversedDepth
|
|
|
- );
|
|
|
+ frustum.setFromArrayCamera( camera );
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ _matrix$1
|
|
|
+ .multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse )
|
|
|
+ .multiply( this.matrixWorld );
|
|
|
+
|
|
|
+ frustum.setFromProjectionMatrix(
|
|
|
+ _matrix$1,
|
|
|
+ camera.coordinateSystem,
|
|
|
+ camera.reversedDepth
|
|
|
+ );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -27528,7 +27531,7 @@ class BatchedMesh extends Mesh {
|
|
|
let culled = false;
|
|
|
if ( perObjectFrustumCulled ) {
|
|
|
|
|
|
- culled = ! frustum.intersectsSphere( _sphere$2, camera );
|
|
|
+ culled = ! frustum.intersectsSphere( _sphere$2 );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -27585,7 +27588,7 @@ class BatchedMesh extends Mesh {
|
|
|
// get the bounds in world space
|
|
|
this.getMatrixAt( i, _matrix$1 );
|
|
|
this.getBoundingSphereAt( geometryId, _sphere$2 ).applyMatrix4( _matrix$1 );
|
|
|
- culled = ! frustum.intersectsSphere( _sphere$2, camera );
|
|
|
+ culled = ! frustum.intersectsSphere( _sphere$2 );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -29374,9 +29377,6 @@ class CubeDepthTexture extends DepthTexture {
|
|
|
* This may be a texture from a protected media stream, device camera feed,
|
|
|
* or other data feeds like a depth sensor.
|
|
|
*
|
|
|
- * Note that this class is only supported in {@link WebGLRenderer}, and in
|
|
|
- * the {@link WebGPURenderer} WebGPU backend.
|
|
|
- *
|
|
|
* @augments Texture
|
|
|
*/
|
|
|
class ExternalTexture extends Texture {
|
|
|
@@ -67058,9 +67058,10 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
|
|
|
|
|
|
gl.bindAttribLocation( program, 0, parameters.index0AttributeName );
|
|
|
|
|
|
- } else if ( parameters.morphTargets === true ) {
|
|
|
+ } else if ( parameters.hasPositionAttribute === true ) {
|
|
|
+
|
|
|
+ // below avoids the "Attribute 0 is disabled" performance penalty
|
|
|
|
|
|
- // programs with morphTargets displace position out of attribute 0
|
|
|
gl.bindAttribLocation( program, 0, 'position' );
|
|
|
|
|
|
}
|
|
|
@@ -67250,13 +67251,7 @@ class WebGLShaderCache {
|
|
|
|
|
|
}
|
|
|
|
|
|
- update( material ) {
|
|
|
-
|
|
|
- const vertexShader = material.vertexShader;
|
|
|
- const fragmentShader = material.fragmentShader;
|
|
|
-
|
|
|
- const vertexShaderStage = this._getShaderStage( vertexShader );
|
|
|
- const fragmentShaderStage = this._getShaderStage( fragmentShader );
|
|
|
+ update( material, vertexShaderStage, fragmentShaderStage ) {
|
|
|
|
|
|
const materialShaders = this._getShaderCacheForMaterial( material );
|
|
|
|
|
|
@@ -67296,15 +67291,15 @@ class WebGLShaderCache {
|
|
|
|
|
|
}
|
|
|
|
|
|
- getVertexShaderID( material ) {
|
|
|
+ getVertexShaderStage( material ) {
|
|
|
|
|
|
- return this._getShaderStage( material.vertexShader ).id;
|
|
|
+ return this._getShaderStage( material.vertexShader );
|
|
|
|
|
|
}
|
|
|
|
|
|
- getFragmentShaderID( material ) {
|
|
|
+ getFragmentShaderStage( material ) {
|
|
|
|
|
|
- return this._getShaderStage( material.fragmentShader ).id;
|
|
|
+ return this._getShaderStage( material.fragmentShader );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -67463,10 +67458,13 @@ function WebGLPrograms( renderer, environments, extensions, capabilities, bindin
|
|
|
vertexShader = material.vertexShader;
|
|
|
fragmentShader = material.fragmentShader;
|
|
|
|
|
|
- _customShaders.update( material );
|
|
|
+ const vertexShaderStage = _customShaders.getVertexShaderStage( material );
|
|
|
+ const fragmentShaderStage = _customShaders.getFragmentShaderStage( material );
|
|
|
|
|
|
- customVertexShaderID = _customShaders.getVertexShaderID( material );
|
|
|
- customFragmentShaderID = _customShaders.getFragmentShaderID( material );
|
|
|
+ _customShaders.update( material, vertexShaderStage, fragmentShaderStage );
|
|
|
+
|
|
|
+ customVertexShaderID = vertexShaderStage.id;
|
|
|
+ customFragmentShaderID = fragmentShaderStage.id;
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -67679,6 +67677,8 @@ function WebGLPrograms( renderer, environments, extensions, capabilities, bindin
|
|
|
|
|
|
skinning: object.isSkinnedMesh === true,
|
|
|
|
|
|
+ hasPositionAttribute: geometry.attributes.position !== undefined,
|
|
|
+
|
|
|
morphTargets: geometry.morphAttributes.position !== undefined,
|
|
|
morphNormals: geometry.morphAttributes.normal !== undefined,
|
|
|
morphColors: geometry.morphAttributes.color !== undefined,
|
|
|
@@ -67940,6 +67940,8 @@ function WebGLPrograms( renderer, environments, extensions, capabilities, bindin
|
|
|
_programLayers.enable( 21 );
|
|
|
if ( parameters.numLightProbeGrids > 0 )
|
|
|
_programLayers.enable( 22 );
|
|
|
+ if ( parameters.hasPositionAttribute )
|
|
|
+ _programLayers.enable( 23 );
|
|
|
|
|
|
array.push( _programLayers.mask );
|
|
|
|
|
|
@@ -77154,7 +77156,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.determinantAffine() < 0 );
|
|
|
|
|
|
const program = setProgram( camera, scene, geometry, material, object );
|
|
|
|