CSMHelper.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import {
  2. Group,
  3. Mesh,
  4. LineSegments,
  5. BufferGeometry,
  6. LineBasicMaterial,
  7. Box3Helper,
  8. Box3,
  9. PlaneGeometry,
  10. MeshBasicMaterial,
  11. BufferAttribute,
  12. DoubleSide
  13. } from 'three';
  14. /**
  15. * A helper for visualizing the cascades of a CSM instance.
  16. *
  17. * @augments Group
  18. */
  19. class CSMHelper extends Group {
  20. /**
  21. * Constructs a new CSM helper.
  22. *
  23. * @param {CSM|CSMShadowNode} csm - The CSM instance to visualize.
  24. */
  25. constructor( csm ) {
  26. super();
  27. /**
  28. * The CSM instance to visualize.
  29. *
  30. * @type {CSM|CSMShadowNode}
  31. */
  32. this.csm = csm;
  33. /**
  34. * Whether to display the CSM frustum or not.
  35. *
  36. * @type {boolean}
  37. * @default true
  38. */
  39. this.displayFrustum = true;
  40. /**
  41. * Whether to display the cascade planes or not.
  42. *
  43. * @type {boolean}
  44. * @default true
  45. */
  46. this.displayPlanes = true;
  47. /**
  48. * Whether to display the shadow bounds or not.
  49. *
  50. * @type {boolean}
  51. * @default true
  52. */
  53. this.displayShadowBounds = true;
  54. const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );
  55. const positions = new Float32Array( 24 );
  56. const frustumGeometry = new BufferGeometry();
  57. frustumGeometry.setIndex( new BufferAttribute( indices, 1 ) );
  58. frustumGeometry.setAttribute( 'position', new BufferAttribute( positions, 3, false ) );
  59. const frustumLines = new LineSegments( frustumGeometry, new LineBasicMaterial() );
  60. this.add( frustumLines );
  61. this.frustumLines = frustumLines;
  62. this.cascadeLines = [];
  63. this.cascadePlanes = [];
  64. this.shadowLines = [];
  65. }
  66. /**
  67. * This method must be called if one of the `display*` properties is changed at runtime.
  68. */
  69. updateVisibility() {
  70. const displayFrustum = this.displayFrustum;
  71. const displayPlanes = this.displayPlanes;
  72. const displayShadowBounds = this.displayShadowBounds;
  73. const frustumLines = this.frustumLines;
  74. const cascadeLines = this.cascadeLines;
  75. const cascadePlanes = this.cascadePlanes;
  76. const shadowLines = this.shadowLines;
  77. for ( let i = 0, l = cascadeLines.length; i < l; i ++ ) {
  78. const cascadeLine = cascadeLines[ i ];
  79. const cascadePlane = cascadePlanes[ i ];
  80. const shadowLineGroup = shadowLines[ i ];
  81. cascadeLine.visible = displayFrustum;
  82. cascadePlane.visible = displayFrustum && displayPlanes;
  83. shadowLineGroup.visible = displayShadowBounds;
  84. }
  85. frustumLines.visible = displayFrustum;
  86. }
  87. /**
  88. * Updates the helper. This method should be called in the app's animation loop.
  89. */
  90. update() {
  91. const csm = this.csm;
  92. const camera = csm.camera;
  93. const cascades = csm.cascades;
  94. const mainFrustum = csm.mainFrustum;
  95. const frustums = csm.frustums;
  96. const lights = csm.lights;
  97. const frustumLines = this.frustumLines;
  98. const frustumLinePositions = frustumLines.geometry.getAttribute( 'position' );
  99. const cascadeLines = this.cascadeLines;
  100. const cascadePlanes = this.cascadePlanes;
  101. const shadowLines = this.shadowLines;
  102. if ( camera === null ) return;
  103. this.position.copy( camera.position );
  104. this.quaternion.copy( camera.quaternion );
  105. this.scale.copy( camera.scale );
  106. this.updateMatrixWorld( true );
  107. while ( cascadeLines.length > cascades ) {
  108. this.remove( cascadeLines.pop() );
  109. this.remove( cascadePlanes.pop() );
  110. this.remove( shadowLines.pop() );
  111. }
  112. while ( cascadeLines.length < cascades ) {
  113. const cascadeLine = new Box3Helper( new Box3(), 0xffffff );
  114. const planeMat = new MeshBasicMaterial( { transparent: true, opacity: 0.1, depthWrite: false, side: DoubleSide } );
  115. const cascadePlane = new Mesh( new PlaneGeometry(), planeMat );
  116. const shadowLineGroup = new Group();
  117. const shadowLine = new Box3Helper( new Box3(), 0xffff00 );
  118. shadowLineGroup.add( shadowLine );
  119. this.add( cascadeLine );
  120. this.add( cascadePlane );
  121. this.add( shadowLineGroup );
  122. cascadeLines.push( cascadeLine );
  123. cascadePlanes.push( cascadePlane );
  124. shadowLines.push( shadowLineGroup );
  125. }
  126. for ( let i = 0; i < cascades; i ++ ) {
  127. const frustum = frustums[ i ];
  128. const light = lights[ i ];
  129. const shadowCam = light.shadow.camera;
  130. const farVerts = frustum.vertices.far;
  131. const cascadeLine = cascadeLines[ i ];
  132. const cascadePlane = cascadePlanes[ i ];
  133. const shadowLineGroup = shadowLines[ i ];
  134. const shadowLine = shadowLineGroup.children[ 0 ];
  135. cascadeLine.box.min.copy( farVerts[ 2 ] );
  136. cascadeLine.box.max.copy( farVerts[ 0 ] );
  137. cascadeLine.box.max.z += 1e-4;
  138. cascadePlane.position.addVectors( farVerts[ 0 ], farVerts[ 2 ] );
  139. cascadePlane.position.multiplyScalar( 0.5 );
  140. cascadePlane.scale.subVectors( farVerts[ 0 ], farVerts[ 2 ] );
  141. cascadePlane.scale.z = 1e-4;
  142. this.remove( shadowLineGroup );
  143. shadowLineGroup.position.copy( shadowCam.position );
  144. shadowLineGroup.quaternion.copy( shadowCam.quaternion );
  145. shadowLineGroup.scale.copy( shadowCam.scale );
  146. shadowLineGroup.updateMatrixWorld( true );
  147. this.attach( shadowLineGroup );
  148. shadowLine.box.min.set( shadowCam.bottom, shadowCam.left, - shadowCam.far );
  149. shadowLine.box.max.set( shadowCam.top, shadowCam.right, - shadowCam.near );
  150. }
  151. const nearVerts = mainFrustum.vertices.near;
  152. const farVerts = mainFrustum.vertices.far;
  153. frustumLinePositions.setXYZ( 0, farVerts[ 0 ].x, farVerts[ 0 ].y, farVerts[ 0 ].z );
  154. frustumLinePositions.setXYZ( 1, farVerts[ 3 ].x, farVerts[ 3 ].y, farVerts[ 3 ].z );
  155. frustumLinePositions.setXYZ( 2, farVerts[ 2 ].x, farVerts[ 2 ].y, farVerts[ 2 ].z );
  156. frustumLinePositions.setXYZ( 3, farVerts[ 1 ].x, farVerts[ 1 ].y, farVerts[ 1 ].z );
  157. frustumLinePositions.setXYZ( 4, nearVerts[ 0 ].x, nearVerts[ 0 ].y, nearVerts[ 0 ].z );
  158. frustumLinePositions.setXYZ( 5, nearVerts[ 3 ].x, nearVerts[ 3 ].y, nearVerts[ 3 ].z );
  159. frustumLinePositions.setXYZ( 6, nearVerts[ 2 ].x, nearVerts[ 2 ].y, nearVerts[ 2 ].z );
  160. frustumLinePositions.setXYZ( 7, nearVerts[ 1 ].x, nearVerts[ 1 ].y, nearVerts[ 1 ].z );
  161. frustumLinePositions.needsUpdate = true;
  162. }
  163. /**
  164. * Frees the GPU-related resources allocated by this instance. Call this
  165. * method whenever this instance is no longer used in your app.
  166. */
  167. dispose() {
  168. const frustumLines = this.frustumLines;
  169. const cascadeLines = this.cascadeLines;
  170. const cascadePlanes = this.cascadePlanes;
  171. const shadowLines = this.shadowLines;
  172. frustumLines.geometry.dispose();
  173. frustumLines.material.dispose();
  174. const cascades = this.csm.cascades;
  175. for ( let i = 0; i < cascades; i ++ ) {
  176. const cascadeLine = cascadeLines[ i ];
  177. const cascadePlane = cascadePlanes[ i ];
  178. const shadowLineGroup = shadowLines[ i ];
  179. const shadowLine = shadowLineGroup.children[ 0 ];
  180. cascadeLine.dispose(); // Box3Helper
  181. cascadePlane.geometry.dispose();
  182. cascadePlane.material.dispose();
  183. shadowLine.dispose(); // Box3Helper
  184. }
  185. }
  186. }
  187. export { CSMHelper };
粤ICP备19079148号