directionalLight.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. import { __decorate } from "../tslib.es6.js";
  2. import { serialize } from "../Misc/decorators.js";
  3. import { Matrix, Vector3 } from "../Maths/math.vector.js";
  4. import { Node } from "../node.js";
  5. import { Light } from "./light.js";
  6. import { ShadowLight } from "./shadowLight.js";
  7. Node.AddNodeConstructor("Light_Type_1", (name, scene) => {
  8. return () => new DirectionalLight(name, Vector3.Zero(), scene);
  9. });
  10. /**
  11. * A directional light is defined by a direction (what a surprise!).
  12. * The light is emitted from everywhere in the specified direction, and has an infinite range.
  13. * An example of a directional light is when a distance planet is lit by the apparently parallel lines of light from its sun. Light in a downward direction will light the top of an object.
  14. * Documentation: https://doc.babylonjs.com/features/featuresDeepDive/lights/lights_introduction
  15. */
  16. export class DirectionalLight extends ShadowLight {
  17. /**
  18. * Fix frustum size for the shadow generation. This is disabled if the value is 0.
  19. */
  20. get shadowFrustumSize() {
  21. return this._shadowFrustumSize;
  22. }
  23. /**
  24. * Specifies a fix frustum size for the shadow generation.
  25. */
  26. set shadowFrustumSize(value) {
  27. this._shadowFrustumSize = value;
  28. this.forceProjectionMatrixCompute();
  29. }
  30. /**
  31. * Gets the shadow projection scale against the optimal computed one.
  32. * 0.1 by default which means that the projection window is increase by 10% from the optimal size.
  33. * This does not impact in fixed frustum size (shadowFrustumSize being set)
  34. */
  35. get shadowOrthoScale() {
  36. return this._shadowOrthoScale;
  37. }
  38. /**
  39. * Sets the shadow projection scale against the optimal computed one.
  40. * 0.1 by default which means that the projection window is increase by 10% from the optimal size.
  41. * This does not impact in fixed frustum size (shadowFrustumSize being set)
  42. */
  43. set shadowOrthoScale(value) {
  44. this._shadowOrthoScale = value;
  45. this.forceProjectionMatrixCompute();
  46. }
  47. /**
  48. * Gets or sets the orthoLeft property used to build the light frustum
  49. */
  50. get orthoLeft() {
  51. return this._orthoLeft;
  52. }
  53. set orthoLeft(left) {
  54. this._orthoLeft = left;
  55. }
  56. /**
  57. * Gets or sets the orthoRight property used to build the light frustum
  58. */
  59. get orthoRight() {
  60. return this._orthoRight;
  61. }
  62. set orthoRight(right) {
  63. this._orthoRight = right;
  64. }
  65. /**
  66. * Gets or sets the orthoTop property used to build the light frustum
  67. */
  68. get orthoTop() {
  69. return this._orthoTop;
  70. }
  71. set orthoTop(top) {
  72. this._orthoTop = top;
  73. }
  74. /**
  75. * Gets or sets the orthoBottom property used to build the light frustum
  76. */
  77. get orthoBottom() {
  78. return this._orthoBottom;
  79. }
  80. set orthoBottom(bottom) {
  81. this._orthoBottom = bottom;
  82. }
  83. /**
  84. * Creates a DirectionalLight object in the scene, oriented towards the passed direction (Vector3).
  85. * The directional light is emitted from everywhere in the given direction.
  86. * It can cast shadows.
  87. * Documentation : https://doc.babylonjs.com/features/featuresDeepDive/lights/lights_introduction
  88. * @param name The friendly name of the light
  89. * @param direction The direction of the light
  90. * @param scene The scene the light belongs to
  91. */
  92. constructor(name, direction, scene) {
  93. super(name, scene);
  94. this._shadowFrustumSize = 0;
  95. this._shadowOrthoScale = 0.1;
  96. /**
  97. * Automatically compute the projection matrix to best fit (including all the casters)
  98. * on each frame.
  99. */
  100. this.autoUpdateExtends = true;
  101. /**
  102. * Automatically compute the shadowMinZ and shadowMaxZ for the projection matrix to best fit (including all the casters)
  103. * on each frame. autoUpdateExtends must be set to true for this to work
  104. */
  105. this.autoCalcShadowZBounds = false;
  106. // Cache
  107. this._orthoLeft = Number.MAX_VALUE;
  108. this._orthoRight = Number.MIN_VALUE;
  109. this._orthoTop = Number.MIN_VALUE;
  110. this._orthoBottom = Number.MAX_VALUE;
  111. this.position = direction.scale(-1.0);
  112. this.direction = direction;
  113. }
  114. /**
  115. * Returns the string "DirectionalLight".
  116. * @returns The class name
  117. */
  118. getClassName() {
  119. return "DirectionalLight";
  120. }
  121. /**
  122. * Returns the integer 1.
  123. * @returns The light Type id as a constant defines in Light.LIGHTTYPEID_x
  124. */
  125. getTypeID() {
  126. return Light.LIGHTTYPEID_DIRECTIONALLIGHT;
  127. }
  128. /**
  129. * Sets the passed matrix "matrix" as projection matrix for the shadows cast by the light according to the passed view matrix.
  130. * Returns the DirectionalLight Shadow projection matrix.
  131. * @param matrix
  132. * @param viewMatrix
  133. * @param renderList
  134. */
  135. _setDefaultShadowProjectionMatrix(matrix, viewMatrix, renderList) {
  136. if (this.shadowFrustumSize > 0) {
  137. this._setDefaultFixedFrustumShadowProjectionMatrix(matrix);
  138. }
  139. else {
  140. this._setDefaultAutoExtendShadowProjectionMatrix(matrix, viewMatrix, renderList);
  141. }
  142. }
  143. /**
  144. * Sets the passed matrix "matrix" as fixed frustum projection matrix for the shadows cast by the light according to the passed view matrix.
  145. * Returns the DirectionalLight Shadow projection matrix.
  146. * @param matrix
  147. */
  148. _setDefaultFixedFrustumShadowProjectionMatrix(matrix) {
  149. const activeCamera = this.getScene().activeCamera;
  150. if (!activeCamera) {
  151. return;
  152. }
  153. Matrix.OrthoLHToRef(this.shadowFrustumSize, this.shadowFrustumSize, this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ, this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ, matrix, this.getScene().getEngine().isNDCHalfZRange);
  154. }
  155. /**
  156. * Sets the passed matrix "matrix" as auto extend projection matrix for the shadows cast by the light according to the passed view matrix.
  157. * Returns the DirectionalLight Shadow projection matrix.
  158. * @param matrix
  159. * @param viewMatrix
  160. * @param renderList
  161. */
  162. _setDefaultAutoExtendShadowProjectionMatrix(matrix, viewMatrix, renderList) {
  163. const activeCamera = this.getScene().activeCamera;
  164. if (!activeCamera) {
  165. return;
  166. }
  167. // Check extends
  168. if (this.autoUpdateExtends || this._orthoLeft === Number.MAX_VALUE) {
  169. const tempVector3 = Vector3.Zero();
  170. this._orthoLeft = Number.MAX_VALUE;
  171. this._orthoRight = -Number.MAX_VALUE;
  172. this._orthoTop = -Number.MAX_VALUE;
  173. this._orthoBottom = Number.MAX_VALUE;
  174. let shadowMinZ = Number.MAX_VALUE;
  175. let shadowMaxZ = -Number.MAX_VALUE;
  176. for (let meshIndex = 0; meshIndex < renderList.length; meshIndex++) {
  177. const mesh = renderList[meshIndex];
  178. if (!mesh) {
  179. continue;
  180. }
  181. const boundingInfo = mesh.getBoundingInfo();
  182. const boundingBox = boundingInfo.boundingBox;
  183. for (let index = 0; index < boundingBox.vectorsWorld.length; index++) {
  184. Vector3.TransformCoordinatesToRef(boundingBox.vectorsWorld[index], viewMatrix, tempVector3);
  185. if (tempVector3.x < this._orthoLeft) {
  186. this._orthoLeft = tempVector3.x;
  187. }
  188. if (tempVector3.y < this._orthoBottom) {
  189. this._orthoBottom = tempVector3.y;
  190. }
  191. if (tempVector3.x > this._orthoRight) {
  192. this._orthoRight = tempVector3.x;
  193. }
  194. if (tempVector3.y > this._orthoTop) {
  195. this._orthoTop = tempVector3.y;
  196. }
  197. if (this.autoCalcShadowZBounds) {
  198. if (tempVector3.z < shadowMinZ) {
  199. shadowMinZ = tempVector3.z;
  200. }
  201. if (tempVector3.z > shadowMaxZ) {
  202. shadowMaxZ = tempVector3.z;
  203. }
  204. }
  205. }
  206. }
  207. if (this.autoCalcShadowZBounds) {
  208. this._shadowMinZ = shadowMinZ;
  209. this._shadowMaxZ = shadowMaxZ;
  210. }
  211. }
  212. const xOffset = this._orthoRight - this._orthoLeft;
  213. const yOffset = this._orthoTop - this._orthoBottom;
  214. const minZ = this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ;
  215. const maxZ = this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ;
  216. const useReverseDepthBuffer = this.getScene().getEngine().useReverseDepthBuffer;
  217. Matrix.OrthoOffCenterLHToRef(this._orthoLeft - xOffset * this.shadowOrthoScale, this._orthoRight + xOffset * this.shadowOrthoScale, this._orthoBottom - yOffset * this.shadowOrthoScale, this._orthoTop + yOffset * this.shadowOrthoScale, useReverseDepthBuffer ? maxZ : minZ, useReverseDepthBuffer ? minZ : maxZ, matrix, this.getScene().getEngine().isNDCHalfZRange);
  218. }
  219. _buildUniformLayout() {
  220. this._uniformBuffer.addUniform("vLightData", 4);
  221. this._uniformBuffer.addUniform("vLightDiffuse", 4);
  222. this._uniformBuffer.addUniform("vLightSpecular", 4);
  223. this._uniformBuffer.addUniform("shadowsInfo", 3);
  224. this._uniformBuffer.addUniform("depthValues", 2);
  225. this._uniformBuffer.create();
  226. }
  227. /**
  228. * Sets the passed Effect object with the DirectionalLight transformed position (or position if not parented) and the passed name.
  229. * @param effect The effect to update
  230. * @param lightIndex The index of the light in the effect to update
  231. * @returns The directional light
  232. */
  233. transferToEffect(effect, lightIndex) {
  234. if (this.computeTransformedInformation()) {
  235. this._uniformBuffer.updateFloat4("vLightData", this.transformedDirection.x, this.transformedDirection.y, this.transformedDirection.z, 1, lightIndex);
  236. return this;
  237. }
  238. this._uniformBuffer.updateFloat4("vLightData", this.direction.x, this.direction.y, this.direction.z, 1, lightIndex);
  239. return this;
  240. }
  241. transferToNodeMaterialEffect(effect, lightDataUniformName) {
  242. if (this.computeTransformedInformation()) {
  243. effect.setFloat3(lightDataUniformName, this.transformedDirection.x, this.transformedDirection.y, this.transformedDirection.z);
  244. return this;
  245. }
  246. effect.setFloat3(lightDataUniformName, this.direction.x, this.direction.y, this.direction.z);
  247. return this;
  248. }
  249. /**
  250. * Gets the minZ used for shadow according to both the scene and the light.
  251. *
  252. * Values are fixed on directional lights as it relies on an ortho projection hence the need to convert being
  253. * -1 and 1 to 0 and 1 doing (depth + min) / (min + max) -> (depth + 1) / (1 + 1) -> (depth * 0.5) + 0.5.
  254. * (when not using reverse depth buffer / NDC half Z range)
  255. * @param activeCamera The camera we are returning the min for
  256. * @returns the depth min z
  257. */
  258. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  259. getDepthMinZ(activeCamera) {
  260. const engine = this._scene.getEngine();
  261. return !engine.useReverseDepthBuffer && engine.isNDCHalfZRange ? 0 : 1;
  262. }
  263. /**
  264. * Gets the maxZ used for shadow according to both the scene and the light.
  265. *
  266. * Values are fixed on directional lights as it relies on an ortho projection hence the need to convert being
  267. * -1 and 1 to 0 and 1 doing (depth + min) / (min + max) -> (depth + 1) / (1 + 1) -> (depth * 0.5) + 0.5.
  268. * (when not using reverse depth buffer / NDC half Z range)
  269. * @param activeCamera The camera we are returning the max for
  270. * @returns the depth max z
  271. */
  272. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  273. getDepthMaxZ(activeCamera) {
  274. const engine = this._scene.getEngine();
  275. return engine.useReverseDepthBuffer && engine.isNDCHalfZRange ? 0 : 1;
  276. }
  277. /**
  278. * Prepares the list of defines specific to the light type.
  279. * @param defines the list of defines
  280. * @param lightIndex defines the index of the light for the effect
  281. */
  282. prepareLightSpecificDefines(defines, lightIndex) {
  283. defines["DIRLIGHT" + lightIndex] = true;
  284. }
  285. }
  286. __decorate([
  287. serialize()
  288. ], DirectionalLight.prototype, "shadowFrustumSize", null);
  289. __decorate([
  290. serialize()
  291. ], DirectionalLight.prototype, "shadowOrthoScale", null);
  292. __decorate([
  293. serialize()
  294. ], DirectionalLight.prototype, "autoUpdateExtends", void 0);
  295. __decorate([
  296. serialize()
  297. ], DirectionalLight.prototype, "autoCalcShadowZBounds", void 0);
  298. __decorate([
  299. serialize("orthoLeft")
  300. ], DirectionalLight.prototype, "_orthoLeft", void 0);
  301. __decorate([
  302. serialize("orthoRight")
  303. ], DirectionalLight.prototype, "_orthoRight", void 0);
  304. __decorate([
  305. serialize("orthoTop")
  306. ], DirectionalLight.prototype, "_orthoTop", void 0);
  307. __decorate([
  308. serialize("orthoBottom")
  309. ], DirectionalLight.prototype, "_orthoBottom", void 0);
  310. //# sourceMappingURL=directionalLight.js.map