-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WebGPURenderer: Support clipping (#27691)
* clipping * add ; * remove testing code * cleanup and adjust default settiing in example * remove unused import * cleanup ClippingNode * combine methods * rework to increase efficiency * update screenshot * fix asyncCompile * fix against upstream * cleanup * simplify * exclude tests --------- Co-authored-by: aardgoose <angus.sawyer@email.com>
- Loading branch information
Showing
13 changed files
with
730 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
|
||
import Node from '../core/Node.js'; | ||
import { nodeObject } from '../shadernode/ShaderNode.js'; | ||
import { positionView } from './PositionNode.js'; | ||
import { diffuseColor, property } from '../core/PropertyNode.js'; | ||
import { tslFn } from '../shadernode/ShaderNode.js'; | ||
import { loop } from '../utils/LoopNode.js'; | ||
import { smoothstep } from '../math/MathNode.js'; | ||
import { uniforms } from './UniformsNode.js'; | ||
|
||
class ClippingNode extends Node { | ||
|
||
constructor( scope = ClippingNode.DEFAULT ) { | ||
|
||
super(); | ||
|
||
this.scope = scope; | ||
|
||
} | ||
|
||
setup( builder ) { | ||
|
||
super.setup( builder ); | ||
|
||
const clippingContext = builder.clippingContext; | ||
const { localClipIntersection, localClippingCount, globalClippingCount } = 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 ); | ||
|
||
} else { | ||
|
||
return this.setupDefault( clippingContext.planes, numClippingPlanes, numUnionClippingPlanes ); | ||
|
||
} | ||
|
||
} | ||
|
||
setupAlphaToCoverage( planes, numClippingPlanes, numUnionClippingPlanes ) { | ||
|
||
return tslFn( () => { | ||
|
||
const clippingPlanes = uniforms( planes ); | ||
|
||
const distanceToPlane = property( 'float', 'distanceToPlane' ); | ||
const distanceGradient = property( 'float', 'distanceToGradient' ); | ||
|
||
const clipOpacity = property( 'float', 'clipOpacity' ); | ||
|
||
clipOpacity.assign( 1 ); | ||
|
||
let plane; | ||
|
||
loop( numUnionClippingPlanes, ( { i } ) => { | ||
|
||
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.equal( 0.0 ).discard(); | ||
|
||
} ); | ||
|
||
if ( numUnionClippingPlanes < numClippingPlanes ) { | ||
|
||
const unionClipOpacity = property( 'float', 'unionclipOpacity' ); | ||
|
||
unionClipOpacity.assign( 1 ); | ||
|
||
loop( { start: numUnionClippingPlanes, end: numClippingPlanes }, ( { 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() ); | ||
|
||
} ); | ||
|
||
clipOpacity.mulAssign( unionClipOpacity.oneMinus() ); | ||
|
||
} | ||
|
||
diffuseColor.a.mulAssign( clipOpacity ); | ||
|
||
diffuseColor.a.equal( 0.0 ).discard(); | ||
|
||
} )(); | ||
|
||
} | ||
|
||
setupDefault( planes, numClippingPlanes, numUnionClippingPlanes ) { | ||
|
||
return tslFn( () => { | ||
|
||
const clippingPlanes = uniforms( planes ); | ||
|
||
let plane; | ||
|
||
loop( numUnionClippingPlanes, ( { i } ) => { | ||
|
||
plane = clippingPlanes.element( i ); | ||
positionView.dot( plane.xyz ).greaterThan( plane.w ).discard(); | ||
|
||
} ); | ||
|
||
if ( numUnionClippingPlanes < numClippingPlanes ) { | ||
|
||
const clipped = property( 'bool', 'clipped' ); | ||
|
||
clipped.assign( true ); | ||
|
||
loop( { start: numUnionClippingPlanes, end: numClippingPlanes }, ( { i } ) => { | ||
|
||
plane = clippingPlanes.element( i ); | ||
clipped.assign( positionView.dot( plane.xyz ).greaterThan( plane.w ).and( clipped ) ); | ||
|
||
} ); | ||
|
||
clipped.discard(); | ||
} | ||
|
||
} )(); | ||
|
||
} | ||
|
||
} | ||
|
||
ClippingNode.ALPHA_TO_COVERAGE = 'alphaToCoverage'; | ||
ClippingNode.DEFAULT = 'default'; | ||
|
||
export default ClippingNode; | ||
|
||
export const clipping = () => nodeObject( new ClippingNode() ); | ||
|
||
export const clippingAlpha = () => nodeObject( new ClippingNode( ClippingNode.ALPHA_TO_COVERAGE ) ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import { Matrix3, Plane, Vector4 } from 'three'; | ||
|
||
const _plane = new Plane(); | ||
const _viewNormalMatrix = new Matrix3(); | ||
|
||
let _clippingContextVersion = 0; | ||
|
||
class ClippingContext { | ||
|
||
constructor() { | ||
|
||
this.version = ++ _clippingContextVersion; | ||
|
||
this.globalClippingCount = 0; | ||
|
||
this.localClippingCount = 0; | ||
this.localClippingEnabled = false; | ||
this.localClipIntersection = false; | ||
|
||
this.planes = []; | ||
|
||
this.parentVersion = 0; | ||
|
||
} | ||
|
||
projectPlanes( source, offset ) { | ||
|
||
const l = source.length; | ||
const planes = this.planes; | ||
|
||
for ( let i = 0; i < l; i ++ ) { | ||
|
||
_plane.copy( source[ i ] ).applyMatrix4( this.viewMatrix, _viewNormalMatrix ); | ||
|
||
const v = planes[ offset + i ]; | ||
const normal = _plane.normal; | ||
|
||
v.x = - normal.x; | ||
v.y = - normal.y; | ||
v.z = - normal.z; | ||
v.w = _plane.constant; | ||
|
||
} | ||
|
||
} | ||
|
||
updateGlobal( renderer, camera ) { | ||
|
||
const rendererClippingPlanes = renderer.clippingPlanes; | ||
this.viewMatrix = camera.matrixWorldInverse; | ||
|
||
_viewNormalMatrix.getNormalMatrix( this.viewMatrix ); | ||
|
||
let update = false; | ||
|
||
if ( Array.isArray( rendererClippingPlanes ) && rendererClippingPlanes.length !== 0 ) { | ||
|
||
const l = rendererClippingPlanes.length; | ||
|
||
if ( l !== this.globalClippingCount ) { | ||
|
||
const planes = []; | ||
|
||
for ( let i = 0; i < l; i ++ ) { | ||
|
||
planes.push( new Vector4() ); | ||
|
||
} | ||
|
||
this.globalClippingCount = l; | ||
this.planes = planes; | ||
|
||
update = true; | ||
|
||
} | ||
|
||
this.projectPlanes( rendererClippingPlanes, 0 ); | ||
|
||
} else if ( this.globalClippingCount !== 0 ) { | ||
|
||
this.globalClippingCount = 0; | ||
this.planes = []; | ||
update = true; | ||
|
||
} | ||
|
||
if ( renderer.localClippingEnabled !== this.localClippingEnabled ) { | ||
|
||
this.localClippingEnabled = renderer.localClippingEnabled; | ||
update = true; | ||
|
||
} | ||
|
||
if ( update ) this.version = _clippingContextVersion ++; | ||
|
||
} | ||
|
||
update( parent, material ) { | ||
|
||
let update = false; | ||
|
||
if ( this !== parent && parent.version !== this.parentVersion ) { | ||
|
||
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; | ||
|
||
|
||
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.localClippingCount = l; | ||
update = true; | ||
|
||
} | ||
|
||
this.projectPlanes( localClippingPlanes, offset ); | ||
|
||
|
||
} else if ( this.localClippingCount !== 0 ) { | ||
|
||
this.localClippingCount = 0; | ||
update = true; | ||
|
||
} | ||
|
||
if ( this.localClipIntersection !== material.clipIntersection ) { | ||
|
||
this.localClipIntersection = material.clipIntersection; | ||
update = true; | ||
|
||
} | ||
|
||
} | ||
|
||
if ( update ) this.version = _clippingContextVersion ++; | ||
|
||
} | ||
|
||
} | ||
|
||
export default ClippingContext; |
Oops, something went wrong.