cascadedShadowGenerator.ts 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350
  1. import { SmartArray } from "../../Misc/smartArray";
  2. import { Nullable } from "../../types";
  3. import { Scene } from "../../scene";
  4. import { Matrix, Vector3 } from "../../Maths/math.vector";
  5. import { Color4 } from "../../Maths/math.color";
  6. import { VertexBuffer } from "../../Meshes/buffer";
  7. import { SubMesh } from "../../Meshes/subMesh";
  8. import { AbstractMesh } from "../../Meshes/abstractMesh";
  9. import { Mesh } from "../../Meshes/mesh";
  10. import { Material } from "../../Materials/material";
  11. import { MaterialHelper } from "../../Materials/materialHelper";
  12. import { Effect } from "../../Materials/effect";
  13. import { Texture } from "../../Materials/Textures/texture";
  14. import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture";
  15. import { _TimeToken } from "../../Instrumentation/timeToken";
  16. import { Constants } from "../../Engines/constants";
  17. import "../../Shaders/shadowMap.fragment";
  18. import "../../Shaders/shadowMap.vertex";
  19. import "../../Shaders/depthBoxBlur.fragment";
  20. import { Observable } from '../../Misc/observable';
  21. import { _DevTools } from '../../Misc/devTools';
  22. import { EffectFallbacks } from '../../Materials/effectFallbacks';
  23. import { IShadowGenerator } from './shadowGenerator';
  24. import { DirectionalLight } from '../directionalLight';
  25. import { BoundingSphere } from "../../Culling/boundingSphere";
  26. /**
  27. * A CSM implementation allowing casting shadows on large scenes.
  28. * Documentation : https://doc.babylonjs.com/babylon101/cascadedShadows
  29. * Based on: https://johanmedestrom.wordpress.com/2016/03/18/opengl-cascaded-shadow-maps/
  30. */
  31. export class CascadedShadowGenerator implements IShadowGenerator {
  32. /**
  33. * Defines the default number of cascades used by the CSM.
  34. */
  35. public static readonly DEFAULT_CASCADES_COUNT = 4;
  36. /**
  37. * Defines the minimum number of cascades used by the CSM.
  38. */
  39. public static readonly MIN_CASCADES_COUNT = 2;
  40. /**
  41. * Defines the maximum number of cascades used by the CSM.
  42. */
  43. public static readonly MAX_CASCADES_COUNT = 4;
  44. /**
  45. * Shadow generator mode None: no filtering applied.
  46. */
  47. public static readonly FILTER_NONE = 0;
  48. /**
  49. * Shadow generator mode PCF: Percentage Closer Filtering
  50. * benefits from Webgl 2 shadow samplers. Fallback to Poisson Sampling in Webgl 1
  51. * (https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch11.html)
  52. */
  53. public static readonly FILTER_PCF = 6;
  54. /**
  55. * Shadow generator mode PCSS: Percentage Closering Soft Shadow.
  56. * benefits from Webgl 2 shadow samplers. Fallback to Poisson Sampling in Webgl 1
  57. * Contact Hardening
  58. */
  59. public static readonly FILTER_PCSS = 7;
  60. /**
  61. * Reserved for PCF and PCSS
  62. * Highest Quality.
  63. *
  64. * Execute PCF on a 5*5 kernel improving a lot the shadow aliasing artifacts.
  65. *
  66. * Execute PCSS with 32 taps blocker search and 64 taps PCF.
  67. */
  68. public static readonly QUALITY_HIGH = 0;
  69. /**
  70. * Reserved for PCF and PCSS
  71. * Good tradeoff for quality/perf cross devices
  72. *
  73. * Execute PCF on a 3*3 kernel.
  74. *
  75. * Execute PCSS with 16 taps blocker search and 32 taps PCF.
  76. */
  77. public static readonly QUALITY_MEDIUM = 1;
  78. /**
  79. * Reserved for PCF and PCSS
  80. * The lowest quality but the fastest.
  81. *
  82. * Execute PCF on a 1*1 kernel.
  83. *
  84. * Execute PCSS with 16 taps blocker search and 16 taps PCF.
  85. */
  86. public static readonly QUALITY_LOW = 2;
  87. private static readonly _CLEARONE = new Color4(1.0, 1.0, 1.0, 1.0);
  88. /**
  89. * Observable triggered before the shadow is rendered. Can be used to update internal effect state
  90. */
  91. public onBeforeShadowMapRenderObservable = new Observable<Effect>();
  92. /**
  93. * Observable triggered after the shadow is rendered. Can be used to restore internal effect state
  94. */
  95. public onAfterShadowMapRenderObservable = new Observable<Effect>();
  96. /**
  97. * Observable triggered before a mesh is rendered in the shadow map.
  98. * Can be used to update internal effect state (that you can get from the onBeforeShadowMapRenderObservable)
  99. */
  100. public onBeforeShadowMapRenderMeshObservable = new Observable<Mesh>();
  101. /**
  102. * Observable triggered after a mesh is rendered in the shadow map.
  103. * Can be used to update internal effect state (that you can get from the onAfterShadowMapRenderObservable)
  104. */
  105. public onAfterShadowMapRenderMeshObservable = new Observable<Mesh>();
  106. private _bias = 0.00005;
  107. /**
  108. * Gets the bias: offset applied on the depth preventing acnea (in light direction).
  109. */
  110. public get bias(): number {
  111. return this._bias;
  112. }
  113. /**
  114. * Sets the bias: offset applied on the depth preventing acnea (in light direction).
  115. */
  116. public set bias(bias: number) {
  117. this._bias = bias;
  118. }
  119. private _normalBias = 0;
  120. /**
  121. * Gets the normalBias: offset applied on the depth preventing acnea (along side the normal direction and proportinal to the light/normal angle).
  122. */
  123. public get normalBias(): number {
  124. return this._normalBias;
  125. }
  126. /**
  127. * Sets the normalBias: offset applied on the depth preventing acnea (along side the normal direction and proportinal to the light/normal angle).
  128. */
  129. public set normalBias(normalBias: number) {
  130. this._normalBias = normalBias;
  131. }
  132. private _filter = CascadedShadowGenerator.FILTER_PCF;
  133. /**
  134. * Gets the current mode of the shadow generator (normal, PCF, PCSS...).
  135. * The returned value is a number equal to one of the available mode defined in ShadowMap.FILTER_x like _FILTER_NONE
  136. */
  137. public get filter(): number {
  138. return this._filter;
  139. }
  140. /**
  141. * Sets the current mode of the shadow generator (normal, PCF, PCSS...).
  142. * The returned value is a number equal to one of the available mode defined in ShadowMap.FILTER_x like _FILTER_NONE
  143. */
  144. public set filter(value: number) {
  145. if (this._filter === value) {
  146. return;
  147. }
  148. this._filter = value;
  149. this._applyFilterValues();
  150. this._light._markMeshesAsLightDirty();
  151. }
  152. /**
  153. * Gets if the current filter is set to "PCF" (percentage closer filtering).
  154. */
  155. public get usePercentageCloserFiltering(): boolean {
  156. return this.filter === CascadedShadowGenerator.FILTER_PCF;
  157. }
  158. /**
  159. * Sets the current filter to "PCF" (percentage closer filtering).
  160. */
  161. public set usePercentageCloserFiltering(value: boolean) {
  162. if (!value && this.filter !== CascadedShadowGenerator.FILTER_PCF) {
  163. return;
  164. }
  165. this.filter = (value ? CascadedShadowGenerator.FILTER_PCF : CascadedShadowGenerator.FILTER_NONE);
  166. }
  167. private _filteringQuality = CascadedShadowGenerator.QUALITY_HIGH;
  168. /**
  169. * Gets the PCF or PCSS Quality.
  170. * Only valid if usePercentageCloserFiltering or usePercentageCloserFiltering is true.
  171. */
  172. public get filteringQuality(): number {
  173. return this._filteringQuality;
  174. }
  175. /**
  176. * Sets the PCF or PCSS Quality.
  177. * Only valid if usePercentageCloserFiltering or usePercentageCloserFiltering is true.
  178. */
  179. public set filteringQuality(filteringQuality: number) {
  180. if (this._filteringQuality === filteringQuality) {
  181. return;
  182. }
  183. this._filteringQuality = filteringQuality;
  184. this._applyFilterValues();
  185. this._light._markMeshesAsLightDirty();
  186. }
  187. /**
  188. * Gets if the current filter is set to "PCSS" (contact hardening).
  189. */
  190. public get useContactHardeningShadow(): boolean {
  191. return this.filter === CascadedShadowGenerator.FILTER_PCSS;
  192. }
  193. /**
  194. * Sets the current filter to "PCSS" (contact hardening).
  195. */
  196. public set useContactHardeningShadow(value: boolean) {
  197. if (!value && this.filter !== CascadedShadowGenerator.FILTER_PCSS) {
  198. return;
  199. }
  200. this.filter = (value ? CascadedShadowGenerator.FILTER_PCSS : CascadedShadowGenerator.FILTER_NONE);
  201. }
  202. private _contactHardeningLightSizeUVRatio = 0.1;
  203. /**
  204. * Gets the Light Size (in shadow map uv unit) used in PCSS to determine the blocker search area and the penumbra size.
  205. * Using a ratio helps keeping shape stability independently of the map size.
  206. *
  207. * It does not account for the light projection as it was having too much
  208. * instability during the light setup or during light position changes.
  209. *
  210. * Only valid if useContactHardeningShadow is true.
  211. */
  212. public get contactHardeningLightSizeUVRatio(): number {
  213. return this._contactHardeningLightSizeUVRatio;
  214. }
  215. /**
  216. * Sets the Light Size (in shadow map uv unit) used in PCSS to determine the blocker search area and the penumbra size.
  217. * Using a ratio helps keeping shape stability independently of the map size.
  218. *
  219. * It does not account for the light projection as it was having too much
  220. * instability during the light setup or during light position changes.
  221. *
  222. * Only valid if useContactHardeningShadow is true.
  223. */
  224. public set contactHardeningLightSizeUVRatio(contactHardeningLightSizeUVRatio: number) {
  225. this._contactHardeningLightSizeUVRatio = contactHardeningLightSizeUVRatio;
  226. }
  227. private _darkness = 0;
  228. /** Gets or sets the actual darkness of a shadow */
  229. public get darkness() {
  230. return this._darkness;
  231. }
  232. public set darkness(value: number) {
  233. this.setDarkness(value);
  234. }
  235. /**
  236. * Returns the darkness value (float). This can only decrease the actual darkness of a shadow.
  237. * 0 means strongest and 1 would means no shadow.
  238. * @returns the darkness.
  239. */
  240. public getDarkness(): number {
  241. return this._darkness;
  242. }
  243. /**
  244. * Sets the darkness value (float). This can only decrease the actual darkness of a shadow.
  245. * @param darkness The darkness value 0 means strongest and 1 would means no shadow.
  246. * @returns the shadow generator allowing fluent coding.
  247. */
  248. public setDarkness(darkness: number): CascadedShadowGenerator {
  249. if (darkness >= 1.0) {
  250. this._darkness = 1.0;
  251. }
  252. else if (darkness <= 0.0) {
  253. this._darkness = 0.0;
  254. }
  255. else {
  256. this._darkness = darkness;
  257. }
  258. return this;
  259. }
  260. private _transparencyShadow = false;
  261. /** Gets or sets the ability to have transparent shadow */
  262. public get transparencyShadow() {
  263. return this._transparencyShadow;
  264. }
  265. public set transparencyShadow(value: boolean) {
  266. this.setTransparencyShadow(value);
  267. }
  268. /**
  269. * Sets the ability to have transparent shadow (boolean).
  270. * @param transparent True if transparent else False
  271. * @returns the shadow generator allowing fluent coding
  272. */
  273. public setTransparencyShadow(transparent: boolean): CascadedShadowGenerator {
  274. this._transparencyShadow = transparent;
  275. return this;
  276. }
  277. private _cascades = CascadedShadowGenerator.DEFAULT_CASCADES_COUNT;
  278. /**
  279. * Gets or set the number of cascades used by the CSM.
  280. */
  281. public get cascades(): number {
  282. return this._cascades;
  283. }
  284. public set cascades(value: number) {
  285. value = Math.min(Math.max(value, CascadedShadowGenerator.MIN_CASCADES_COUNT), CascadedShadowGenerator.MAX_CASCADES_COUNT);
  286. if (value === this._cascades) {
  287. return;
  288. }
  289. this._cascades = value;
  290. this.recreateShadowMap();
  291. }
  292. private _shadowMap: Nullable<RenderTargetTexture>;
  293. /**
  294. * Gets the main RTT containing the shadow map (usually storing depth from the light point of view).
  295. * @returns The render target texture if present otherwise, null
  296. */
  297. public getShadowMap(): Nullable<RenderTargetTexture> {
  298. return this._shadowMap;
  299. }
  300. /**
  301. * Gets the class name of that object
  302. * @returns "ShadowGenerator"
  303. */
  304. public getClassName(): string {
  305. return "CascadedShadowGenerator";
  306. }
  307. /**
  308. * Helper function to add a mesh and its descendants to the list of shadow casters.
  309. * @param mesh Mesh to add
  310. * @param includeDescendants boolean indicating if the descendants should be added. Default to true
  311. * @returns the Shadow Generator itself
  312. */
  313. public addShadowCaster(mesh: AbstractMesh, includeDescendants = true): CascadedShadowGenerator {
  314. if (!this._shadowMap) {
  315. return this;
  316. }
  317. if (!this._shadowMap.renderList) {
  318. this._shadowMap.renderList = [];
  319. }
  320. this._shadowMap.renderList.push(mesh);
  321. if (includeDescendants) {
  322. this._shadowMap.renderList.push(...mesh.getChildMeshes());
  323. }
  324. return this;
  325. }
  326. /**
  327. * Helper function to remove a mesh and its descendants from the list of shadow casters
  328. * @param mesh Mesh to remove
  329. * @param includeDescendants boolean indicating if the descendants should be removed. Default to true
  330. * @returns the Shadow Generator itself
  331. */
  332. public removeShadowCaster(mesh: AbstractMesh, includeDescendants = true): CascadedShadowGenerator {
  333. if (!this._shadowMap || !this._shadowMap.renderList) {
  334. return this;
  335. }
  336. var index = this._shadowMap.renderList.indexOf(mesh);
  337. if (index !== -1) {
  338. this._shadowMap.renderList.splice(index, 1);
  339. }
  340. if (includeDescendants) {
  341. for (var child of mesh.getChildren()) {
  342. this.removeShadowCaster(<any>child);
  343. }
  344. }
  345. return this;
  346. }
  347. /**
  348. * Controls the extent to which the shadows fade out at the edge of the frustum
  349. * Used only by directionals and spots
  350. */
  351. public frustumEdgeFalloff = 0;
  352. private _light: DirectionalLight;
  353. /**
  354. * Returns the associated light object.
  355. * @returns the light generating the shadow
  356. */
  357. public getLight(): DirectionalLight {
  358. return this._light;
  359. }
  360. /**
  361. * If true the shadow map is generated by rendering the back face of the mesh instead of the front face.
  362. * This can help with self-shadowing as the geometry making up the back of objects is slightly offset.
  363. * It might on the other hand introduce peter panning.
  364. */
  365. public forceBackFacesOnly = false;
  366. private _scene: Scene;
  367. private _lightDirection = Vector3.Zero();
  368. private _effect: Effect;
  369. private _viewMatrix: Array<Matrix>;
  370. private _cachedPosition: Vector3 = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  371. private _cachedDirection: Vector3 = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  372. private _cachedDefines: string;
  373. private _currentRenderID: Array<number>;
  374. private _mapSize: number;
  375. private _currentLayer = 0;
  376. private _textureType: number;
  377. private _defaultTextureMatrix = Matrix.Identity();
  378. private _storedUniqueId: Nullable<number>;
  379. private _viewSpaceFrustums: Array<Array<Vector3>>;
  380. private _viewSpaceFrustumsZ: Array<number>;
  381. private _viewSpaceBoundingSpheres: Array<BoundingSphere>;
  382. private _transformMatrices: Array<Matrix>;
  383. private _transformMatricesAsArray: Float32Array;
  384. private _frustumLength: number;
  385. /**
  386. * Gets the csmFrustumLength value: furthest range of the frustum for the CSM mode.
  387. */
  388. public get frustumLength(): number {
  389. if (!this._scene || !this._scene.activeCamera) {
  390. return 0;
  391. }
  392. return this._frustumLength;
  393. }
  394. /**
  395. * Sets the csmFrustumLength: furthest range of the frustum for the CSM mode.
  396. */
  397. public set frustumLength(value: number) {
  398. if (!this._scene || !this._scene.activeCamera) {
  399. this._frustumLength = value;
  400. return;
  401. }
  402. if (this._frustumLength === value || value < this._scene.activeCamera.minZ || value > this._scene.activeCamera.maxZ) {
  403. return;
  404. }
  405. this._frustumLength = value;
  406. this._initCascades();
  407. }
  408. protected _debug = false;
  409. public get debug(): boolean {
  410. return this._debug;
  411. }
  412. public set debug(dbg: boolean) {
  413. this._debug = dbg;
  414. this._light._markMeshesAsLightDirty();
  415. }
  416. public depthClamp: boolean = false;
  417. public splitBlendPercentage: number = 0.1;
  418. private _lambda = 0.5;
  419. /**
  420. * Gets csmLambda: parameter used for calculating the frustum in CSM.
  421. */
  422. public get lambda(): number {
  423. return this._lambda;
  424. }
  425. /**
  426. * Sets csmLambda: parameter used for calculating the frustum in CSM.
  427. */
  428. public set lambda(value: number) {
  429. const lambda = Math.min(Math.max(value, 0), 1);
  430. if (this._lambda == lambda) {
  431. return;
  432. }
  433. this._lambda = lambda;
  434. this._initCascades();
  435. }
  436. private _initCascades(): void {
  437. let camera = this._scene.activeCamera;
  438. if (!camera) {
  439. return;
  440. }
  441. if (!this._frustumLength) {
  442. this._frustumLength = camera.maxZ;
  443. }
  444. this._currentRenderID = new Array(this._cascades);
  445. // inits and sets all static params related to CSM
  446. let engine = this._scene.getEngine();
  447. this._viewSpaceFrustums = [];
  448. this._viewSpaceFrustumsZ = [];
  449. this._viewSpaceBoundingSpheres = [];
  450. let nearx = 0;
  451. let neary = 0;
  452. let farx = 0;
  453. let fary = 0;
  454. // get all internal camera cascaded frustum points
  455. let breaks = this._frustumSplit(this.cascades, camera.minZ, this._frustumLength, this._lambda);
  456. if (camera.fovMode === 0) {
  457. nearx = camera.minZ * Math.tan(camera.fov / 2) * engine.getAspectRatio(camera);
  458. neary = camera.minZ * Math.tan(camera.fov / 2);
  459. farx = this._frustumLength * Math.tan(camera.fov / 2) * engine.getAspectRatio(camera);
  460. fary = this._frustumLength * Math.tan(camera.fov / 2);
  461. } else if (camera.fovMode === 1) {
  462. nearx = camera.minZ * Math.tan(camera.fov / 2);
  463. neary = camera.minZ * Math.tan(camera.fov / 2) * engine.getAspectRatio(camera);
  464. farx = this._frustumLength * Math.tan(camera.fov / 2);
  465. fary = this._frustumLength * Math.tan(camera.fov / 2) * engine.getAspectRatio(camera);
  466. }
  467. // populate the viewSpaceFrustums array
  468. for (let i = 0; i < this.cascades + 1; i++) {
  469. this._viewSpaceFrustums[i] = [];
  470. this._viewSpaceFrustums[i].push(Vector3.Lerp(new Vector3(nearx, neary, camera.minZ), new Vector3(farx, fary, this._frustumLength), breaks[i]));
  471. this._viewSpaceFrustums[i].push(Vector3.Lerp(new Vector3(nearx, -neary, camera.minZ), new Vector3(farx, -fary, this._frustumLength), breaks[i]));
  472. this._viewSpaceFrustums[i].push(Vector3.Lerp(new Vector3(-nearx, -neary, camera.minZ), new Vector3(-farx, -fary, this._frustumLength), breaks[i]));
  473. this._viewSpaceFrustums[i].push(Vector3.Lerp(new Vector3(-nearx, neary, camera.minZ), new Vector3(-farx, fary, this._frustumLength), breaks[i]));
  474. }
  475. // populate the viewSpaceBoundingSpheres array
  476. var minX = Number.MAX_VALUE;
  477. var minY = Number.MAX_VALUE;
  478. var minZ = Number.MAX_VALUE;
  479. var maxX = Number.MIN_VALUE;
  480. var maxY = Number.MIN_VALUE;
  481. var maxZ = Number.MIN_VALUE;
  482. for (let i = 0; i < this.cascades; i++) {
  483. for (let j = 0; j < this._viewSpaceFrustums[i].length; j++) {
  484. minX = Math.min(minX, this._viewSpaceFrustums[i][j].x);
  485. minY = Math.min(minY, this._viewSpaceFrustums[i][j].y);
  486. minZ = Math.min(minZ, this._viewSpaceFrustums[i][j].z);
  487. maxX = Math.max(maxX, this._viewSpaceFrustums[i][j].x);
  488. maxY = Math.max(maxY, this._viewSpaceFrustums[i][j].y);
  489. maxZ = Math.max(maxZ, this._viewSpaceFrustums[i][j].z);
  490. minX = Math.min(minX, this._viewSpaceFrustums[i + 1][j].x);
  491. minY = Math.min(minY, this._viewSpaceFrustums[i + 1][j].y);
  492. minZ = Math.min(minZ, this._viewSpaceFrustums[i + 1][j].z);
  493. maxX = Math.max(maxX, this._viewSpaceFrustums[i + 1][j].x);
  494. maxY = Math.max(maxY, this._viewSpaceFrustums[i + 1][j].y);
  495. maxZ = Math.max(maxZ, this._viewSpaceFrustums[i + 1][j].z);
  496. }
  497. let bs = new BoundingSphere(new Vector3(minX, minY, minZ), new Vector3(maxX, maxY, maxZ));
  498. this._viewSpaceBoundingSpheres.push(bs);
  499. this._viewSpaceFrustumsZ[i] = this._viewSpaceFrustums[i + 1][0].z;
  500. }
  501. // initialize the CSM transformMatrices
  502. this._viewMatrix = [];
  503. this._transformMatrices = [];
  504. for (let index = 0; index < this.cascades; index++) {
  505. this._viewMatrix[index] = Matrix.Zero();
  506. this._transformMatrices[index] = Matrix.Zero();
  507. }
  508. this._transformMatricesAsArray = new Float32Array(this.cascades * 16);
  509. }
  510. private _uniformSplit(amount: number, near: number, far: number): Array<number> {
  511. let r = [];
  512. for (let i = 1; i < amount; i++) {
  513. r.push((near + (far - near) * i / amount) / far);
  514. }
  515. r.push(1);
  516. return r;
  517. }
  518. private _logarithmicSplit(amount: number, near: number, far: number): Array<number> {
  519. let r = [];
  520. for (let i = 1; i < amount; i++) {
  521. r.push((near * (far / near) ** (i / amount)) / far);
  522. }
  523. r.push(1);
  524. return r;
  525. }
  526. private _frustumSplit(amount: number, near: number, far: number, lambda: number): Array<number> {
  527. let log = this._logarithmicSplit(amount, near, far);
  528. let uni = this._uniformSplit(amount, near, far);
  529. let r = [];
  530. r.push(0);
  531. for (let i = 1; i < amount; i++) {
  532. r.push(lambda * log[i - 1] + (1 - lambda) * uni[i - 1]);
  533. }
  534. r.push(1);
  535. return r;
  536. }
  537. /**
  538. * Gets the CSM transformation matrix used to project the meshes into the map from the light point of view.
  539. * (eq to shadow prjection matrix * light transform matrix)
  540. * @param mapIndex index number of the cascaded shadow map
  541. * @returns The transform matrix used to create the CSM shadow map
  542. */
  543. public getCSMTransformMatrix(mapIndex: number): Matrix {
  544. var scene = this._scene;
  545. if (this._currentRenderID[mapIndex] === scene.getRenderId()) {
  546. return this._transformMatrices[mapIndex];
  547. }
  548. let camera = scene.activeCamera;
  549. if (!camera) {
  550. return this._transformMatrices[mapIndex];
  551. }
  552. this._currentRenderID[mapIndex] = scene.getRenderId();
  553. var lightPosition = this._light.position;
  554. if (this._light.computeTransformedInformation()) {
  555. lightPosition = this._light.transformedPosition;
  556. }
  557. Vector3.NormalizeToRef(this._light.getShadowDirection(this._currentLayer), this._lightDirection);
  558. if (Math.abs(Vector3.Dot(this._lightDirection, Vector3.Up())) === 1.0) {
  559. this._lightDirection.z = 0.0000000000001; // Required to avoid perfectly perpendicular light
  560. }
  561. if (this._light.needProjectionMatrixCompute() || !this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !this._lightDirection.equals(this._cachedDirection)) {
  562. this._cachedPosition.copyFrom(lightPosition);
  563. this._cachedDirection.copyFrom(this._lightDirection);
  564. // get bounding sphere of current cascade
  565. let bs = new BoundingSphere(this._viewSpaceBoundingSpheres[mapIndex].minimum, this._viewSpaceBoundingSpheres[mapIndex].maximum, camera.getWorldMatrix());
  566. // get view matrix
  567. let shadowCamPos = bs.centerWorld.subtract(this._lightDirection.scale(bs.radius));
  568. Matrix.LookAtLHToRef(shadowCamPos, bs.centerWorld, Vector3.Up(), this._viewMatrix[mapIndex]);
  569. // get ortho matrix
  570. let OrthoMatrix = Matrix.OrthoLH(2.0 * bs.radius, 2.0 * bs.radius, 0, 2.0 * bs.radius);
  571. // get projection matrix
  572. this._viewMatrix[mapIndex].multiplyToRef(OrthoMatrix, this._transformMatrices[mapIndex]);
  573. // rounding the transform matrix to prevent shimmering artifacts from camera movement
  574. let shadowOrigin = Vector3.TransformCoordinates(Vector3.Zero(), this._transformMatrices[mapIndex]);
  575. shadowOrigin = shadowOrigin.scale(this._mapSize / 2.0);
  576. let roundedOrigin = new Vector3(Math.ceil(shadowOrigin.x), Math.ceil(shadowOrigin.y), Math.ceil(shadowOrigin.z));
  577. let roundOffset = roundedOrigin.subtract(shadowOrigin);
  578. roundOffset = roundOffset.scale(2.0 / this._mapSize);
  579. let roundMatrix = Matrix.Translation(roundOffset.x, roundOffset.y, 0.0);
  580. this._transformMatrices[mapIndex].multiplyToRef(roundMatrix, this._transformMatrices[mapIndex]);
  581. }
  582. this._transformMatrices[mapIndex].copyToArray(this._transformMatricesAsArray, mapIndex * 16);
  583. return this._transformMatrices[mapIndex];
  584. }
  585. /** @hidden */
  586. public static _SceneComponentInitialization: (scene: Scene) => void = (_) => {
  587. throw _DevTools.WarnImport("ShadowGeneratorSceneComponent");
  588. }
  589. /**
  590. * Creates a Cascaded Shadow Generator object.
  591. * A ShadowGenerator is the required tool to use the shadows.
  592. * Each directional light casting shadows needs to use its own ShadowGenerator.
  593. * Documentation : https://doc.babylonjs.com/babylon101/cascadedShadows
  594. * @param mapSize The size of the texture what stores the shadows. Example : 1024.
  595. * @param light The directional light object generating the shadows.
  596. * @param usefulFloatFirst By default the generator will try to use half float textures but if you need precision (for self shadowing for instance), you can use this option to enforce full float texture.
  597. */
  598. constructor(mapSize: number, light: DirectionalLight, usefulFloatFirst?: boolean) {
  599. this._scene = light.getScene();
  600. if (this._scene.getEngine().webGLVersion == 1) {
  601. throw "CSM can only be used in WebGL2";
  602. }
  603. this._light = light;
  604. this._mapSize = mapSize;
  605. light._shadowGenerator = this;
  606. this.frustumLength = this._scene.activeCamera?.maxZ ?? 10000;
  607. CascadedShadowGenerator._SceneComponentInitialization(this._scene);
  608. // Texture type fallback from float to int if not supported.
  609. var caps = this._scene.getEngine().getCaps();
  610. if (!usefulFloatFirst) {
  611. if (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering) {
  612. this._textureType = Constants.TEXTURETYPE_HALF_FLOAT;
  613. }
  614. else if (caps.textureFloatRender && caps.textureFloatLinearFiltering) {
  615. this._textureType = Constants.TEXTURETYPE_FLOAT;
  616. }
  617. else {
  618. this._textureType = Constants.TEXTURETYPE_UNSIGNED_INT;
  619. }
  620. } else {
  621. if (caps.textureFloatRender && caps.textureFloatLinearFiltering) {
  622. this._textureType = Constants.TEXTURETYPE_FLOAT;
  623. }
  624. else if (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering) {
  625. this._textureType = Constants.TEXTURETYPE_HALF_FLOAT;
  626. }
  627. else {
  628. this._textureType = Constants.TEXTURETYPE_UNSIGNED_INT;
  629. }
  630. }
  631. this._initializeGenerator();
  632. this._applyFilterValues();
  633. }
  634. private _initializeGenerator(): void {
  635. this._light._markMeshesAsLightDirty();
  636. this._initializeShadowMap();
  637. }
  638. private _initializeShadowMap(): void {
  639. // Render target
  640. let engine = this._scene.getEngine();
  641. const size = { width: this._mapSize, height: this._mapSize, layers: this.cascades };
  642. this._shadowMap = new RenderTargetTexture(this._light.name + "_shadowMap", size, this._scene, false, true, this._textureType, false, undefined, false, false);
  643. this._shadowMap.createDepthStencilTexture(Constants.LESS, true);
  644. this._shadowMap.wrapU = Texture.CLAMP_ADDRESSMODE;
  645. this._shadowMap.wrapV = Texture.CLAMP_ADDRESSMODE;
  646. this._shadowMap.anisotropicFilteringLevel = 4;
  647. this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  648. this._shadowMap.renderParticles = false;
  649. this._shadowMap.ignoreCameraViewport = true;
  650. if (this._storedUniqueId) {
  651. this._shadowMap.uniqueId = this._storedUniqueId;
  652. }
  653. // Record Face Index before render.
  654. this._shadowMap.onBeforeRenderObservable.add((layer: number) => {
  655. this._currentLayer = layer;
  656. if (this._filter === CascadedShadowGenerator.FILTER_PCF) {
  657. engine.setColorWrite(this.debug);
  658. }
  659. });
  660. // Custom render function.
  661. this._shadowMap.customRenderFunction = this._renderForShadowMap.bind(this);
  662. // Restore state after bind.
  663. this._shadowMap.onAfterUnbindObservable.add(() => {
  664. if (this._filter === CascadedShadowGenerator.FILTER_PCF) {
  665. engine.setColorWrite(true);
  666. }
  667. });
  668. // Clear according to the chosen filter.
  669. this._shadowMap.onClearObservable.add((engine) => {
  670. if (this._filter === CascadedShadowGenerator.FILTER_PCF) {
  671. engine.clear(CascadedShadowGenerator._CLEARONE, this.debug, true, false);
  672. }
  673. else {
  674. engine.clear(CascadedShadowGenerator._CLEARONE, true, true, false);
  675. }
  676. });
  677. // Recreate on resize.
  678. this._shadowMap.onResizeObservable.add((RTT) => {
  679. this._storedUniqueId = this._shadowMap!.uniqueId;
  680. this._mapSize = RTT.getRenderSize();
  681. this._light._markMeshesAsLightDirty();
  682. this.recreateShadowMap();
  683. });
  684. }
  685. private _renderForShadowMap(opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>): void {
  686. var index: number;
  687. let engine = this._scene.getEngine();
  688. const colorWrite = engine.getColorWrite();
  689. if (depthOnlySubMeshes.length) {
  690. engine.setColorWrite(false);
  691. for (index = 0; index < depthOnlySubMeshes.length; index++) {
  692. this._renderSubMeshForShadowMap(depthOnlySubMeshes.data[index]);
  693. }
  694. engine.setColorWrite(colorWrite);
  695. }
  696. for (index = 0; index < opaqueSubMeshes.length; index++) {
  697. this._renderSubMeshForShadowMap(opaqueSubMeshes.data[index]);
  698. }
  699. for (index = 0; index < alphaTestSubMeshes.length; index++) {
  700. this._renderSubMeshForShadowMap(alphaTestSubMeshes.data[index]);
  701. }
  702. if (this._transparencyShadow) {
  703. for (index = 0; index < transparentSubMeshes.length; index++) {
  704. this._renderSubMeshForShadowMap(transparentSubMeshes.data[index]);
  705. }
  706. }
  707. }
  708. private _renderSubMeshForShadowMap(subMesh: SubMesh): void {
  709. var mesh = subMesh.getRenderingMesh();
  710. var scene = this._scene;
  711. var engine = scene.getEngine();
  712. let material = subMesh.getMaterial();
  713. mesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
  714. if (!material || subMesh.verticesCount === 0) {
  715. return;
  716. }
  717. // Culling
  718. engine.setState(material.backFaceCulling);
  719. // Managing instances
  720. var batch = mesh._getInstancesRenderList(subMesh._id);
  721. if (batch.mustReturn) {
  722. return;
  723. }
  724. var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
  725. if (this.isReady(subMesh, hardwareInstancedRendering)) {
  726. engine.enableEffect(this._effect);
  727. mesh._bind(subMesh, this._effect, Material.TriangleFillMode);
  728. this._effect.setFloat3("biasAndScale", this.bias, this.normalBias, 0);
  729. this._effect.setMatrix("viewProjection", this.getCSMTransformMatrix(this._currentLayer));
  730. this._effect.setVector3("lightData", this._cachedDirection);
  731. if (scene.activeCamera) {
  732. this._effect.setFloat2("depthValues", this.getLight().getDepthMinZ(scene.activeCamera), this.getLight().getDepthMinZ(scene.activeCamera) + this.getLight().getDepthMaxZ(scene.activeCamera));
  733. }
  734. // Alpha test
  735. if (material && material.needAlphaTesting()) {
  736. var alphaTexture = material.getAlphaTestTexture();
  737. if (alphaTexture) {
  738. this._effect.setTexture("diffuseSampler", alphaTexture);
  739. this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix() || this._defaultTextureMatrix);
  740. }
  741. }
  742. // Bones
  743. if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
  744. const skeleton = mesh.skeleton;
  745. if (skeleton.isUsingTextureForMatrices) {
  746. const boneTexture = skeleton.getTransformMatrixTexture(mesh);
  747. if (!boneTexture) {
  748. return;
  749. }
  750. this._effect.setTexture("boneSampler", boneTexture);
  751. this._effect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
  752. } else {
  753. this._effect.setMatrices("mBones", skeleton.getTransformMatrices((mesh)));
  754. }
  755. }
  756. // Morph targets
  757. MaterialHelper.BindMorphTargetParameters(mesh, this._effect);
  758. if (this.forceBackFacesOnly) {
  759. engine.setState(true, 0, false, true);
  760. }
  761. // Observables
  762. this.onBeforeShadowMapRenderMeshObservable.notifyObservers(mesh);
  763. this.onBeforeShadowMapRenderObservable.notifyObservers(this._effect);
  764. // Draw
  765. mesh._processRendering(subMesh, this._effect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
  766. (isInstance, world) => this._effect.setMatrix("world", world));
  767. if (this.forceBackFacesOnly) {
  768. engine.setState(true, 0, false, false);
  769. }
  770. // Observables
  771. this.onAfterShadowMapRenderObservable.notifyObservers(this._effect);
  772. this.onAfterShadowMapRenderMeshObservable.notifyObservers(mesh);
  773. } else {
  774. // Need to reset refresh rate of the shadowMap
  775. if (this._shadowMap) {
  776. this._shadowMap.resetRefreshCounter();
  777. }
  778. }
  779. }
  780. private _applyFilterValues(): void {
  781. if (!this._shadowMap) {
  782. return;
  783. }
  784. if (this.filter === CascadedShadowGenerator.FILTER_PCSS) {
  785. this._shadowMap.updateSamplingMode(Texture.NEAREST_SAMPLINGMODE);
  786. } else {
  787. this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  788. }
  789. }
  790. /**
  791. * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
  792. * @param onCompiled Callback triggered at the and of the effects compilation
  793. * @param options Sets of optional options forcing the compilation with different modes
  794. */
  795. public forceCompilation(onCompiled?: (generator: IShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void {
  796. let localOptions = {
  797. useInstances: false,
  798. ...options
  799. };
  800. let shadowMap = this.getShadowMap();
  801. if (!shadowMap) {
  802. if (onCompiled) {
  803. onCompiled(this);
  804. }
  805. return;
  806. }
  807. let renderList = shadowMap.renderList;
  808. if (!renderList) {
  809. if (onCompiled) {
  810. onCompiled(this);
  811. }
  812. return;
  813. }
  814. var subMeshes = new Array<SubMesh>();
  815. for (var mesh of renderList) {
  816. subMeshes.push(...mesh.subMeshes);
  817. }
  818. if (subMeshes.length === 0) {
  819. if (onCompiled) {
  820. onCompiled(this);
  821. }
  822. return;
  823. }
  824. var currentIndex = 0;
  825. var checkReady = () => {
  826. if (!this._scene || !this._scene.getEngine()) {
  827. return;
  828. }
  829. while (this.isReady(subMeshes[currentIndex], localOptions.useInstances)) {
  830. currentIndex++;
  831. if (currentIndex >= subMeshes.length) {
  832. if (onCompiled) {
  833. onCompiled(this);
  834. }
  835. return;
  836. }
  837. }
  838. setTimeout(checkReady, 16);
  839. };
  840. checkReady();
  841. }
  842. /**
  843. * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
  844. * @param options Sets of optional options forcing the compilation with different modes
  845. * @returns A promise that resolves when the compilation completes
  846. */
  847. public forceCompilationAsync(options?: Partial<{ useInstances: boolean }>): Promise<void> {
  848. return new Promise((resolve) => {
  849. this.forceCompilation(() => {
  850. resolve();
  851. }, options);
  852. });
  853. }
  854. /**
  855. * Determine wheter the shadow generator is ready or not (mainly all effects and related post processes needs to be ready).
  856. * @param subMesh The submesh we want to render in the shadow map
  857. * @param useInstances Defines wether will draw in the map using instances
  858. * @returns true if ready otherwise, false
  859. */
  860. public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
  861. var defines = [];
  862. if (this._textureType !== Constants.TEXTURETYPE_UNSIGNED_INT) {
  863. defines.push("#define FLOAT");
  864. }
  865. defines.push("#define DEPTHTEXTURE");
  866. if (this.depthClamp) {
  867. defines.push("#define DEPTHCLAMP");
  868. }
  869. var attribs = [VertexBuffer.PositionKind];
  870. var mesh = subMesh.getMesh();
  871. var material = subMesh.getMaterial();
  872. // Normal bias.
  873. if (this.normalBias && mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
  874. attribs.push(VertexBuffer.NormalKind);
  875. defines.push("#define NORMAL");
  876. defines.push("#define DIRECTIONINLIGHTDATA");
  877. }
  878. // Alpha test
  879. if (material && material.needAlphaTesting()) {
  880. var alphaTexture = material.getAlphaTestTexture();
  881. if (alphaTexture) {
  882. defines.push("#define ALPHATEST");
  883. if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  884. attribs.push(VertexBuffer.UVKind);
  885. defines.push("#define UV1");
  886. }
  887. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
  888. if (alphaTexture.coordinatesIndex === 1) {
  889. attribs.push(VertexBuffer.UV2Kind);
  890. defines.push("#define UV2");
  891. }
  892. }
  893. }
  894. }
  895. // Bones
  896. const fallbacks = new EffectFallbacks();
  897. if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
  898. attribs.push(VertexBuffer.MatricesIndicesKind);
  899. attribs.push(VertexBuffer.MatricesWeightsKind);
  900. if (mesh.numBoneInfluencers > 4) {
  901. attribs.push(VertexBuffer.MatricesIndicesExtraKind);
  902. attribs.push(VertexBuffer.MatricesWeightsExtraKind);
  903. }
  904. const skeleton = mesh.skeleton;
  905. defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
  906. if (mesh.numBoneInfluencers > 0) {
  907. fallbacks.addCPUSkinningFallback(0, mesh);
  908. }
  909. if (skeleton.isUsingTextureForMatrices) {
  910. defines.push("#define BONETEXTURE");
  911. } else {
  912. defines.push("#define BonesPerMesh " + (skeleton.bones.length + 1));
  913. }
  914. } else {
  915. defines.push("#define NUM_BONE_INFLUENCERS 0");
  916. }
  917. // Morph targets
  918. var manager = (<Mesh>mesh).morphTargetManager;
  919. let morphInfluencers = 0;
  920. if (manager) {
  921. if (manager.numInfluencers > 0) {
  922. defines.push("#define MORPHTARGETS");
  923. morphInfluencers = manager.numInfluencers;
  924. defines.push("#define NUM_MORPH_INFLUENCERS " + morphInfluencers);
  925. MaterialHelper.PrepareAttributesForMorphTargetsInfluencers(attribs, mesh, morphInfluencers);
  926. }
  927. }
  928. // Instances
  929. if (useInstances) {
  930. defines.push("#define INSTANCES");
  931. MaterialHelper.PushAttributesForInstances(attribs);
  932. }
  933. // Get correct effect
  934. var join = defines.join("\n");
  935. if (this._cachedDefines !== join) {
  936. this._cachedDefines = join;
  937. let shaderName = "shadowMap";
  938. let uniforms = ["world", "mBones", "viewProjection", "diffuseMatrix", "lightData", "depthValues", "biasAndScale", "morphTargetInfluences", "boneTextureWidth"];
  939. let samplers = ["diffuseSampler", "boneSampler"];
  940. this._effect = this._scene.getEngine().createEffect(shaderName,
  941. attribs, uniforms,
  942. samplers, join,
  943. fallbacks, undefined, undefined, { maxSimultaneousMorphTargets: morphInfluencers });
  944. }
  945. if (!this._effect.isReady()) {
  946. return false;
  947. }
  948. return true;
  949. }
  950. /**
  951. * Prepare all the defines in a material relying on a shadow map at the specified light index.
  952. * @param defines Defines of the material we want to update
  953. * @param lightIndex Index of the light in the enabled light list of the material
  954. */
  955. public prepareDefines(defines: any, lightIndex: number): void {
  956. var scene = this._scene;
  957. var light = this._light;
  958. if (!scene.shadowsEnabled || !light.shadowEnabled) {
  959. return;
  960. }
  961. defines["SHADOW" + lightIndex] = true;
  962. defines["SHADOWCSM" + lightIndex] = true;
  963. defines["SHADOWCSMDEBUG" + lightIndex] = this.debug;
  964. defines["SHADOWCSMNUM_CASCADES" + lightIndex] = this.cascades;
  965. if (this.useContactHardeningShadow) {
  966. defines["SHADOWPCSS" + lightIndex] = true;
  967. if (this._filteringQuality === CascadedShadowGenerator.QUALITY_LOW) {
  968. defines["SHADOWLOWQUALITY" + lightIndex] = true;
  969. }
  970. else if (this._filteringQuality === CascadedShadowGenerator.QUALITY_MEDIUM) {
  971. defines["SHADOWMEDIUMQUALITY" + lightIndex] = true;
  972. }
  973. // else default to high.
  974. }
  975. else if (this.usePercentageCloserFiltering) {
  976. defines["SHADOWPCF" + lightIndex] = true;
  977. if (this._filteringQuality === CascadedShadowGenerator.QUALITY_LOW) {
  978. defines["SHADOWLOWQUALITY" + lightIndex] = true;
  979. }
  980. else if (this._filteringQuality === CascadedShadowGenerator.QUALITY_MEDIUM) {
  981. defines["SHADOWMEDIUMQUALITY" + lightIndex] = true;
  982. }
  983. // else default to high.
  984. }
  985. }
  986. /**
  987. * Binds the shadow related information inside of an effect (information like near, far, darkness...
  988. * defined in the generator but impacting the effect).
  989. * @param lightIndex Index of the light in the enabled light list of the material owning the effect
  990. * @param effect The effect we are binfing the information for
  991. */
  992. public bindShadowLight(lightIndex: string, effect: Effect): void {
  993. const light = this._light;
  994. const scene = this._scene;
  995. if (!scene.shadowsEnabled || !light.shadowEnabled) {
  996. return;
  997. }
  998. const camera = scene.activeCamera;
  999. if (!camera) {
  1000. return;
  1001. }
  1002. const shadowMap = this.getShadowMap();
  1003. if (!shadowMap) {
  1004. return;
  1005. }
  1006. const width = shadowMap.getSize().width;
  1007. effect.setFloat("splitBlendFactor" + lightIndex, this.splitBlendPercentage === 0 ? 10000 : 1 / this.splitBlendPercentage);
  1008. // Only PCF uses depth stencil texture.
  1009. if (this._filter === CascadedShadowGenerator.FILTER_PCF) {
  1010. effect.setDepthStencilTexture("shadowSampler" + lightIndex, shadowMap);
  1011. light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), width, 1 / width, this.frustumEdgeFalloff, lightIndex);
  1012. } else if (this._filter === CascadedShadowGenerator.FILTER_PCSS) {
  1013. effect.setDepthStencilTexture("shadowSampler" + lightIndex, shadowMap);
  1014. effect.setTexture("depthSampler" + lightIndex, shadowMap);
  1015. light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), 1 / width, this._contactHardeningLightSizeUVRatio * width, this.frustumEdgeFalloff, lightIndex);
  1016. }
  1017. else {
  1018. effect.setTexture("shadowSampler" + lightIndex, shadowMap);
  1019. light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), width, 1 / width, this.frustumEdgeFalloff, lightIndex);
  1020. }
  1021. light._uniformBuffer.updateFloat2("depthValues", this.getLight().getDepthMinZ(camera), this.getLight().getDepthMinZ(camera) + this.getLight().getDepthMaxZ(camera), lightIndex);
  1022. }
  1023. /**
  1024. * Gets the transformation matrix used to project the meshes into the map from the light point of view.
  1025. * (eq to shadow prjection matrix * light transform matrix)
  1026. * @returns The transform matrix used to create the shadow map
  1027. */
  1028. public getTransformMatrix(): Matrix {
  1029. return this.getCSMTransformMatrix(0);
  1030. }
  1031. /**
  1032. * Recreates the shadow map dependencies like RTT and post processes. This can be used during the switch between
  1033. * Cube and 2D textures for instance.
  1034. */
  1035. public recreateShadowMap(): void {
  1036. let shadowMap = this._shadowMap;
  1037. if (!shadowMap) {
  1038. return;
  1039. }
  1040. // Track render list.
  1041. var renderList = shadowMap.renderList;
  1042. // Clean up existing data.
  1043. this._disposeRTT();
  1044. // Reinitializes.
  1045. this._initializeGenerator();
  1046. // Reaffect the filter to ensure a correct fallback if necessary.
  1047. this.filter = this.filter;
  1048. // Reaffect the filter.
  1049. this._applyFilterValues();
  1050. // Reaffect Render List.
  1051. this._shadowMap!.renderList = renderList;
  1052. }
  1053. private _disposeRTT(): void {
  1054. if (this._shadowMap) {
  1055. this._shadowMap.dispose();
  1056. this._shadowMap = null;
  1057. }
  1058. }
  1059. /**
  1060. * Disposes the ShadowGenerator.
  1061. * Returns nothing.
  1062. */
  1063. public dispose(): void {
  1064. this._disposeRTT();
  1065. if (this._light) {
  1066. this._light._shadowGenerator = null;
  1067. this._light._markMeshesAsLightDirty();
  1068. }
  1069. this.onBeforeShadowMapRenderMeshObservable.clear();
  1070. this.onBeforeShadowMapRenderObservable.clear();
  1071. this.onAfterShadowMapRenderMeshObservable.clear();
  1072. this.onAfterShadowMapRenderObservable.clear();
  1073. }
  1074. /**
  1075. * Serializes the shadow generator setup to a json object.
  1076. * @returns The serialized JSON object
  1077. */
  1078. public serialize(): any {
  1079. var serializationObject: any = {};
  1080. var shadowMap = this.getShadowMap();
  1081. if (!shadowMap) {
  1082. return serializationObject;
  1083. }
  1084. serializationObject.lightId = this._light.id;
  1085. serializationObject.mapSize = shadowMap.getRenderSize();
  1086. serializationObject.forceBackFacesOnly = this.forceBackFacesOnly;
  1087. serializationObject.darkness = this.getDarkness();
  1088. serializationObject.transparencyShadow = this._transparencyShadow;
  1089. serializationObject.frustumEdgeFalloff = this.frustumEdgeFalloff;
  1090. serializationObject.bias = this.bias;
  1091. serializationObject.normalBias = this.normalBias;
  1092. serializationObject.usePercentageCloserFiltering = this.usePercentageCloserFiltering;
  1093. serializationObject.useContactHardeningShadow = this.useContactHardeningShadow;
  1094. serializationObject.filteringQuality = this.filteringQuality;
  1095. serializationObject.contactHardeningLightSizeUVRatio = this.contactHardeningLightSizeUVRatio;
  1096. serializationObject.renderList = [];
  1097. if (shadowMap.renderList) {
  1098. for (var meshIndex = 0; meshIndex < shadowMap.renderList.length; meshIndex++) {
  1099. var mesh = shadowMap.renderList[meshIndex];
  1100. serializationObject.renderList.push(mesh.id);
  1101. }
  1102. }
  1103. return serializationObject;
  1104. }
  1105. /**
  1106. * Parses a serialized ShadowGenerator and returns a new ShadowGenerator.
  1107. * @param parsedShadowGenerator The JSON object to parse
  1108. * @param scene The scene to create the shadow map for
  1109. * @returns The parsed shadow generator
  1110. */
  1111. public static Parse(parsedShadowGenerator: any, scene: Scene): CascadedShadowGenerator {
  1112. var light = <DirectionalLight>scene.getLightByID(parsedShadowGenerator.lightId);
  1113. var shadowGenerator = new CascadedShadowGenerator(parsedShadowGenerator.mapSize, light);
  1114. var shadowMap = shadowGenerator.getShadowMap();
  1115. for (var meshIndex = 0; meshIndex < parsedShadowGenerator.renderList.length; meshIndex++) {
  1116. var meshes = scene.getMeshesByID(parsedShadowGenerator.renderList[meshIndex]);
  1117. meshes.forEach(function(mesh) {
  1118. if (!shadowMap) {
  1119. return;
  1120. }
  1121. if (!shadowMap.renderList) {
  1122. shadowMap.renderList = [];
  1123. }
  1124. shadowMap.renderList.push(mesh);
  1125. });
  1126. }
  1127. if (parsedShadowGenerator.usePercentageCloserFiltering) {
  1128. shadowGenerator.usePercentageCloserFiltering = true;
  1129. }
  1130. else if (parsedShadowGenerator.useContactHardeningShadow) {
  1131. shadowGenerator.useContactHardeningShadow = true;
  1132. }
  1133. if (parsedShadowGenerator.filteringQuality) {
  1134. shadowGenerator.filteringQuality = parsedShadowGenerator.filteringQuality;
  1135. }
  1136. if (parsedShadowGenerator.contactHardeningLightSizeUVRatio) {
  1137. shadowGenerator.contactHardeningLightSizeUVRatio = parsedShadowGenerator.contactHardeningLightSizeUVRatio;
  1138. }
  1139. if (parsedShadowGenerator.bias !== undefined) {
  1140. shadowGenerator.bias = parsedShadowGenerator.bias;
  1141. }
  1142. if (parsedShadowGenerator.normalBias !== undefined) {
  1143. shadowGenerator.normalBias = parsedShadowGenerator.normalBias;
  1144. }
  1145. if (parsedShadowGenerator.frustumEdgeFalloff !== undefined) {
  1146. shadowGenerator.frustumEdgeFalloff = parsedShadowGenerator.frustumEdgeFalloff;
  1147. }
  1148. if (parsedShadowGenerator.darkness) {
  1149. shadowGenerator.setDarkness(parsedShadowGenerator.darkness);
  1150. }
  1151. if (parsedShadowGenerator.transparencyShadow) {
  1152. shadowGenerator.setTransparencyShadow(true);
  1153. }
  1154. shadowGenerator.forceBackFacesOnly = parsedShadowGenerator.forceBackFacesOnly;
  1155. return shadowGenerator;
  1156. }
  1157. }