146 lines
3.7 KiB
JavaScript
146 lines
3.7 KiB
JavaScript
|
|
||
|
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 ) );
|