ClippingNode.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import Node from '../core/Node.js';
  2. import { nodeObject } from '../shadernode/ShaderNode.js';
  3. import { positionView } from './PositionNode.js';
  4. import { diffuseColor, property } from '../core/PropertyNode.js';
  5. import { tslFn } from '../shadernode/ShaderNode.js';
  6. import { loop } from '../utils/LoopNode.js';
  7. import { smoothstep } from '../math/MathNode.js';
  8. import { uniforms } from './UniformsNode.js';
  9. class ClippingNode extends Node {
  10. constructor( scope = ClippingNode.DEFAULT ) {
  11. super();
  12. this.scope = scope;
  13. }
  14. setup( builder ) {
  15. super.setup( builder );
  16. const clippingContext = builder.clippingContext;
  17. const { localClipIntersection, localClippingCount, globalClippingCount } = clippingContext;
  18. const numClippingPlanes = globalClippingCount + localClippingCount;
  19. const numUnionClippingPlanes = localClipIntersection ? numClippingPlanes - localClippingCount : numClippingPlanes;
  20. if ( this.scope === ClippingNode.ALPHA_TO_COVERAGE ) {
  21. return this.setupAlphaToCoverage( clippingContext.planes, numClippingPlanes, numUnionClippingPlanes );
  22. } else {
  23. return this.setupDefault( clippingContext.planes, numClippingPlanes, numUnionClippingPlanes );
  24. }
  25. }
  26. setupAlphaToCoverage( planes, numClippingPlanes, numUnionClippingPlanes ) {
  27. return tslFn( () => {
  28. const clippingPlanes = uniforms( planes );
  29. const distanceToPlane = property( 'float', 'distanceToPlane' );
  30. const distanceGradient = property( 'float', 'distanceToGradient' );
  31. const clipOpacity = property( 'float', 'clipOpacity' );
  32. clipOpacity.assign( 1 );
  33. let plane;
  34. loop( numUnionClippingPlanes, ( { i } ) => {
  35. plane = clippingPlanes.element( i );
  36. distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
  37. distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );
  38. clipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ) );
  39. clipOpacity.equal( 0.0 ).discard();
  40. } );
  41. if ( numUnionClippingPlanes < numClippingPlanes ) {
  42. const unionClipOpacity = property( 'float', 'unionclipOpacity' );
  43. unionClipOpacity.assign( 1 );
  44. loop( { start: numUnionClippingPlanes, end: numClippingPlanes }, ( { i } ) => {
  45. plane = clippingPlanes.element( i );
  46. distanceToPlane.assign( positionView.dot( plane.xyz ).negate().add( plane.w ) );
  47. distanceGradient.assign( distanceToPlane.fwidth().div( 2.0 ) );
  48. unionClipOpacity.mulAssign( smoothstep( distanceGradient.negate(), distanceGradient, distanceToPlane ).oneMinus() );
  49. } );
  50. clipOpacity.mulAssign( unionClipOpacity.oneMinus() );
  51. }
  52. diffuseColor.a.mulAssign( clipOpacity );
  53. diffuseColor.a.equal( 0.0 ).discard();
  54. } )();
  55. }
  56. setupDefault( planes, numClippingPlanes, numUnionClippingPlanes ) {
  57. return tslFn( () => {
  58. const clippingPlanes = uniforms( planes );
  59. let plane;
  60. loop( numUnionClippingPlanes, ( { i } ) => {
  61. plane = clippingPlanes.element( i );
  62. positionView.dot( plane.xyz ).greaterThan( plane.w ).discard();
  63. } );
  64. if ( numUnionClippingPlanes < numClippingPlanes ) {
  65. const clipped = property( 'bool', 'clipped' );
  66. clipped.assign( true );
  67. loop( { start: numUnionClippingPlanes, end: numClippingPlanes }, ( { i } ) => {
  68. plane = clippingPlanes.element( i );
  69. clipped.assign( positionView.dot( plane.xyz ).greaterThan( plane.w ).and( clipped ) );
  70. } );
  71. clipped.discard();
  72. }
  73. } )();
  74. }
  75. }
  76. ClippingNode.ALPHA_TO_COVERAGE = 'alphaToCoverage';
  77. ClippingNode.DEFAULT = 'default';
  78. export default ClippingNode;
  79. export const clipping = () => nodeObject( new ClippingNode() );
  80. export const clippingAlpha = () => nodeObject( new ClippingNode( ClippingNode.ALPHA_TO_COVERAGE ) );