diff --git a/examples/webgpu_clipping.html b/examples/webgpu_clipping.html
index c037d5c7838606..184ac22a597886 100644
--- a/examples/webgpu_clipping.html
+++ b/examples/webgpu_clipping.html
@@ -74,11 +74,23 @@
dirLight.shadow.mapSize.height = 1024;
scene.add( dirLight );
- // ***** Clipping planes: *****
+ // Clipping planes
- const localPlane = new THREE.Plane( new THREE.Vector3( 0, - 1, 0 ), 0.8 );
- const localPlane2 = new THREE.Plane( new THREE.Vector3( 0, 0, - 1 ), 0.1 );
const globalPlane = new THREE.Plane( new THREE.Vector3( - 1, 0, 0 ), 0.1 );
+ const localPlane1 = new THREE.Plane( new THREE.Vector3( 0, - 1, 0 ), 0.8 );
+ const localPlane2 = new THREE.Plane( new THREE.Vector3( 0, 0, - 1 ), 0.1 );
+
+ // Clipping Groups
+
+ const globalClippingGroup = new THREE.ClippingGroup();
+ globalClippingGroup.clippingPlanes = [ globalPlane ];
+
+ const knotClippingGroup = new THREE.ClippingGroup();
+ knotClippingGroup.clippingPlanes = [ localPlane1, localPlane2 ];
+ knotClippingGroup.clipIntersection = true;
+
+ scene.add( globalClippingGroup );
+ globalClippingGroup.add( knotClippingGroup );
// Geometry
@@ -88,27 +100,23 @@
side: THREE.DoubleSide,
// ***** Clipping setup (material): *****
- clippingPlanes: [ localPlane, localPlane2 ],
- clipShadows: true,
- alphaToCoverage: true,
- clipIntersection: true
-
+ alphaToCoverage: true
} );
const geometry = new THREE.TorusKnotGeometry( 0.4, 0.08, 95, 20 );
object = new THREE.Mesh( geometry, material );
object.castShadow = true;
- scene.add( object );
+ knotClippingGroup.add( object );
const ground = new THREE.Mesh(
new THREE.PlaneGeometry( 9, 9, 1, 1 ),
- new THREE.MeshPhongNodeMaterial( { color: 0xa0adaf, shininess: 150 } )
+ new THREE.MeshPhongNodeMaterial( { color: 0xa0adaf, shininess: 150, alphaToCoverage: true } )
);
ground.rotation.x = - Math.PI / 2; // rotates X/Y to X/Z
ground.receiveShadow = true;
- scene.add( ground );
+ globalClippingGroup.add( ground );
// Stats
@@ -125,14 +133,8 @@
window.addEventListener( 'resize', onWindowResize );
document.body.appendChild( renderer.domElement );
- // ***** Clipping setup (renderer): *****
- const globalPlanes = [ globalPlane ];
- const Empty = Object.freeze( [] );
-
- renderer.clippingPlanes = Empty; // GUI sets it to globalPlanes
- renderer.localClippingEnabled = true;
-
// Controls
+
const controls = new OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 1, 0 );
controls.update();
@@ -143,67 +145,67 @@
props = {
alphaToCoverage: true,
},
- folderLocal = gui.addFolder( 'Local Clipping' ),
- propsLocal = {
+ folderKnot = gui.addFolder( 'Knot Clipping Group' ),
+ propsKnot = {
get 'Enabled'() {
- return renderer.localClippingEnabled;
+ return knotClippingGroup.enabled;
},
set 'Enabled'( v ) {
- renderer.localClippingEnabled = v;
+ knotClippingGroup.enabled = v;
},
get 'Shadows'() {
- return material.clipShadows;
+ return knotClippingGroup.clipShadows;
},
set 'Shadows'( v ) {
- material.clipShadows = v;
+ knotClippingGroup.clipShadows = v;
},
get 'Intersection'() {
- return material.clipIntersection;
+ return knotClippingGroup.clipIntersection;
},
set 'Intersection'( v ) {
- material.clipIntersection = v;
+ knotClippingGroup.clipIntersection = v;
},
get 'Plane'() {
- return localPlane.constant;
+ return localPlane1.constant;
},
set 'Plane'( v ) {
- localPlane.constant = v;
+ localPlane1.constant = v;
}
},
- folderGlobal = gui.addFolder( 'Global Clipping' ),
+ folderGlobal = gui.addFolder( 'Global Clipping Group' ),
propsGlobal = {
get 'Enabled'() {
- return renderer.clippingPlanes !== Empty;
+ return globalClippingGroup.enabled;
},
set 'Enabled'( v ) {
- renderer.clippingPlanes = v ? globalPlanes : Empty;
+ globalClippingGroup.enabled = v;
},
@@ -230,10 +232,10 @@
} );
- folderLocal.add( propsLocal, 'Enabled' );
- folderLocal.add( propsLocal, 'Shadows' );
- folderLocal.add( propsLocal, 'Intersection' );
- folderLocal.add( propsLocal, 'Plane', 0.3, 1.25 );
+ folderKnot.add( propsKnot, 'Enabled' );
+ folderKnot.add( propsKnot, 'Shadows' );
+ folderKnot.add( propsKnot, 'Intersection' );
+ folderKnot.add( propsKnot, 'Plane', 0.3, 1.25 );
folderGlobal.add( propsGlobal, 'Enabled' );
folderGlobal.add( propsGlobal, 'Plane', - 0.4, 3 );
diff --git a/src/Three.WebGPU.js b/src/Three.WebGPU.js
index 8613be85887b03..eb77205de000ed 100644
--- a/src/Three.WebGPU.js
+++ b/src/Three.WebGPU.js
@@ -164,6 +164,7 @@ export * from './Three.Legacy.js';
export * from './materials/nodes/NodeMaterials.js';
export { default as WebGPURenderer } from './renderers/webgpu/WebGPURenderer.js';
+export { default as ClippingGroup } from './renderers/common/ClippingGroup.js';
export { default as Lighting } from './renderers/common/Lighting.js';
export { default as BundleGroup } from './renderers/common/BundleGroup.js';
export { default as QuadMesh } from './renderers/common/QuadMesh.js';
diff --git a/src/materials/nodes/NodeMaterial.js b/src/materials/nodes/NodeMaterial.js
index 5179d5864f1c85..fdec68f77f26df 100644
--- a/src/materials/nodes/NodeMaterial.js
+++ b/src/materials/nodes/NodeMaterial.js
@@ -216,11 +216,11 @@ class NodeMaterial extends Material {
if ( builder.clippingContext === null ) return null;
- const { globalClippingCount, localClippingCount } = builder.clippingContext;
+ const { unionPlanes, intersectionPlanes } = builder.clippingContext;
let result = null;
- if ( globalClippingCount || localClippingCount ) {
+ if ( unionPlanes.length > 0 || intersectionPlanes.length > 0 ) {
const samples = builder.renderer.samples;
diff --git a/src/nodes/accessors/ClippingNode.js b/src/nodes/accessors/ClippingNode.js
index 96e00141815af3..28bc7ab1c63a8c 100644
--- a/src/nodes/accessors/ClippingNode.js
+++ b/src/nodes/accessors/ClippingNode.js
@@ -1,9 +1,8 @@
import Node from '../core/Node.js';
-import { nodeObject } from '../tsl/TSLBase.js';
+import { nodeObject, Fn, bool, float } from '../tsl/TSLBase.js';
import { positionView } from './Position.js';
-import { diffuseColor, property } from '../core/PropertyNode.js';
-import { Fn } from '../tsl/TSLBase.js';
+import { diffuseColor } from '../core/PropertyNode.js';
import { Loop } from '../utils/LoopNode.js';
import { smoothstep } from '../math/MathNode.js';
import { uniformArray } from './UniformArrayNode.js';
@@ -29,69 +28,72 @@ class ClippingNode extends Node {
super.setup( builder );
const clippingContext = builder.clippingContext;
- const { localClipIntersection, localClippingCount, globalClippingCount } = clippingContext;
+ const { intersectionPlanes, unionPlanes } = clippingContext;
- const numClippingPlanes = globalClippingCount + localClippingCount;
- const numUnionClippingPlanes = localClipIntersection ? numClippingPlanes - localClippingCount : numClippingPlanes;
if ( this.scope === ClippingNode.ALPHA_TO_COVERAGE ) {
- return this.setupAlphaToCoverage( clippingContext.planes, numClippingPlanes, numUnionClippingPlanes );
+ return this.setupAlphaToCoverage( intersectionPlanes, unionPlanes );
} else {
- return this.setupDefault( clippingContext.planes, numClippingPlanes, numUnionClippingPlanes );
+ return this.setupDefault( intersectionPlanes, unionPlanes );
}
}
- setupAlphaToCoverage( planes, numClippingPlanes, numUnionClippingPlanes ) {
+ setupAlphaToCoverage( intersectionPlanes, unionPlanes ) {
return Fn( () => {
- const clippingPlanes = uniformArray( planes );
+ const distanceToPlane = float().toVar( 'distanceToPlane' );
+ const distanceGradient = float().toVar( 'distanceToGradient' );
- const distanceToPlane = property( 'float', 'distanceToPlane' );
- const distanceGradient = property( 'float', 'distanceToGradient' );
+ const clipOpacity = float( 1 ).toVar( 'clipOpacity' );
- const clipOpacity = property( 'float', 'clipOpacity' );
+ const numUnionPlanes = unionPlanes.length;
- clipOpacity.assign( 1 );
+ if ( numUnionPlanes > 0 ) {
- let plane;
+ const clippingPlanes = uniformArray( unionPlanes );
- Loop( numUnionClippingPlanes, ( { i } ) => {
+ let plane;
- plane = clippingPlanes.element( i );
+ Loop( numUnionPlanes, ( { i } ) => {
- distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
- distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );
+ plane = clippingPlanes.element( i );
+
+ distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
+ distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );
- clipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ) );
+ clipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ) );
- clipOpacity.equal( 0.0 ).discard();
+ } );
+
+ }
- } );
+ const numIntersectionPlanes = intersectionPlanes.length;
- if ( numUnionClippingPlanes < numClippingPlanes ) {
+ if ( numIntersectionPlanes > 0 ) {
- const unionClipOpacity = property( 'float', 'unionclipOpacity' );
+ const clippingPlanes = uniformArray( intersectionPlanes );
+ const intersectionClipOpacity = float( 1 ).toVar( 'intersectionClipOpacity' );
- unionClipOpacity.assign( 1 );
+ let plane;
- Loop( { start: numUnionClippingPlanes, end: numClippingPlanes }, ( { i } ) => {
+ Loop( numIntersectionPlanes, ( { i } ) => {
plane = clippingPlanes.element( i );
distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );
- unionClipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ).oneMinus() );
+ intersectionClipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ).oneMinus() );
} );
- clipOpacity.mulAssign( unionClipOpacity.oneMinus() );
+ clipOpacity.mulAssign( intersectionClipOpacity.oneMinus() );
}
@@ -103,28 +105,37 @@ class ClippingNode extends Node {
}
- setupDefault( planes, numClippingPlanes, numUnionClippingPlanes ) {
+ setupDefault( intersectionPlanes, unionPlanes ) {
return Fn( () => {
- const clippingPlanes = uniformArray( planes );
+ const numUnionPlanes = unionPlanes.length;
- let plane;
+ if ( numUnionPlanes > 0 ) {
- Loop( numUnionClippingPlanes, ( { i } ) => {
+ const clippingPlanes = uniformArray( unionPlanes );
- plane = clippingPlanes.element( i );
- positionView.dot( plane.xyz ).greaterThan( plane.w ).discard();
+ let plane;
+
+ Loop( numUnionPlanes, ( { i } ) => {
+
+ plane = clippingPlanes.element( i );
+ positionView.dot( plane.xyz ).greaterThan( plane.w ).discard();
+
+ } );
+
+ }
- } );
+ const numIntersectionPlanes = intersectionPlanes.length;
- if ( numUnionClippingPlanes < numClippingPlanes ) {
+ if ( numIntersectionPlanes > 0 ) {
- const clipped = property( 'bool', 'clipped' );
+ const clippingPlanes = uniformArray( intersectionPlanes );
+ const clipped = bool( true ).toVar( 'clipped' );
- clipped.assign( true );
+ let plane;
- Loop( { start: numUnionClippingPlanes, end: numClippingPlanes }, ( { i } ) => {
+ Loop( numIntersectionPlanes, ( { i } ) => {
plane = clippingPlanes.element( i );
clipped.assign( positionView.dot( plane.xyz ).greaterThan( plane.w ).and( clipped ) );
diff --git a/src/nodes/display/ToonOutlinePassNode.js b/src/nodes/display/ToonOutlinePassNode.js
index 9d497acab33609..00586208f4d8fd 100644
--- a/src/nodes/display/ToonOutlinePassNode.js
+++ b/src/nodes/display/ToonOutlinePassNode.js
@@ -34,7 +34,7 @@ class ToonOutlinePassNode extends PassNode {
const currentRenderObjectFunction = renderer.getRenderObjectFunction();
- renderer.setRenderObjectFunction( ( object, scene, camera, geometry, material, group, lightsNode ) => {
+ renderer.setRenderObjectFunction( ( object, scene, camera, geometry, material, group, lightsNode, clippingContext ) => {
// only render outline for supported materials
@@ -43,7 +43,7 @@ class ToonOutlinePassNode extends PassNode {
if ( material.wireframe === false ) {
const outlineMaterial = this._getOutlineMaterial( material );
- renderer.renderObject( object, scene, camera, geometry, outlineMaterial, group, lightsNode );
+ renderer.renderObject( object, scene, camera, geometry, outlineMaterial, group, lightsNode, clippingContext );
}
@@ -51,7 +51,7 @@ class ToonOutlinePassNode extends PassNode {
// default
- renderer.renderObject( object, scene, camera, geometry, material, group, lightsNode );
+ renderer.renderObject( object, scene, camera, geometry, material, group, lightsNode, clippingContext );
} );
diff --git a/src/renderers/common/Background.js b/src/renderers/common/Background.js
index a47ddb3b370187..1a1ed200538f31 100644
--- a/src/renderers/common/Background.js
+++ b/src/renderers/common/Background.js
@@ -99,7 +99,7 @@ class Background extends DataMap {
}
- renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null );
+ renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null, null );
} else {
diff --git a/src/renderers/common/ClippingContext.js b/src/renderers/common/ClippingContext.js
index a4624ff044ba98..07243d56984b62 100644
--- a/src/renderers/common/ClippingContext.js
+++ b/src/renderers/common/ClippingContext.js
@@ -1,40 +1,53 @@
import { Matrix3 } from '../../math/Matrix3.js';
import { Plane } from '../../math/Plane.js';
import { Vector4 } from '../../math/Vector4.js';
-import { hash } from '../../nodes/core/NodeUtils.js';
const _plane = /*@__PURE__*/ new Plane();
class ClippingContext {
- constructor() {
+ constructor( parentContext = null ) {
this.version = 0;
- this.globalClippingCount = 0;
+ this.clipIntersection = null;
+ this.cacheKey = '';
- this.localClippingCount = 0;
- this.localClippingEnabled = false;
- this.localClipIntersection = false;
- this.planes = [];
+ if ( parentContext === null ) {
- this.parentVersion = 0;
- this.viewNormalMatrix = new Matrix3();
- this.cacheKey = 0;
+ this.intersectionPlanes = [];
+ this.unionPlanes = [];
+
+ this.viewNormalMatrix = new Matrix3();
+ this.clippingGroupContexts = new WeakMap();
+
+ this.shadowPass = false;
+
+ } else {
+
+ this.viewNormalMatrix = parentContext.viewNormalMatrix;
+ this.clippingGroupContexts = parentContext.clippingGroupContexts;
+
+ this.shadowPass = parentContext.shadowPass;
+
+ this.viewMatrix = parentContext.viewMatrix;
+
+ }
+
+ this.parentVersion = null;
}
- projectPlanes( source, offset ) {
+ projectPlanes( source, destination, offset ) {
const l = source.length;
- const planes = this.planes;
for ( let i = 0; i < l; i ++ ) {
_plane.copy( source[ i ] ).applyMatrix4( this.viewMatrix, this.viewNormalMatrix );
- const v = planes[ offset + i ];
+ const v = destination[ offset + i ];
const normal = _plane.normal;
v.x = - normal.x;
@@ -46,129 +59,102 @@ class ClippingContext {
}
- updateGlobal( renderer, camera ) {
+ updateGlobal( scene, camera ) {
- const rendererClippingPlanes = renderer.clippingPlanes;
+ this.shadowPass = ( scene.overrideMaterial !== null && scene.overrideMaterial.isShadowNodeMaterial );
this.viewMatrix = camera.matrixWorldInverse;
this.viewNormalMatrix.getNormalMatrix( this.viewMatrix );
- let update = false;
-
- if ( Array.isArray( rendererClippingPlanes ) && rendererClippingPlanes.length !== 0 ) {
+ }
- const l = rendererClippingPlanes.length;
+ update( parentContext, clippingGroup ) {
- if ( l !== this.globalClippingCount ) {
+ let update = false;
- const planes = [];
+ if ( parentContext.version !== this.parentVersion ) {
- for ( let i = 0; i < l; i ++ ) {
+ this.intersectionPlanes = Array.from( parentContext.intersectionPlanes );
+ this.unionPlanes = Array.from( parentContext.unionPlanes );
+ this.parentVersion = parentContext.version;
- planes.push( new Vector4() );
+ }
- }
+ if ( this.clipIntersection !== clippingGroup.clipIntersection ) {
- this.globalClippingCount = l;
- this.planes = planes;
+ this.clipIntersection = clippingGroup.clipIntersection;
- update = true;
+ if ( this.clipIntersection ) {
- }
+ this.unionPlanes.length = parentContext.unionPlanes.length;
- this.projectPlanes( rendererClippingPlanes, 0 );
+ } else {
- } else if ( this.globalClippingCount !== 0 ) {
+ this.intersectionPlanes.length = parentContext.intersectionPlanes.length;
- this.globalClippingCount = 0;
- this.planes = [];
- update = true;
+ }
}
- if ( renderer.localClippingEnabled !== this.localClippingEnabled ) {
+ const srcClippingPlanes = clippingGroup.clippingPlanes;
+ const l = srcClippingPlanes.length;
- this.localClippingEnabled = renderer.localClippingEnabled;
- update = true;
+ let dstClippingPlanes;
+ let offset;
- }
+ if ( this.clipIntersection ) {
- if ( update ) {
+ dstClippingPlanes = this.intersectionPlanes;
+ offset = parentContext.intersectionPlanes.length;
- this.version ++;
- this.cacheKey = hash( this.globalClippingCount, this.localClippingEnabled === true ? 1 : 0 );
+ } else {
+
+ dstClippingPlanes = this.unionPlanes;
+ offset = parentContext.unionPlanes.length;
}
- }
+ if ( dstClippingPlanes.length !== offset + l ) {
- update( parent, material ) {
+ dstClippingPlanes.length = offset + l;
- let update = false;
+ for ( let i = 0; i < l; i ++ ) {
- if ( this !== parent && parent.version !== this.parentVersion ) {
+ dstClippingPlanes[ offset + i ] = new Vector4();
- this.globalClippingCount = material.isShadowNodeMaterial ? 0 : parent.globalClippingCount;
- this.localClippingEnabled = parent.localClippingEnabled;
- this.planes = Array.from( parent.planes );
- this.parentVersion = parent.version;
- this.viewMatrix = parent.viewMatrix;
- this.viewNormalMatrix = parent.viewNormalMatrix;
+ }
update = true;
}
- if ( this.localClippingEnabled ) {
-
- const localClippingPlanes = material.clippingPlanes;
-
- if ( ( Array.isArray( localClippingPlanes ) && localClippingPlanes.length !== 0 ) ) {
-
- const l = localClippingPlanes.length;
- const planes = this.planes;
- const offset = this.globalClippingCount;
-
- if ( update || l !== this.localClippingCount ) {
-
- planes.length = offset + l;
-
- for ( let i = 0; i < l; i ++ ) {
-
- planes[ offset + i ] = new Vector4();
-
- }
+ this.projectPlanes( srcClippingPlanes, dstClippingPlanes, offset );
- this.localClippingCount = l;
- update = true;
-
- }
+ if ( update ) {
- this.projectPlanes( localClippingPlanes, offset );
+ this.version ++;
+ this.cacheKey = `${ this.intersectionPlanes.length }:${ this.unionPlanes.length }`;
+ }
- } else if ( this.localClippingCount !== 0 ) {
+ }
- this.localClippingCount = 0;
- update = true;
+ getGroupContext( clippingGroup ) {
- }
+ if ( this.shadowPass && ! clippingGroup.clipShadows ) return this;
- if ( this.localClipIntersection !== material.clipIntersection ) {
+ let context = this.clippingGroupContexts.get( clippingGroup );
- this.localClipIntersection = material.clipIntersection;
- update = true;
+ if ( context === undefined ) {
- }
+ context = new ClippingContext( this );
+ this.clippingGroupContexts.set( clippingGroup, context );
}
- if ( update ) {
+ context.update( this, clippingGroup );
- this.version += parent.version;
- this.cacheKey = hash( parent.cacheKey, this.localClippingCount, this.localClipIntersection === true ? 1 : 0 );
-
- }
+ return context;
}
diff --git a/src/renderers/common/ClippingGroup.js b/src/renderers/common/ClippingGroup.js
new file mode 100644
index 00000000000000..a54dc1195f07bf
--- /dev/null
+++ b/src/renderers/common/ClippingGroup.js
@@ -0,0 +1,19 @@
+import { Group } from '../../objects/Group.js';
+
+class ClippingGroup extends Group {
+
+ constructor() {
+
+ super();
+
+ this.isClippingGroup = true;
+ this.clippingPlanes = [];
+ this.enabled = true;
+ this.clipIntersection = false;
+ this.clipShadows = false;
+
+ }
+
+}
+
+export default ClippingGroup;
diff --git a/src/renderers/common/RenderList.js b/src/renderers/common/RenderList.js
index 76614893f55ac0..d819ee90bbb4c2 100644
--- a/src/renderers/common/RenderList.js
+++ b/src/renderers/common/RenderList.js
@@ -95,7 +95,7 @@ class RenderList {
}
- getNextRenderItem( object, geometry, material, groupOrder, z, group ) {
+ getNextRenderItem( object, geometry, material, groupOrder, z, group, clippingContext ) {
let renderItem = this.renderItems[ this.renderItemsIndex ];
@@ -109,7 +109,8 @@ class RenderList {
groupOrder: groupOrder,
renderOrder: object.renderOrder,
z: z,
- group: group
+ group: group,
+ clippingContext: clippingContext
};
this.renderItems[ this.renderItemsIndex ] = renderItem;
@@ -124,6 +125,7 @@ class RenderList {
renderItem.renderOrder = object.renderOrder;
renderItem.z = z;
renderItem.group = group;
+ renderItem.clippingContext = clippingContext;
}
@@ -133,9 +135,9 @@ class RenderList {
}
- push( object, geometry, material, groupOrder, z, group ) {
+ push( object, geometry, material, groupOrder, z, group, clippingContext ) {
- const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group );
+ const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group, clippingContext );
if ( object.occlusionTest === true ) this.occlusionQueryCount ++;
@@ -153,9 +155,9 @@ class RenderList {
}
- unshift( object, geometry, material, groupOrder, z, group ) {
+ unshift( object, geometry, material, groupOrder, z, group, clippingContext ) {
- const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group );
+ const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group, clippingContext );
if ( material.transparent === true || material.transmission > 0 ) {
@@ -213,6 +215,7 @@ class RenderList {
renderItem.renderOrder = null;
renderItem.z = null;
renderItem.group = null;
+ renderItem.clippingContext = null;
}
diff --git a/src/renderers/common/RenderObject.js b/src/renderers/common/RenderObject.js
index 987f59181039e3..2f25db1c9a5ca8 100644
--- a/src/renderers/common/RenderObject.js
+++ b/src/renderers/common/RenderObject.js
@@ -1,5 +1,4 @@
import { hashString } from '../../nodes/core/NodeUtils.js';
-import ClippingContext from './ClippingContext.js';
let _id = 0;
@@ -39,7 +38,7 @@ function getKeys( obj ) {
export default class RenderObject {
- constructor( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext ) {
+ constructor( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext, clippingContext ) {
this._nodes = nodes;
this._geometries = geometries;
@@ -66,9 +65,8 @@ export default class RenderObject {
this.bundle = null;
- this.updateClipping( renderContext.clippingContext );
-
- this.clippingContextVersion = this.clippingContext.version;
+ this.clippingContext = clippingContext;
+ this.clippingContextCacheKey = clippingContext !== null ? clippingContext.cacheKey : '';
this.initialNodesCacheKey = this.getDynamicCacheKey();
this.initialCacheKey = this.getCacheKey();
@@ -93,34 +91,15 @@ export default class RenderObject {
updateClipping( parent ) {
- const material = this.material;
-
- let clippingContext = this.clippingContext;
-
- if ( Array.isArray( material.clippingPlanes ) ) {
-
- if ( clippingContext === parent || ! clippingContext ) {
-
- clippingContext = new ClippingContext();
- this.clippingContext = clippingContext;
-
- }
-
- clippingContext.update( parent, material );
-
- } else if ( this.clippingContext !== parent ) {
-
- this.clippingContext = parent;
-
- }
+ this.clippingContext = parent;
}
get clippingNeedsUpdate() {
- if ( this.clippingContext.version === this.clippingContextVersion ) return false;
+ if ( this.clippingContext === null || this.clippingContext.cacheKey === this.clippingContextCacheKey ) return false;
- this.clippingContextVersion = this.clippingContext.version;
+ this.clippingContextCacheKey = this.clippingContext.cacheKey;
return true;
@@ -347,7 +326,7 @@ export default class RenderObject {
}
- cacheKey += this.clippingContext.cacheKey + ',';
+ cacheKey += this.clippingContextCacheKey + ',';
if ( object.geometry ) {
diff --git a/src/renderers/common/RenderObjects.js b/src/renderers/common/RenderObjects.js
index 432c82375171c5..26dffcece2979a 100644
--- a/src/renderers/common/RenderObjects.js
+++ b/src/renderers/common/RenderObjects.js
@@ -18,7 +18,7 @@ class RenderObjects {
}
- get( object, material, scene, camera, lightsNode, renderContext, passId ) {
+ get( object, material, scene, camera, lightsNode, renderContext, clippingContext, passId ) {
const chainMap = this.getChainMap( passId );
@@ -32,13 +32,13 @@ class RenderObjects {
if ( renderObject === undefined ) {
- renderObject = this.createRenderObject( this.nodes, this.geometries, this.renderer, object, material, scene, camera, lightsNode, renderContext, passId );
+ renderObject = this.createRenderObject( this.nodes, this.geometries, this.renderer, object, material, scene, camera, lightsNode, renderContext, clippingContext, passId );
chainMap.set( chainArray, renderObject );
} else {
- renderObject.updateClipping( renderContext.clippingContext );
+ renderObject.updateClipping( clippingContext );
if ( renderObject.version !== material.version || renderObject.needsUpdate ) {
@@ -46,7 +46,7 @@ class RenderObjects {
renderObject.dispose();
- renderObject = this.get( object, material, scene, camera, lightsNode, renderContext, passId );
+ renderObject = this.get( object, material, scene, camera, lightsNode, renderContext, clippingContext, passId );
} else {
@@ -74,11 +74,11 @@ class RenderObjects {
}
- createRenderObject( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext, passId ) {
+ createRenderObject( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext, clippingContext, passId ) {
const chainMap = this.getChainMap( passId );
- const renderObject = new RenderObject( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext );
+ const renderObject = new RenderObject( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext, clippingContext );
renderObject.onDispose = () => {
diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js
index 87b7a5d77b2cb5..2a700e1bb6fccd 100644
--- a/src/renderers/common/Renderer.js
+++ b/src/renderers/common/Renderer.js
@@ -78,8 +78,6 @@ class Renderer {
this.depth = depth;
this.stencil = stencil;
- this.clippingPlanes = [];
-
this.info = new Info();
this.nodes = {
@@ -312,7 +310,7 @@ class Renderer {
renderContext.stencil = this.stencil;
if ( ! renderContext.clippingContext ) renderContext.clippingContext = new ClippingContext();
- renderContext.clippingContext.updateGlobal( this, camera );
+ renderContext.clippingContext.updateGlobal( sceneRef, camera );
//
@@ -323,7 +321,7 @@ class Renderer {
const renderList = this._renderLists.get( scene, camera );
renderList.begin();
- this._projectObject( scene, camera, 0, renderList );
+ this._projectObject( scene, camera, 0, renderList, renderContext.clippingContext );
// include lights from target scene
if ( targetScene !== scene ) {
@@ -678,7 +676,7 @@ class Renderer {
renderContext.scissorValue.height >>= activeMipmapLevel;
if ( ! renderContext.clippingContext ) renderContext.clippingContext = new ClippingContext();
- renderContext.clippingContext.updateGlobal( this, camera );
+ renderContext.clippingContext.updateGlobal( sceneRef, camera );
//
@@ -692,7 +690,7 @@ class Renderer {
const renderList = this._renderLists.get( scene, camera );
renderList.begin();
- this._projectObject( scene, camera, 0, renderList );
+ this._projectObject( scene, camera, 0, renderList, renderContext.clippingContext );
renderList.finish();
@@ -1381,7 +1379,7 @@ class Renderer {
}
- _projectObject( object, camera, groupOrder, renderList ) {
+ _projectObject( object, camera, groupOrder, renderList, clippingContext ) {
if ( object.visible === false ) return;
@@ -1393,6 +1391,8 @@ class Renderer {
groupOrder = object.renderOrder;
+ if ( object.isClippingGroup && object.enabled ) clippingContext = clippingContext.getGroupContext( object );
+
} else if ( object.isLOD ) {
if ( object.autoUpdate === true ) object.update( camera );
@@ -1415,7 +1415,7 @@ class Renderer {
if ( material.visible ) {
- renderList.push( object, geometry, material, groupOrder, _vector4.z, null );
+ renderList.push( object, geometry, material, groupOrder, _vector4.z, null, clippingContext );
}
@@ -1453,7 +1453,7 @@ class Renderer {
if ( groupMaterial && groupMaterial.visible ) {
- renderList.push( object, geometry, groupMaterial, groupOrder, _vector4.z, group );
+ renderList.push( object, geometry, groupMaterial, groupOrder, _vector4.z, group, clippingContext );
}
@@ -1461,7 +1461,7 @@ class Renderer {
} else if ( material.visible ) {
- renderList.push( object, geometry, material, groupOrder, _vector4.z, null );
+ renderList.push( object, geometry, material, groupOrder, _vector4.z, null, clippingContext );
}
@@ -1494,7 +1494,7 @@ class Renderer {
for ( let i = 0, l = children.length; i < l; i ++ ) {
- this._projectObject( children[ i ], camera, groupOrder, renderList );
+ this._projectObject( children[ i ], camera, groupOrder, renderList, clippingContext );
}
@@ -1561,7 +1561,7 @@ class Renderer {
// @TODO: Add support for multiple materials per object. This will require to extract
// the material from the renderItem object and pass it with its group data to renderObject().
- const { object, geometry, material, group } = renderItem;
+ const { object, geometry, material, group, clippingContext } = renderItem;
if ( camera.isArrayCamera ) {
@@ -1584,7 +1584,7 @@ class Renderer {
this.backend.updateViewport( this._currentRenderContext );
- this._currentRenderObjectFunction( object, scene, camera2, geometry, material, group, lightsNode, passId );
+ this._currentRenderObjectFunction( object, scene, camera2, geometry, material, group, lightsNode, clippingContext, passId );
}
@@ -1592,7 +1592,7 @@ class Renderer {
} else {
- this._currentRenderObjectFunction( object, scene, camera, geometry, material, group, lightsNode, passId );
+ this._currentRenderObjectFunction( object, scene, camera, geometry, material, group, lightsNode, clippingContext, passId );
}
@@ -1600,7 +1600,7 @@ class Renderer {
}
- renderObject( object, scene, camera, geometry, material, group, lightsNode, passId = null ) {
+ renderObject( object, scene, camera, geometry, material, group, lightsNode, clippingContext = null, passId = null ) {
let overridePositionNode;
let overrideFragmentNode;
@@ -1642,32 +1642,6 @@ class Renderer {
}
- if ( this.localClippingEnabled ) {
-
- if ( material.clipShadows ) {
-
- if ( overrideMaterial.clippingPlanes !== material.clippingPlanes ) {
-
- overrideMaterial.clippingPlanes = material.clippingPlanes;
- overrideMaterial.needsUpdate = true;
-
- }
-
- if ( overrideMaterial.clipIntersection !== material.clipIntersection ) {
-
- overrideMaterial.clipIntersection = material.clipIntersection;
-
- }
-
- } else if ( Array.isArray( overrideMaterial.clippingPlanes ) ) {
-
- overrideMaterial.clippingPlanes = null;
- overrideMaterial.needsUpdate = true;
-
- }
-
- }
-
}
material = overrideMaterial;
@@ -1679,16 +1653,16 @@ class Renderer {
if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) {
material.side = BackSide;
- this._handleObjectFunction( object, material, scene, camera, lightsNode, group, 'backSide' ); // create backSide pass id
+ this._handleObjectFunction( object, material, scene, camera, lightsNode, group, clippingContext, 'backSide' ); // create backSide pass id
material.side = FrontSide;
- this._handleObjectFunction( object, material, scene, camera, lightsNode, group, passId ); // use default pass id
+ this._handleObjectFunction( object, material, scene, camera, lightsNode, group, clippingContext, passId ); // use default pass id
material.side = DoubleSide;
} else {
- this._handleObjectFunction( object, material, scene, camera, lightsNode, group, passId );
+ this._handleObjectFunction( object, material, scene, camera, lightsNode, group, clippingContext, passId );
}
@@ -1718,9 +1692,9 @@ class Renderer {
}
- _renderObjectDirect( object, material, scene, camera, lightsNode, group, passId ) {
+ _renderObjectDirect( object, material, scene, camera, lightsNode, group, clippingContext, passId ) {
- const renderObject = this._objects.get( object, material, scene, camera, lightsNode, this._currentRenderContext, passId );
+ const renderObject = this._objects.get( object, material, scene, camera, lightsNode, this._currentRenderContext, clippingContext, passId );
renderObject.drawRange = object.geometry.drawRange;
renderObject.group = group;
@@ -1759,9 +1733,9 @@ class Renderer {
}
- _createObjectPipeline( object, material, scene, camera, lightsNode, passId ) {
+ _createObjectPipeline( object, material, scene, camera, lightsNode, clippingContext, passId ) {
- const renderObject = this._objects.get( object, material, scene, camera, lightsNode, this._currentRenderContext, passId );
+ const renderObject = this._objects.get( object, material, scene, camera, lightsNode, this._currentRenderContext, clippingContext, passId );
//
diff --git a/src/renderers/webgpu/WebGPUBackend.js b/src/renderers/webgpu/WebGPUBackend.js
index 9d695aae17a193..2e06a4448093f2 100644
--- a/src/renderers/webgpu/WebGPUBackend.js
+++ b/src/renderers/webgpu/WebGPUBackend.js
@@ -1061,7 +1061,7 @@ class WebGPUBackend extends Backend {
data.sampleCount !== sampleCount || data.colorSpace !== colorSpace ||
data.colorFormat !== colorFormat || data.depthStencilFormat !== depthStencilFormat ||
data.primitiveTopology !== primitiveTopology ||
- data.clippingContextCacheKey !== renderObject.clippingContext.cacheKey
+ data.clippingContextCacheKey !== renderObject.clippingContextCacheKey
) {
data.material = material; data.materialVersion = material.version;
@@ -1079,7 +1079,7 @@ class WebGPUBackend extends Backend {
data.colorFormat = colorFormat;
data.depthStencilFormat = depthStencilFormat;
data.primitiveTopology = primitiveTopology;
- data.clippingContextCacheKey = renderObject.clippingContext.cacheKey;
+ data.clippingContextCacheKey = renderObject.clippingContextCacheKey;
needsUpdate = true;
@@ -1110,7 +1110,7 @@ class WebGPUBackend extends Backend {
utils.getCurrentColorSpace( renderContext ), utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ),
utils.getPrimitiveTopology( object, material ),
renderObject.getGeometryCacheKey(),
- renderObject.clippingContext.cacheKey
+ renderObject.clippingContextCacheKey
].join();
}