shadowLight.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. import { serialize, serializeAsVector3 } from "../Misc/decorators";
  2. import { Camera } from "../Cameras/camera";
  3. import { Scene } from "../scene";
  4. import { Matrix, Vector3, Axis } from "../Maths/math";
  5. import { AbstractMesh } from "../Meshes/abstractMesh";
  6. import { Light } from "./light";
  7. import { _TimeToken } from "../Instrumentation/timeToken";
  8. import { _DepthCullingState, _StencilState, _AlphaState } from "../States/index";
  9. /**
  10. * Interface describing all the common properties and methods a shadow light needs to implement.
  11. * This helps both the shadow generator and materials to genrate the corresponding shadow maps
  12. * as well as binding the different shadow properties to the effects.
  13. */
  14. export interface IShadowLight extends Light {
  15. /**
  16. * The light id in the scene (used in scene.findLighById for instance)
  17. */
  18. id: string;
  19. /**
  20. * The position the shdow will be casted from.
  21. */
  22. position: Vector3;
  23. /**
  24. * In 2d mode (needCube being false), the direction used to cast the shadow.
  25. */
  26. direction: Vector3;
  27. /**
  28. * The transformed position. Position of the light in world space taking parenting in account.
  29. */
  30. transformedPosition: Vector3;
  31. /**
  32. * The transformed direction. Direction of the light in world space taking parenting in account.
  33. */
  34. transformedDirection: Vector3;
  35. /**
  36. * The friendly name of the light in the scene.
  37. */
  38. name: string;
  39. /**
  40. * Defines the shadow projection clipping minimum z value.
  41. */
  42. shadowMinZ: number;
  43. /**
  44. * Defines the shadow projection clipping maximum z value.
  45. */
  46. shadowMaxZ: number;
  47. /**
  48. * Computes the transformed information (transformedPosition and transformedDirection in World space) of the current light
  49. * @returns true if the information has been computed, false if it does not need to (no parenting)
  50. */
  51. computeTransformedInformation(): boolean;
  52. /**
  53. * Gets the scene the light belongs to.
  54. * @returns The scene
  55. */
  56. getScene(): Scene;
  57. /**
  58. * Callback defining a custom Projection Matrix Builder.
  59. * This can be used to override the default projection matrix computation.
  60. */
  61. customProjectionMatrixBuilder: (viewMatrix: Matrix, renderList: Array<AbstractMesh>, result: Matrix) => void;
  62. /**
  63. * Sets the shadow projection matrix in parameter to the generated projection matrix.
  64. * @param matrix The materix to updated with the projection information
  65. * @param viewMatrix The transform matrix of the light
  66. * @param renderList The list of mesh to render in the map
  67. * @returns The current light
  68. */
  69. setShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): IShadowLight;
  70. /**
  71. * Gets the current depth scale used in ESM.
  72. * @returns The scale
  73. */
  74. getDepthScale(): number;
  75. /**
  76. * Returns whether or not the shadow generation require a cube texture or a 2d texture.
  77. * @returns true if a cube texture needs to be use
  78. */
  79. needCube(): boolean;
  80. /**
  81. * Detects if the projection matrix requires to be recomputed this frame.
  82. * @returns true if it requires to be recomputed otherwise, false.
  83. */
  84. needProjectionMatrixCompute(): boolean;
  85. /**
  86. * Forces the shadow generator to recompute the projection matrix even if position and direction did not changed.
  87. */
  88. forceProjectionMatrixCompute(): void;
  89. /**
  90. * Get the direction to use to render the shadow map. In case of cube texture, the face index can be passed.
  91. * @param faceIndex The index of the face we are computed the direction to generate shadow
  92. * @returns The set direction in 2d mode otherwise the direction to the cubemap face if needCube() is true
  93. */
  94. getShadowDirection(faceIndex?: number): Vector3;
  95. /**
  96. * Gets the minZ used for shadow according to both the scene and the light.
  97. * @param activeCamera The camera we are returning the min for
  98. * @returns the depth min z
  99. */
  100. getDepthMinZ(activeCamera: Camera): number;
  101. /**
  102. * Gets the maxZ used for shadow according to both the scene and the light.
  103. * @param activeCamera The camera we are returning the max for
  104. * @returns the depth max z
  105. */
  106. getDepthMaxZ(activeCamera: Camera): number;
  107. }
  108. /**
  109. * Base implementation IShadowLight
  110. * It groups all the common behaviour in order to reduce dupplication and better follow the DRY pattern.
  111. */
  112. export abstract class ShadowLight extends Light implements IShadowLight {
  113. protected abstract _setDefaultShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): void;
  114. protected _position: Vector3;
  115. protected _setPosition(value: Vector3) {
  116. this._position = value;
  117. }
  118. /**
  119. * Sets the position the shadow will be casted from. Also use as the light position for both
  120. * point and spot lights.
  121. */
  122. @serializeAsVector3()
  123. public get position(): Vector3 {
  124. return this._position;
  125. }
  126. /**
  127. * Sets the position the shadow will be casted from. Also use as the light position for both
  128. * point and spot lights.
  129. */
  130. public set position(value: Vector3) {
  131. this._setPosition(value);
  132. }
  133. protected _direction: Vector3;
  134. protected _setDirection(value: Vector3) {
  135. this._direction = value;
  136. }
  137. /**
  138. * In 2d mode (needCube being false), gets the direction used to cast the shadow.
  139. * Also use as the light direction on spot and directional lights.
  140. */
  141. @serializeAsVector3()
  142. public get direction(): Vector3 {
  143. return this._direction;
  144. }
  145. /**
  146. * In 2d mode (needCube being false), sets the direction used to cast the shadow.
  147. * Also use as the light direction on spot and directional lights.
  148. */
  149. public set direction(value: Vector3) {
  150. this._setDirection(value);
  151. }
  152. private _shadowMinZ: number;
  153. /**
  154. * Gets the shadow projection clipping minimum z value.
  155. */
  156. @serialize()
  157. public get shadowMinZ(): number {
  158. return this._shadowMinZ;
  159. }
  160. /**
  161. * Sets the shadow projection clipping minimum z value.
  162. */
  163. public set shadowMinZ(value: number) {
  164. this._shadowMinZ = value;
  165. this.forceProjectionMatrixCompute();
  166. }
  167. private _shadowMaxZ: number;
  168. /**
  169. * Sets the shadow projection clipping maximum z value.
  170. */
  171. @serialize()
  172. public get shadowMaxZ(): number {
  173. return this._shadowMaxZ;
  174. }
  175. /**
  176. * Gets the shadow projection clipping maximum z value.
  177. */
  178. public set shadowMaxZ(value: number) {
  179. this._shadowMaxZ = value;
  180. this.forceProjectionMatrixCompute();
  181. }
  182. /**
  183. * Callback defining a custom Projection Matrix Builder.
  184. * This can be used to override the default projection matrix computation.
  185. */
  186. public customProjectionMatrixBuilder: (viewMatrix: Matrix, renderList: Array<AbstractMesh>, result: Matrix) => void;
  187. /**
  188. * The transformed position. Position of the light in world space taking parenting in account.
  189. */
  190. public transformedPosition: Vector3;
  191. /**
  192. * The transformed direction. Direction of the light in world space taking parenting in account.
  193. */
  194. public transformedDirection: Vector3;
  195. private _needProjectionMatrixCompute: boolean = true;
  196. /**
  197. * Computes the transformed information (transformedPosition and transformedDirection in World space) of the current light
  198. * @returns true if the information has been computed, false if it does not need to (no parenting)
  199. */
  200. public computeTransformedInformation(): boolean {
  201. if (this.parent && this.parent.getWorldMatrix) {
  202. if (!this.transformedPosition) {
  203. this.transformedPosition = Vector3.Zero();
  204. }
  205. Vector3.TransformCoordinatesToRef(this.position, this.parent.getWorldMatrix(), this.transformedPosition);
  206. // In case the direction is present.
  207. if (this.direction) {
  208. if (!this.transformedDirection) {
  209. this.transformedDirection = Vector3.Zero();
  210. }
  211. Vector3.TransformNormalToRef(this.direction, this.parent.getWorldMatrix(), this.transformedDirection);
  212. }
  213. return true;
  214. }
  215. return false;
  216. }
  217. /**
  218. * Return the depth scale used for the shadow map.
  219. * @returns the depth scale.
  220. */
  221. public getDepthScale(): number {
  222. return 50.0;
  223. }
  224. /**
  225. * Get the direction to use to render the shadow map. In case of cube texture, the face index can be passed.
  226. * @param faceIndex The index of the face we are computed the direction to generate shadow
  227. * @returns The set direction in 2d mode otherwise the direction to the cubemap face if needCube() is true
  228. */
  229. public getShadowDirection(faceIndex?: number): Vector3 {
  230. return this.transformedDirection ? this.transformedDirection : this.direction;
  231. }
  232. /**
  233. * Returns the ShadowLight absolute position in the World.
  234. * @returns the position vector in world space
  235. */
  236. public getAbsolutePosition(): Vector3 {
  237. return this.transformedPosition ? this.transformedPosition : this.position;
  238. }
  239. /**
  240. * Sets the ShadowLight direction toward the passed target.
  241. * @param target The point to target in local space
  242. * @returns the updated ShadowLight direction
  243. */
  244. public setDirectionToTarget(target: Vector3): Vector3 {
  245. this.direction = Vector3.Normalize(target.subtract(this.position));
  246. return this.direction;
  247. }
  248. /**
  249. * Returns the light rotation in euler definition.
  250. * @returns the x y z rotation in local space.
  251. */
  252. public getRotation(): Vector3 {
  253. this.direction.normalize();
  254. var xaxis = Vector3.Cross(this.direction, Axis.Y);
  255. var yaxis = Vector3.Cross(xaxis, this.direction);
  256. return Vector3.RotationFromAxis(xaxis, yaxis, this.direction);
  257. }
  258. /**
  259. * Returns whether or not the shadow generation require a cube texture or a 2d texture.
  260. * @returns true if a cube texture needs to be use
  261. */
  262. public needCube(): boolean {
  263. return false;
  264. }
  265. /**
  266. * Detects if the projection matrix requires to be recomputed this frame.
  267. * @returns true if it requires to be recomputed otherwise, false.
  268. */
  269. public needProjectionMatrixCompute(): boolean {
  270. return this._needProjectionMatrixCompute;
  271. }
  272. /**
  273. * Forces the shadow generator to recompute the projection matrix even if position and direction did not changed.
  274. */
  275. public forceProjectionMatrixCompute(): void {
  276. this._needProjectionMatrixCompute = true;
  277. }
  278. /** @hidden */
  279. public _initCache() {
  280. super._initCache();
  281. this._cache.position = Vector3.Zero();
  282. }
  283. /** @hidden */
  284. public _isSynchronized(): boolean {
  285. if (!this._cache.position.equals(this.position)) {
  286. return false;
  287. }
  288. return true;
  289. }
  290. /**
  291. * Computes the world matrix of the node
  292. * @param force defines if the cache version should be invalidated forcing the world matrix to be created from scratch
  293. * @returns the world matrix
  294. */
  295. public computeWorldMatrix(force?: boolean): Matrix {
  296. if (!force && this.isSynchronized()) {
  297. this._currentRenderId = this.getScene().getRenderId();
  298. return this._worldMatrix;
  299. }
  300. this._updateCache();
  301. this._cache.position.copyFrom(this.position);
  302. if (!this._worldMatrix) {
  303. this._worldMatrix = Matrix.Identity();
  304. }
  305. Matrix.TranslationToRef(this.position.x, this.position.y, this.position.z, this._worldMatrix);
  306. if (this.parent && this.parent.getWorldMatrix) {
  307. this._worldMatrix.multiplyToRef(this.parent.getWorldMatrix(), this._worldMatrix);
  308. this._markSyncedWithParent();
  309. }
  310. // Cache the determinant
  311. this._worldMatrixDeterminantIsDirty = true;
  312. return this._worldMatrix;
  313. }
  314. /**
  315. * Gets the minZ used for shadow according to both the scene and the light.
  316. * @param activeCamera The camera we are returning the min for
  317. * @returns the depth min z
  318. */
  319. public getDepthMinZ(activeCamera: Camera): number {
  320. return this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ;
  321. }
  322. /**
  323. * Gets the maxZ used for shadow according to both the scene and the light.
  324. * @param activeCamera The camera we are returning the max for
  325. * @returns the depth max z
  326. */
  327. public getDepthMaxZ(activeCamera: Camera): number {
  328. return this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ;
  329. }
  330. /**
  331. * Sets the shadow projection matrix in parameter to the generated projection matrix.
  332. * @param matrix The materix to updated with the projection information
  333. * @param viewMatrix The transform matrix of the light
  334. * @param renderList The list of mesh to render in the map
  335. * @returns The current light
  336. */
  337. public setShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): IShadowLight {
  338. if (this.customProjectionMatrixBuilder) {
  339. this.customProjectionMatrixBuilder(viewMatrix, renderList, matrix);
  340. }
  341. else {
  342. this._setDefaultShadowProjectionMatrix(matrix, viewMatrix, renderList);
  343. }
  344. return this;
  345. }
  346. }