ShadowMesh.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import {
  2. Matrix4,
  3. Mesh,
  4. MeshBasicMaterial,
  5. EqualStencilFunc,
  6. IncrementStencilOp
  7. } from 'three';
  8. const _shadowMatrix = new Matrix4();
  9. /**
  10. * A Shadow Mesh that follows a shadow-casting mesh in the scene,
  11. * but is confined to a single plane. This technique can be used as
  12. * a very performant alternative to classic shadow mapping. However,
  13. * it has serious limitations like:
  14. *
  15. * - Shadows can only be casted on flat planes.
  16. * - No soft shadows support.
  17. *
  18. * ```js
  19. * const cubeShadow = new ShadowMesh( cube );
  20. * scene.add( cubeShadow );
  21. * ```
  22. *
  23. * @augments Mesh
  24. * @three_import import { ShadowMesh } from 'three/addons/objects/ShadowMesh.js';
  25. */
  26. class ShadowMesh extends Mesh {
  27. /**
  28. * Constructs a new shadow mesh.
  29. *
  30. * @param {Mesh} mesh - The shadow-casting reference mesh.
  31. */
  32. constructor( mesh ) {
  33. const shadowMaterial = new MeshBasicMaterial( {
  34. color: 0x000000,
  35. transparent: true,
  36. opacity: 0.6,
  37. depthWrite: false,
  38. stencilWrite: true,
  39. stencilFunc: EqualStencilFunc,
  40. stencilRef: 0,
  41. stencilZPass: IncrementStencilOp
  42. } );
  43. super( mesh.geometry, shadowMaterial );
  44. /**
  45. * This flag can be used for type testing.
  46. *
  47. * @type {boolean}
  48. * @readonly
  49. * @default true
  50. */
  51. this.isShadowMesh = true;
  52. /**
  53. * Represent the world matrix of the reference mesh.
  54. *
  55. * @type {Matrix4}
  56. */
  57. this.meshMatrix = mesh.matrixWorld;
  58. /**
  59. * Overwritten to disable view-frustum culling by default.
  60. *
  61. * @type {boolean}
  62. * @default false
  63. */
  64. this.frustumCulled = false;
  65. /**
  66. * Overwritten to disable automatic matrix update. The local
  67. * matrix is computed manually in {@link ShadowMesh#update}.
  68. *
  69. * @type {boolean}
  70. * @default false
  71. */
  72. this.matrixAutoUpdate = false;
  73. }
  74. /**
  75. * Updates the shadow mesh so it follows its shadow-casting reference mesh.
  76. *
  77. * @param {Plane} plane - The plane onto the shadow mesh is projected.
  78. * @param {Vector4} lightPosition4D - The light position.
  79. */
  80. update( plane, lightPosition4D ) {
  81. // based on https://www.opengl.org/archives/resources/features/StencilTalk/tsld021.htm
  82. const dot = plane.normal.x * lightPosition4D.x +
  83. plane.normal.y * lightPosition4D.y +
  84. plane.normal.z * lightPosition4D.z +
  85. - plane.constant * lightPosition4D.w;
  86. const sme = _shadowMatrix.elements;
  87. sme[ 0 ] = dot - lightPosition4D.x * plane.normal.x;
  88. sme[ 4 ] = - lightPosition4D.x * plane.normal.y;
  89. sme[ 8 ] = - lightPosition4D.x * plane.normal.z;
  90. sme[ 12 ] = - lightPosition4D.x * - plane.constant;
  91. sme[ 1 ] = - lightPosition4D.y * plane.normal.x;
  92. sme[ 5 ] = dot - lightPosition4D.y * plane.normal.y;
  93. sme[ 9 ] = - lightPosition4D.y * plane.normal.z;
  94. sme[ 13 ] = - lightPosition4D.y * - plane.constant;
  95. sme[ 2 ] = - lightPosition4D.z * plane.normal.x;
  96. sme[ 6 ] = - lightPosition4D.z * plane.normal.y;
  97. sme[ 10 ] = dot - lightPosition4D.z * plane.normal.z;
  98. sme[ 14 ] = - lightPosition4D.z * - plane.constant;
  99. sme[ 3 ] = - lightPosition4D.w * plane.normal.x;
  100. sme[ 7 ] = - lightPosition4D.w * plane.normal.y;
  101. sme[ 11 ] = - lightPosition4D.w * plane.normal.z;
  102. sme[ 15 ] = dot - lightPosition4D.w * - plane.constant;
  103. this.matrix.multiplyMatrices( _shadowMatrix, this.meshMatrix );
  104. }
  105. }
  106. export { ShadowMesh };