cascadedShadowGenerator.ts 72 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792
  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, Observer } 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 { BoundingInfo } from '../../Culling/boundingInfo';
  26. import { DepthRenderer } from '../../Rendering/depthRenderer';
  27. import { DepthReducer } from '../../Misc/depthReducer';
  28. interface ICascade {
  29. prevBreakDistance: number;
  30. breakDistance: number;
  31. }
  32. const UpDir = Vector3.Up();
  33. const ZeroVec = Vector3.Zero();
  34. let tmpv1 = new Vector3(),
  35. tmpv2 = new Vector3(),
  36. matrix = new Matrix();
  37. /**
  38. * A CSM implementation allowing casting shadows on large scenes.
  39. * Documentation : https://doc.babylonjs.com/babylon101/cascadedShadows
  40. * Based on: https://github.com/TheRealMJP/Shadows and https://johanmedestrom.wordpress.com/2016/03/18/opengl-cascaded-shadow-maps/
  41. */
  42. export class CascadedShadowGenerator implements IShadowGenerator {
  43. private static readonly frustumCornersNDCSpace = [
  44. new Vector3(-1.0, +1.0, -1.0),
  45. new Vector3(+1.0, +1.0, -1.0),
  46. new Vector3(+1.0, -1.0, -1.0),
  47. new Vector3(-1.0, -1.0, -1.0),
  48. new Vector3(-1.0, +1.0, +1.0),
  49. new Vector3(+1.0, +1.0, +1.0),
  50. new Vector3(+1.0, -1.0, +1.0),
  51. new Vector3(-1.0, -1.0, +1.0),
  52. ];
  53. /**
  54. * Defines the default number of cascades used by the CSM.
  55. */
  56. public static readonly DEFAULT_CASCADES_COUNT = 4;
  57. /**
  58. * Defines the minimum number of cascades used by the CSM.
  59. */
  60. public static readonly MIN_CASCADES_COUNT = 2;
  61. /**
  62. * Defines the maximum number of cascades used by the CSM.
  63. */
  64. public static readonly MAX_CASCADES_COUNT = 4;
  65. /**
  66. * Shadow generator mode None: no filtering applied.
  67. */
  68. public static readonly FILTER_NONE = 0;
  69. /**
  70. * Shadow generator mode PCF: Percentage Closer Filtering
  71. * benefits from Webgl 2 shadow samplers. Fallback to Poisson Sampling in Webgl 1
  72. * (https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch11.html)
  73. */
  74. public static readonly FILTER_PCF = 6;
  75. /**
  76. * Shadow generator mode PCSS: Percentage Closering Soft Shadow.
  77. * benefits from Webgl 2 shadow samplers. Fallback to Poisson Sampling in Webgl 1
  78. * Contact Hardening
  79. */
  80. public static readonly FILTER_PCSS = 7;
  81. /**
  82. * Reserved for PCF and PCSS
  83. * Highest Quality.
  84. *
  85. * Execute PCF on a 5*5 kernel improving a lot the shadow aliasing artifacts.
  86. *
  87. * Execute PCSS with 32 taps blocker search and 64 taps PCF.
  88. */
  89. public static readonly QUALITY_HIGH = 0;
  90. /**
  91. * Reserved for PCF and PCSS
  92. * Good tradeoff for quality/perf cross devices
  93. *
  94. * Execute PCF on a 3*3 kernel.
  95. *
  96. * Execute PCSS with 16 taps blocker search and 32 taps PCF.
  97. */
  98. public static readonly QUALITY_MEDIUM = 1;
  99. /**
  100. * Reserved for PCF and PCSS
  101. * The lowest quality but the fastest.
  102. *
  103. * Execute PCF on a 1*1 kernel.
  104. *
  105. * Execute PCSS with 16 taps blocker search and 16 taps PCF.
  106. */
  107. public static readonly QUALITY_LOW = 2;
  108. private static readonly _CLEARONE = new Color4(1.0, 1.0, 1.0, 1.0);
  109. /**
  110. * Observable triggered before the shadow is rendered. Can be used to update internal effect state
  111. */
  112. public onBeforeShadowMapRenderObservable = new Observable<Effect>();
  113. /**
  114. * Observable triggered after the shadow is rendered. Can be used to restore internal effect state
  115. */
  116. public onAfterShadowMapRenderObservable = new Observable<Effect>();
  117. /**
  118. * Observable triggered before a mesh is rendered in the shadow map.
  119. * Can be used to update internal effect state (that you can get from the onBeforeShadowMapRenderObservable)
  120. */
  121. public onBeforeShadowMapRenderMeshObservable = new Observable<Mesh>();
  122. /**
  123. * Observable triggered after a mesh is rendered in the shadow map.
  124. * Can be used to update internal effect state (that you can get from the onAfterShadowMapRenderObservable)
  125. */
  126. public onAfterShadowMapRenderMeshObservable = new Observable<Mesh>();
  127. private _bias = 0.00005;
  128. /**
  129. * Gets the bias: offset applied on the depth preventing acnea (in light direction).
  130. */
  131. public get bias(): number {
  132. return this._bias;
  133. }
  134. /**
  135. * Sets the bias: offset applied on the depth preventing acnea (in light direction).
  136. */
  137. public set bias(bias: number) {
  138. this._bias = bias;
  139. }
  140. private _normalBias = 0;
  141. /**
  142. * Gets the normalBias: offset applied on the depth preventing acnea (along side the normal direction and proportinal to the light/normal angle).
  143. */
  144. public get normalBias(): number {
  145. return this._normalBias;
  146. }
  147. /**
  148. * Sets the normalBias: offset applied on the depth preventing acnea (along side the normal direction and proportinal to the light/normal angle).
  149. */
  150. public set normalBias(normalBias: number) {
  151. this._normalBias = normalBias;
  152. }
  153. private _filter = CascadedShadowGenerator.FILTER_PCF;
  154. /**
  155. * Gets the current mode of the shadow generator (normal, PCF, PCSS...).
  156. * The returned value is a number equal to one of the available mode defined in ShadowMap.FILTER_x like _FILTER_NONE
  157. */
  158. public get filter(): number {
  159. return this._filter;
  160. }
  161. /**
  162. * Sets the current mode of the shadow generator (normal, PCF, PCSS...).
  163. * The returned value is a number equal to one of the available mode defined in ShadowMap.FILTER_x like _FILTER_NONE
  164. */
  165. public set filter(value: number) {
  166. if (this._filter === value) {
  167. return;
  168. }
  169. this._filter = value;
  170. this._applyFilterValues();
  171. this._light._markMeshesAsLightDirty();
  172. }
  173. /**
  174. * Gets if the current filter is set to "PCF" (percentage closer filtering).
  175. */
  176. public get usePercentageCloserFiltering(): boolean {
  177. return this.filter === CascadedShadowGenerator.FILTER_PCF;
  178. }
  179. /**
  180. * Sets the current filter to "PCF" (percentage closer filtering).
  181. */
  182. public set usePercentageCloserFiltering(value: boolean) {
  183. if (!value && this.filter !== CascadedShadowGenerator.FILTER_PCF) {
  184. return;
  185. }
  186. this.filter = (value ? CascadedShadowGenerator.FILTER_PCF : CascadedShadowGenerator.FILTER_NONE);
  187. }
  188. private _filteringQuality = CascadedShadowGenerator.QUALITY_HIGH;
  189. /**
  190. * Gets the PCF or PCSS Quality.
  191. * Only valid if usePercentageCloserFiltering or usePercentageCloserFiltering is true.
  192. */
  193. public get filteringQuality(): number {
  194. return this._filteringQuality;
  195. }
  196. /**
  197. * Sets the PCF or PCSS Quality.
  198. * Only valid if usePercentageCloserFiltering or usePercentageCloserFiltering is true.
  199. */
  200. public set filteringQuality(filteringQuality: number) {
  201. if (this._filteringQuality === filteringQuality) {
  202. return;
  203. }
  204. this._filteringQuality = filteringQuality;
  205. this._applyFilterValues();
  206. this._light._markMeshesAsLightDirty();
  207. }
  208. /**
  209. * Gets if the current filter is set to "PCSS" (contact hardening).
  210. */
  211. public get useContactHardeningShadow(): boolean {
  212. return this.filter === CascadedShadowGenerator.FILTER_PCSS;
  213. }
  214. /**
  215. * Sets the current filter to "PCSS" (contact hardening).
  216. */
  217. public set useContactHardeningShadow(value: boolean) {
  218. if (!value && this.filter !== CascadedShadowGenerator.FILTER_PCSS) {
  219. return;
  220. }
  221. this.filter = (value ? CascadedShadowGenerator.FILTER_PCSS : CascadedShadowGenerator.FILTER_NONE);
  222. }
  223. private _contactHardeningLightSizeUVRatio = 0.1;
  224. /**
  225. * Gets the Light Size (in shadow map uv unit) used in PCSS to determine the blocker search area and the penumbra size.
  226. * Using a ratio helps keeping shape stability independently of the map size.
  227. *
  228. * It does not account for the light projection as it was having too much
  229. * instability during the light setup or during light position changes.
  230. *
  231. * Only valid if useContactHardeningShadow is true.
  232. */
  233. public get contactHardeningLightSizeUVRatio(): number {
  234. return this._contactHardeningLightSizeUVRatio;
  235. }
  236. /**
  237. * Sets the Light Size (in shadow map uv unit) used in PCSS to determine the blocker search area and the penumbra size.
  238. * Using a ratio helps keeping shape stability independently of the map size.
  239. *
  240. * It does not account for the light projection as it was having too much
  241. * instability during the light setup or during light position changes.
  242. *
  243. * Only valid if useContactHardeningShadow is true.
  244. */
  245. public set contactHardeningLightSizeUVRatio(contactHardeningLightSizeUVRatio: number) {
  246. this._contactHardeningLightSizeUVRatio = contactHardeningLightSizeUVRatio;
  247. }
  248. private _darkness = 0;
  249. /** Gets or sets the actual darkness of a shadow */
  250. public get darkness() {
  251. return this._darkness;
  252. }
  253. public set darkness(value: number) {
  254. this.setDarkness(value);
  255. }
  256. /**
  257. * Returns the darkness value (float). This can only decrease the actual darkness of a shadow.
  258. * 0 means strongest and 1 would means no shadow.
  259. * @returns the darkness.
  260. */
  261. public getDarkness(): number {
  262. return this._darkness;
  263. }
  264. /**
  265. * Sets the darkness value (float). This can only decrease the actual darkness of a shadow.
  266. * @param darkness The darkness value 0 means strongest and 1 would means no shadow.
  267. * @returns the shadow generator allowing fluent coding.
  268. */
  269. public setDarkness(darkness: number): CascadedShadowGenerator {
  270. if (darkness >= 1.0) {
  271. this._darkness = 1.0;
  272. }
  273. else if (darkness <= 0.0) {
  274. this._darkness = 0.0;
  275. }
  276. else {
  277. this._darkness = darkness;
  278. }
  279. return this;
  280. }
  281. /**
  282. * Gets or sets the actual darkness of the soft shadows while using PCSS filtering (value between 0. and 1.)
  283. */
  284. public penumbraDarkness: number = 1.0;
  285. private _transparencyShadow = false;
  286. /** Gets or sets the ability to have transparent shadow */
  287. public get transparencyShadow() {
  288. return this._transparencyShadow;
  289. }
  290. public set transparencyShadow(value: boolean) {
  291. this.setTransparencyShadow(value);
  292. }
  293. /**
  294. * Sets the ability to have transparent shadow (boolean).
  295. * @param transparent True if transparent else False
  296. * @returns the shadow generator allowing fluent coding
  297. */
  298. public setTransparencyShadow(transparent: boolean): CascadedShadowGenerator {
  299. this._transparencyShadow = transparent;
  300. return this;
  301. }
  302. private _numCascades = CascadedShadowGenerator.DEFAULT_CASCADES_COUNT;
  303. /**
  304. * Gets or set the number of cascades used by the CSM.
  305. */
  306. public get numCascades(): number {
  307. return this._numCascades;
  308. }
  309. public set numCascades(value: number) {
  310. value = Math.min(Math.max(value, CascadedShadowGenerator.MIN_CASCADES_COUNT), CascadedShadowGenerator.MAX_CASCADES_COUNT);
  311. if (value === this._numCascades) {
  312. return;
  313. }
  314. this._numCascades = value;
  315. this.recreateShadowMap();
  316. }
  317. /**
  318. * Sets this to true if you want that the edges of the shadows don't "swimm" / "shimmer" when rotating the camera.
  319. * The trade off is that you loose some precision in the shadow rendering when enabling this setting.
  320. */
  321. public stabilizeCascades: boolean = false;
  322. private _shadowMap: Nullable<RenderTargetTexture>;
  323. /**
  324. * Gets the main RTT containing the shadow map (usually storing depth from the light point of view).
  325. * @returns The render target texture if present otherwise, null
  326. */
  327. public getShadowMap(): Nullable<RenderTargetTexture> {
  328. return this._shadowMap;
  329. }
  330. protected _freezeShadowCastersBoundingInfo: boolean = false;
  331. private _freezeShadowCastersBoundingInfoObservable: Nullable<Observer<Scene>> = null;
  332. /**
  333. * Enables or disables the shadow casters bounding info computation.
  334. * If your shadow casters don't move, you can disable this feature.
  335. * If it is enabled, the bounding box computation is done every frame.
  336. */
  337. public get freezeShadowCastersBoundingInfo(): boolean {
  338. return this._freezeShadowCastersBoundingInfo;
  339. }
  340. public set freezeShadowCastersBoundingInfo(freeze: boolean) {
  341. if (this._freezeShadowCastersBoundingInfoObservable && freeze) {
  342. this._scene.onBeforeRenderObservable.remove(this._freezeShadowCastersBoundingInfoObservable);
  343. this._freezeShadowCastersBoundingInfoObservable = null;
  344. }
  345. if (!this._freezeShadowCastersBoundingInfoObservable && !freeze) {
  346. this._freezeShadowCastersBoundingInfoObservable = this._scene.onBeforeRenderObservable.add(this._computeShadowCastersBoundingInfo.bind(this));
  347. }
  348. this._freezeShadowCastersBoundingInfo = freeze;
  349. if (freeze) {
  350. this._computeShadowCastersBoundingInfo();
  351. }
  352. }
  353. private _scbiMin = new Vector3(0, 0, 0);
  354. private _scbiMax = new Vector3(0, 0, 0);
  355. protected _computeShadowCastersBoundingInfo(): void {
  356. this._scbiMin.copyFromFloats(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  357. this._scbiMax.copyFromFloats(Number.MIN_VALUE, Number.MIN_VALUE, Number.MIN_VALUE);
  358. if (this._shadowMap && this._shadowMap.renderList) {
  359. const renderList = this._shadowMap.renderList;
  360. for (let meshIndex = 0; meshIndex < renderList.length; meshIndex++) {
  361. const mesh = renderList[meshIndex];
  362. if (!mesh) {
  363. continue;
  364. }
  365. const boundingInfo = mesh.getBoundingInfo(),
  366. boundingBox = boundingInfo.boundingBox;
  367. this._scbiMin.minimizeInPlace(boundingBox.minimumWorld);
  368. this._scbiMax.maximizeInPlace(boundingBox.maximumWorld);
  369. }
  370. }
  371. this._shadowCastersBoundingInfo.reConstruct(this._scbiMin, this._scbiMax);
  372. }
  373. protected _shadowCastersBoundingInfo: BoundingInfo;
  374. /**
  375. * Gets or sets the shadow casters bounding info.
  376. * If you provide your own shadow casters bounding info, first enable freezeShadowCastersBoundingInfo
  377. * so that the system won't overwrite the bounds you provide
  378. */
  379. public get shadowCastersBoundingInfo(): BoundingInfo {
  380. return this._shadowCastersBoundingInfo;
  381. }
  382. public set shadowCastersBoundingInfo(boundingInfo: BoundingInfo) {
  383. this._shadowCastersBoundingInfo = boundingInfo;
  384. }
  385. protected _breaksAreDirty: boolean = true;
  386. protected _minDistance: number = 0;
  387. protected _maxDistance: number = 1;
  388. /**
  389. * Sets the minimal and maximal distances to use when computing the cascade breaks.
  390. *
  391. * The values of min / max are typically the depth zmin and zmax values of your scene, for a given frame.
  392. * If you don't know these values, simply leave them to their defaults and don't call this function.
  393. * @param min minimal distance for the breaks (default to 0.)
  394. * @param max maximal distance for the breaks (default to 1.)
  395. */
  396. public setMinMaxDistance(min: number, max: number): void {
  397. if (this._minDistance === min && this._maxDistance === max) {
  398. return;
  399. }
  400. if (min > max) {
  401. min = 0;
  402. max = 1;
  403. }
  404. if (min < 0) {
  405. min = 0;
  406. }
  407. if (max > 1) {
  408. max = 1;
  409. }
  410. this._minDistance = min;
  411. this._maxDistance = max;
  412. this._breaksAreDirty = true;
  413. }
  414. /**
  415. * Gets the class name of that object
  416. * @returns "ShadowGenerator"
  417. */
  418. public getClassName(): string {
  419. return "CascadedShadowGenerator";
  420. }
  421. /**
  422. * Helper function to add a mesh and its descendants to the list of shadow casters.
  423. * @param mesh Mesh to add
  424. * @param includeDescendants boolean indicating if the descendants should be added. Default to true
  425. * @returns the Shadow Generator itself
  426. */
  427. public addShadowCaster(mesh: AbstractMesh, includeDescendants = true): CascadedShadowGenerator {
  428. if (!this._shadowMap) {
  429. return this;
  430. }
  431. if (!this._shadowMap.renderList) {
  432. this._shadowMap.renderList = [];
  433. }
  434. this._shadowMap.renderList.push(mesh);
  435. if (includeDescendants) {
  436. this._shadowMap.renderList.push(...mesh.getChildMeshes());
  437. }
  438. return this;
  439. }
  440. /**
  441. * Helper function to remove a mesh and its descendants from the list of shadow casters
  442. * @param mesh Mesh to remove
  443. * @param includeDescendants boolean indicating if the descendants should be removed. Default to true
  444. * @returns the Shadow Generator itself
  445. */
  446. public removeShadowCaster(mesh: AbstractMesh, includeDescendants = true): CascadedShadowGenerator {
  447. if (!this._shadowMap || !this._shadowMap.renderList) {
  448. return this;
  449. }
  450. var index = this._shadowMap.renderList.indexOf(mesh);
  451. if (index !== -1) {
  452. this._shadowMap.renderList.splice(index, 1);
  453. }
  454. if (includeDescendants) {
  455. for (var child of mesh.getChildren()) {
  456. this.removeShadowCaster(<any>child);
  457. }
  458. }
  459. return this;
  460. }
  461. /**
  462. * Controls the extent to which the shadows fade out at the edge of the frustum
  463. */
  464. public frustumEdgeFalloff = 0;
  465. private _light: DirectionalLight;
  466. /**
  467. * Returns the associated light object.
  468. * @returns the light generating the shadow
  469. */
  470. public getLight(): DirectionalLight {
  471. return this._light;
  472. }
  473. /**
  474. * If true the shadow map is generated by rendering the back face of the mesh instead of the front face.
  475. * This can help with self-shadowing as the geometry making up the back of objects is slightly offset.
  476. * It might on the other hand introduce peter panning.
  477. */
  478. public forceBackFacesOnly = false;
  479. private _cascadeMinExtents: Array<Vector3>;
  480. private _cascadeMaxExtents: Array<Vector3>;
  481. /**
  482. * Gets a cascade minimum extents
  483. * @param cascadeIndex index of the cascade
  484. * @returns the minimum cascade extents
  485. */
  486. public getCascadeMinExtents(cascadeIndex: number): Nullable<Vector3> {
  487. return cascadeIndex >= 0 && cascadeIndex < this._numCascades ? this._cascadeMinExtents[cascadeIndex] : null;
  488. }
  489. /**
  490. * Gets a cascade maximum extents
  491. * @param cascadeIndex index of the cascade
  492. * @returns the maximum cascade extents
  493. */
  494. public getCascadeMaxExtents(cascadeIndex: number): Nullable<Vector3> {
  495. return cascadeIndex >= 0 && cascadeIndex < this._numCascades ? this._cascadeMaxExtents[cascadeIndex] : null;
  496. }
  497. private _scene: Scene;
  498. private _lightDirection = Vector3.Zero();
  499. private _effect: Effect;
  500. private _cascades: Array<ICascade>;
  501. private _cachedPosition: Vector3 = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  502. private _cachedDirection: Vector3 = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  503. private _cachedDefines: string;
  504. private _currentRenderID: Array<number>;
  505. private _mapSize: number;
  506. private _currentLayer = 0;
  507. private _textureType: number;
  508. private _defaultTextureMatrix = Matrix.Identity();
  509. private _storedUniqueId: Nullable<number>;
  510. private _viewSpaceFrustumsZ: Array<number>;
  511. private _viewMatrices: Array<Matrix>;
  512. private _projectionMatrices: Array<Matrix>;
  513. private _transformMatrices: Array<Matrix>;
  514. private _transformMatricesAsArray: Float32Array;
  515. private _frustumLengths: Array<number>;
  516. private _lightSizeUVCorrection: Array<number>;
  517. private _depthCorrection: Array<number>;
  518. private _frustumCornersWorldSpace: Array<Array<Vector3>>;
  519. private _frustumCenter: Array<Vector3>;
  520. private _shadowCameraPos: Array<Vector3>;
  521. private _shadowMaxZ: number;
  522. /**
  523. * Gets the shadow max z distance. It's the limit beyond which shadows are not displayed.
  524. * It defaults to camera.maxZ
  525. */
  526. public get shadowMaxZ(): number {
  527. if (!this._scene || !this._scene.activeCamera) {
  528. return 0;
  529. }
  530. return this._shadowMaxZ;
  531. }
  532. /**
  533. * Sets the shadow max z distance.
  534. */
  535. public set shadowMaxZ(value: number) {
  536. if (!this._scene || !this._scene.activeCamera) {
  537. this._shadowMaxZ = value;
  538. return;
  539. }
  540. if (this._shadowMaxZ === value || value < this._scene.activeCamera.minZ || value > this._scene.activeCamera.maxZ) {
  541. return;
  542. }
  543. this._shadowMaxZ = value;
  544. this._breaksAreDirty = true;
  545. }
  546. protected _debug = false;
  547. /**
  548. * Gets or sets the debug flag.
  549. * When enabled, the cascades are materialized by different colors on the screen.
  550. */
  551. public get debug(): boolean {
  552. return this._debug;
  553. }
  554. public set debug(dbg: boolean) {
  555. this._debug = dbg;
  556. this._light._markMeshesAsLightDirty();
  557. }
  558. private _depthClamp = true;
  559. /**
  560. * Gets or sets the depth clamping value.
  561. *
  562. * When enabled, it improves the shadow quality because the near z plane of the light frustum don't need to be adjusted
  563. * to account for the shadow casters far away.
  564. *
  565. * Note that this property is incompatible with PCSS filtering, so it won't be used in that case.
  566. */
  567. public get depthClamp(): boolean {
  568. return this._depthClamp;
  569. }
  570. public set depthClamp(value: boolean) {
  571. this._depthClamp = value;
  572. }
  573. /**
  574. * Gets or sets the percentage of blending between two cascades (value between 0. and 1.).
  575. * It defaults to 0.1 (10% blending).
  576. */
  577. public cascadeBlendPercentage: number = 0.1;
  578. private _lambda = 0.5;
  579. /**
  580. * Gets or set the lambda parameter.
  581. * This parameter is used to split the camera frustum and create the cascades.
  582. * It's a value between 0. and 1.: If 0, the split is a uniform split of the frustum, if 1 it is a logarithmic split.
  583. * For all values in-between, it's a linear combination of the uniform and logarithm split algorithm.
  584. */
  585. public get lambda(): number {
  586. return this._lambda;
  587. }
  588. public set lambda(value: number) {
  589. const lambda = Math.min(Math.max(value, 0), 1);
  590. if (this._lambda == lambda) {
  591. return;
  592. }
  593. this._lambda = lambda;
  594. this._breaksAreDirty = true;
  595. }
  596. /**
  597. * Gets the view matrix corresponding to a given cascade
  598. * @param cascadeNum cascade to retrieve the view matrix from
  599. * @returns the cascade view matrix
  600. */
  601. public getCascadeViewMatrix(cascadeNum: number): Nullable<Matrix> {
  602. return cascadeNum >= 0 && cascadeNum < this._numCascades ? this._viewMatrices[cascadeNum] : null;
  603. }
  604. private _depthRenderer: Nullable<DepthRenderer>;
  605. /**
  606. * Sets the depth renderer to use when autoCalcDepthBounds is enabled.
  607. *
  608. * Note that if no depth renderer is set, a new one will be automatically created internally when necessary.
  609. *
  610. * You should call this function if you already have a depth renderer enabled in your scene, to avoid
  611. * doing multiple depth rendering each frame. If you provide your own depth renderer, make sure it stores linear depth!
  612. * @param depthRenderer The depth renderer to use when autoCalcDepthBounds is enabled. If you pass null or don't call this function at all, a depth renderer will be automatically created
  613. */
  614. public setDepthRenderer(depthRenderer: Nullable<DepthRenderer>): void {
  615. this._depthRenderer = depthRenderer;
  616. if (this._depthReducer) {
  617. this._depthReducer.setDepthRenderer(this._depthRenderer);
  618. }
  619. }
  620. private _depthReducer: Nullable<DepthReducer>;
  621. private _autoCalcDepthBounds = false;
  622. /**
  623. * Gets or sets the autoCalcDepthBounds property.
  624. *
  625. * When enabled, a depth rendering pass is first performed (with an internally created depth renderer or with the one
  626. * you provide by calling setDepthRenderer). Then, a min/max reducing is applied on the depth map to compute the
  627. * minimal and maximal depth of the map and those values are used as inputs for the setMinMaxDistance() function.
  628. * It can greatly enhance the shadow quality, at the expense of more GPU works.
  629. * When using this option, you should increase the value of the lambda parameter, and even set it to 1 for best results.
  630. */
  631. public get autoCalcDepthBounds(): boolean {
  632. return this._autoCalcDepthBounds;
  633. }
  634. public set autoCalcDepthBounds(value: boolean) {
  635. const camera = this._scene.activeCamera;
  636. if (!camera) {
  637. return;
  638. }
  639. if (!value) {
  640. if (this._depthReducer) {
  641. this._depthReducer.deactivate();
  642. }
  643. this.setMinMaxDistance(0, 1);
  644. return;
  645. }
  646. if (!this._depthReducer) {
  647. this._depthReducer = new DepthReducer(camera);
  648. this._depthReducer.onAfterReductionPerformed.add((minmax: { min: number, max: number}) => {
  649. let min = minmax.min, max = minmax.max;
  650. if (min >= max) {
  651. min = 0;
  652. max = 1;
  653. }
  654. if (min != this._minDistance || max != this._maxDistance) {
  655. this.setMinMaxDistance(min, max);
  656. }
  657. });
  658. this._depthReducer.setDepthRenderer(this._depthRenderer);
  659. }
  660. this._depthReducer.activate();
  661. }
  662. /**
  663. * Defines the refresh rate of the min/max computation used when autoCalcDepthBounds is set to true
  664. * Use 0 to compute just once, 1 to compute on every frame, 2 to compute every two frames and so on...
  665. * Note that if you provided your own depth renderer through a call to setDepthRenderer, you are responsible
  666. * for setting the refresh rate on the renderer yourself!
  667. */
  668. public get autoCalcDepthBoundsRefreshRate(): number {
  669. return this._depthReducer?.depthRenderer?.getDepthMap().refreshRate ?? -1;
  670. }
  671. public set autoCalcDepthBoundsRefreshRate(value: number) {
  672. if (this._depthReducer?.depthRenderer) {
  673. this._depthReducer.depthRenderer.getDepthMap().refreshRate = value;
  674. }
  675. }
  676. /**
  677. * Create the cascade breaks according to the lambda, shadowMaxZ and min/max distance properties, as well as the camera near and far planes.
  678. * This function is automatically called when updating lambda, shadowMaxZ and min/max distances, however you should call it yourself if
  679. * you change the camera near/far planes!
  680. */
  681. public splitFrustum(): void {
  682. this._breaksAreDirty = true;
  683. }
  684. private _splitFrustum(): void {
  685. let camera = this._scene.activeCamera;
  686. if (!camera) {
  687. return;
  688. }
  689. const near = camera.minZ,
  690. far = camera.maxZ,
  691. cameraRange = far - near,
  692. minDistance = this._minDistance,
  693. maxDistance = this._shadowMaxZ < far && this._shadowMaxZ >= near ? Math.min((this._shadowMaxZ - near) / (far - near), this._maxDistance) : this._maxDistance;
  694. const minZ = near + minDistance * cameraRange,
  695. maxZ = near + maxDistance * cameraRange;
  696. const range = maxZ - minZ,
  697. ratio = maxZ / minZ;
  698. for (let cascadeIndex = 0; cascadeIndex < this._cascades.length; ++cascadeIndex) {
  699. const p = (cascadeIndex + 1) / this._numCascades,
  700. log = minZ * (ratio ** p),
  701. uniform = minZ + range * p;
  702. const d = this._lambda * (log - uniform) + uniform;
  703. this._cascades[cascadeIndex].prevBreakDistance = cascadeIndex === 0 ? minDistance : this._cascades[cascadeIndex - 1].breakDistance;
  704. this._cascades[cascadeIndex].breakDistance = (d - near) / cameraRange;
  705. this._viewSpaceFrustumsZ[cascadeIndex] = near + this._cascades[cascadeIndex].breakDistance * cameraRange;
  706. this._frustumLengths[cascadeIndex] = (this._cascades[cascadeIndex].breakDistance - this._cascades[cascadeIndex].prevBreakDistance) * cameraRange;
  707. }
  708. this._breaksAreDirty = false;
  709. }
  710. /**
  711. * Gets the CSM transformation matrix used to project the meshes into the map from the light point of view.
  712. * (eq to view projection * shadow projection matrices)
  713. * @param cascadeIndex index number of the cascaded shadow map
  714. * @returns The transform matrix used to create the CSM shadow map
  715. */
  716. public getCSMTransformMatrix(cascadeIndex: number): Matrix {
  717. var scene = this._scene;
  718. if (this._currentRenderID[cascadeIndex] === scene.getRenderId()) {
  719. return this._transformMatrices[cascadeIndex];
  720. }
  721. let camera = scene.activeCamera;
  722. if (!camera) {
  723. return this._transformMatrices[cascadeIndex];
  724. }
  725. this._currentRenderID[cascadeIndex] = scene.getRenderId();
  726. var lightPosition = this._light.position;
  727. if (this._light.computeTransformedInformation()) {
  728. lightPosition = this._light.transformedPosition;
  729. }
  730. Vector3.NormalizeToRef(this._light.getShadowDirection(0), this._lightDirection);
  731. if (Math.abs(Vector3.Dot(this._lightDirection, Vector3.Up())) === 1.0) {
  732. this._lightDirection.z = 0.0000000000001; // Required to avoid perfectly perpendicular light
  733. }
  734. if (this._light.needProjectionMatrixCompute() || !this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !this._lightDirection.equals(this._cachedDirection)) {
  735. this._cachedPosition.copyFrom(lightPosition);
  736. this._cachedDirection.copyFrom(this._lightDirection);
  737. this._computeFrustumInWorldSpace(cascadeIndex);
  738. this._computeCascadeFrustum(cascadeIndex);
  739. this._cascadeMaxExtents[cascadeIndex].subtractToRef(this._cascadeMinExtents[cascadeIndex], tmpv1); // tmpv1 = cascadeExtents
  740. // Get position of the shadow camera
  741. this._frustumCenter[cascadeIndex].addToRef(this._lightDirection.scale(this._cascadeMinExtents[cascadeIndex].z), this._shadowCameraPos[cascadeIndex]);
  742. // Come up with a new orthographic camera for the shadow caster
  743. Matrix.LookAtLHToRef(this._shadowCameraPos[cascadeIndex], this._frustumCenter[cascadeIndex], UpDir, this._viewMatrices[cascadeIndex]);
  744. let minZ = 0, maxZ = tmpv1.z;
  745. // Try to tighten minZ and maxZ based on the bounding box of the shadow casters
  746. const boundingInfo = this._shadowCastersBoundingInfo;
  747. boundingInfo.update(this._viewMatrices[cascadeIndex]);
  748. maxZ = Math.min(maxZ, boundingInfo.boundingBox.maximumWorld.z);
  749. if (!this._depthClamp || this.filter === CascadedShadowGenerator.FILTER_PCSS) {
  750. // If we don't use depth clamping, we must set minZ so that all shadow casters are in the light frustum
  751. minZ = Math.min(minZ, boundingInfo.boundingBox.minimumWorld.z);
  752. } else {
  753. // If using depth clamping, we can adjust minZ to reduce the [minZ, maxZ] range (and get some additional precision in the shadow map)
  754. minZ = Math.max(minZ, boundingInfo.boundingBox.minimumWorld.z);
  755. }
  756. if (this._scene.useRightHandedSystem) {
  757. Matrix.OrthoOffCenterRHToRef(this._cascadeMinExtents[cascadeIndex].x, this._cascadeMaxExtents[cascadeIndex].x, this._cascadeMinExtents[cascadeIndex].y, this._cascadeMaxExtents[cascadeIndex].y, minZ, maxZ, this._projectionMatrices[cascadeIndex]);
  758. } else {
  759. Matrix.OrthoOffCenterLHToRef(this._cascadeMinExtents[cascadeIndex].x, this._cascadeMaxExtents[cascadeIndex].x, this._cascadeMinExtents[cascadeIndex].y, this._cascadeMaxExtents[cascadeIndex].y, minZ, maxZ, this._projectionMatrices[cascadeIndex]);
  760. }
  761. this._cascadeMinExtents[cascadeIndex].z = minZ;
  762. this._cascadeMaxExtents[cascadeIndex].z = maxZ;
  763. this._viewMatrices[cascadeIndex].multiplyToRef(this._projectionMatrices[cascadeIndex], this._transformMatrices[cascadeIndex]);
  764. // Create the rounding matrix, by projecting the world-space origin and determining
  765. // the fractional offset in texel space
  766. Vector3.TransformCoordinatesToRef(ZeroVec, this._transformMatrices[cascadeIndex], tmpv1); // tmpv1 = shadowOrigin
  767. tmpv1.scaleInPlace(this._mapSize / 2);
  768. tmpv2.copyFromFloats(Math.round(tmpv1.x), Math.round(tmpv1.y), Math.round(tmpv1.z)); // tmpv2 = roundedOrigin
  769. tmpv2.subtractInPlace(tmpv1).scaleInPlace(2 / this._mapSize); // tmpv2 = roundOffset
  770. Matrix.TranslationToRef(tmpv2.x, tmpv2.y, 0.0, matrix);
  771. this._projectionMatrices[cascadeIndex].multiplyToRef(matrix, this._projectionMatrices[cascadeIndex]);
  772. this._viewMatrices[cascadeIndex].multiplyToRef(this._projectionMatrices[cascadeIndex], this._transformMatrices[cascadeIndex]);
  773. }
  774. this._transformMatrices[cascadeIndex].copyToArray(this._transformMatricesAsArray, cascadeIndex * 16);
  775. return this._transformMatrices[cascadeIndex];
  776. }
  777. // Get the 8 points of the view frustum in world space
  778. private _computeFrustumInWorldSpace(cascadeIndex: number): void {
  779. if (!this._scene.activeCamera) {
  780. return;
  781. }
  782. const prevSplitDist = this._cascades[cascadeIndex].prevBreakDistance,
  783. splitDist = this._cascades[cascadeIndex].breakDistance;
  784. this._scene.activeCamera.getViewMatrix(); // make sure the transformation matrix we get when calling 'getTransformationMatrix()' is calculated with an up to date view matrix
  785. const invViewProj = Matrix.Invert(this._scene.activeCamera.getTransformationMatrix());
  786. for (let cornerIndex = 0; cornerIndex < CascadedShadowGenerator.frustumCornersNDCSpace.length; ++cornerIndex) {
  787. Vector3.TransformCoordinatesToRef(CascadedShadowGenerator.frustumCornersNDCSpace[cornerIndex], invViewProj, this._frustumCornersWorldSpace[cascadeIndex][cornerIndex]);
  788. }
  789. // Get the corners of the current cascade slice of the view frustum
  790. for (let cornerIndex = 0; cornerIndex < CascadedShadowGenerator.frustumCornersNDCSpace.length / 2; ++cornerIndex) {
  791. tmpv1.copyFrom(this._frustumCornersWorldSpace[cascadeIndex][cornerIndex + 4]).subtractInPlace(this._frustumCornersWorldSpace[cascadeIndex][cornerIndex]);
  792. tmpv2.copyFrom(tmpv1).scaleInPlace(prevSplitDist); // near corner ray
  793. tmpv1.scaleInPlace(splitDist); // far corner ray
  794. tmpv1.addInPlace(this._frustumCornersWorldSpace[cascadeIndex][cornerIndex]);
  795. this._frustumCornersWorldSpace[cascadeIndex][cornerIndex + 4].copyFrom(tmpv1);
  796. this._frustumCornersWorldSpace[cascadeIndex][cornerIndex].addInPlace(tmpv2);
  797. }
  798. }
  799. private _computeCascadeFrustum(cascadeIndex: number): void {
  800. this._cascadeMinExtents[cascadeIndex].copyFromFloats(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  801. this._cascadeMaxExtents[cascadeIndex].copyFromFloats(Number.MIN_VALUE, Number.MIN_VALUE, Number.MIN_VALUE);
  802. this._frustumCenter[cascadeIndex].copyFromFloats(0, 0, 0);
  803. const camera = this._scene.activeCamera;
  804. if (!camera) {
  805. return;
  806. }
  807. // Calculate the centroid of the view frustum slice
  808. for (let cornerIndex = 0; cornerIndex < this._frustumCornersWorldSpace[cascadeIndex].length; ++cornerIndex) {
  809. this._frustumCenter[cascadeIndex].addInPlace(this._frustumCornersWorldSpace[cascadeIndex][cornerIndex]);
  810. }
  811. this._frustumCenter[cascadeIndex].scaleInPlace(1 / this._frustumCornersWorldSpace[cascadeIndex].length);
  812. if (this.stabilizeCascades) {
  813. // Calculate the radius of a bounding sphere surrounding the frustum corners
  814. let sphereRadius = 0;
  815. for (let cornerIndex = 0; cornerIndex < this._frustumCornersWorldSpace[cascadeIndex].length; ++cornerIndex) {
  816. const dist = this._frustumCornersWorldSpace[cascadeIndex][cornerIndex].subtractToRef(this._frustumCenter[cascadeIndex], tmpv1).length();
  817. sphereRadius = Math.max(sphereRadius, dist);
  818. }
  819. sphereRadius = Math.ceil(sphereRadius * 16) / 16;
  820. this._cascadeMaxExtents[cascadeIndex].copyFromFloats(sphereRadius, sphereRadius, sphereRadius);
  821. this._cascadeMinExtents[cascadeIndex].copyFromFloats(-sphereRadius, -sphereRadius, -sphereRadius);
  822. } else {
  823. // Create a temporary view matrix for the light
  824. const lightCameraPos = this._frustumCenter[cascadeIndex];
  825. this._frustumCenter[cascadeIndex].addToRef(this._lightDirection, tmpv1); // tmpv1 = look at
  826. Matrix.LookAtLHToRef(lightCameraPos, tmpv1, UpDir, matrix); // matrix = lightView
  827. // Calculate an AABB around the frustum corners
  828. for (let cornerIndex = 0; cornerIndex < this._frustumCornersWorldSpace[cascadeIndex].length; ++cornerIndex) {
  829. Vector3.TransformCoordinatesToRef(this._frustumCornersWorldSpace[cascadeIndex][cornerIndex], matrix, tmpv1);
  830. this._cascadeMinExtents[cascadeIndex].minimizeInPlace(tmpv1);
  831. this._cascadeMaxExtents[cascadeIndex].maximizeInPlace(tmpv1);
  832. }
  833. }
  834. return;
  835. }
  836. /** @hidden */
  837. public static _SceneComponentInitialization: (scene: Scene) => void = (_) => {
  838. throw _DevTools.WarnImport("ShadowGeneratorSceneComponent");
  839. }
  840. /**
  841. * Creates a Cascaded Shadow Generator object.
  842. * A ShadowGenerator is the required tool to use the shadows.
  843. * Each directional light casting shadows needs to use its own ShadowGenerator.
  844. * Documentation : https://doc.babylonjs.com/babylon101/cascadedShadows
  845. * @param mapSize The size of the texture what stores the shadows. Example : 1024.
  846. * @param light The directional light object generating the shadows.
  847. * @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.
  848. */
  849. constructor(mapSize: number, light: DirectionalLight, usefulFloatFirst?: boolean) {
  850. this._scene = light.getScene();
  851. if (this._scene.getEngine().webGLVersion == 1) {
  852. throw "CSM can only be used in WebGL2";
  853. }
  854. this._light = light;
  855. this._mapSize = mapSize;
  856. light._shadowGenerator = this;
  857. this._shadowMaxZ = this._scene.activeCamera?.maxZ ?? 10000;
  858. this._shadowCastersBoundingInfo = new BoundingInfo(new Vector3(0, 0, 0), new Vector3(0, 0, 0));
  859. this.freezeShadowCastersBoundingInfo = false;
  860. CascadedShadowGenerator._SceneComponentInitialization(this._scene);
  861. // Texture type fallback from float to int if not supported.
  862. var caps = this._scene.getEngine().getCaps();
  863. if (!usefulFloatFirst) {
  864. if (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering) {
  865. this._textureType = Constants.TEXTURETYPE_HALF_FLOAT;
  866. }
  867. else if (caps.textureFloatRender && caps.textureFloatLinearFiltering) {
  868. this._textureType = Constants.TEXTURETYPE_FLOAT;
  869. }
  870. else {
  871. this._textureType = Constants.TEXTURETYPE_UNSIGNED_INT;
  872. }
  873. } else {
  874. if (caps.textureFloatRender && caps.textureFloatLinearFiltering) {
  875. this._textureType = Constants.TEXTURETYPE_FLOAT;
  876. }
  877. else if (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering) {
  878. this._textureType = Constants.TEXTURETYPE_HALF_FLOAT;
  879. }
  880. else {
  881. this._textureType = Constants.TEXTURETYPE_UNSIGNED_INT;
  882. }
  883. }
  884. this._initializeGenerator();
  885. this._applyFilterValues();
  886. }
  887. private _initializeGenerator(): void {
  888. this._light._markMeshesAsLightDirty();
  889. this._initializeShadowMap();
  890. }
  891. private _initializeShadowMap(): void {
  892. // CSM
  893. this._transformMatricesAsArray = new Float32Array(this._numCascades * 16);
  894. this._viewSpaceFrustumsZ = new Array(this._numCascades);
  895. this._frustumLengths = new Array(this._numCascades);
  896. this._currentRenderID = new Array(this._numCascades);
  897. this._lightSizeUVCorrection = new Array(this._numCascades * 2);
  898. this._depthCorrection = new Array(this._numCascades);
  899. this._cascades = [];
  900. this._viewMatrices = [];
  901. this._projectionMatrices = [];
  902. this._transformMatrices = [];
  903. this._cascadeMinExtents = [];
  904. this._cascadeMaxExtents = [];
  905. this._frustumCenter = [];
  906. this._shadowCameraPos = [];
  907. this._frustumCornersWorldSpace = [];
  908. for (let cascadeIndex = 0; cascadeIndex < this._numCascades; ++cascadeIndex) {
  909. this._cascades[cascadeIndex] = {
  910. prevBreakDistance: 0,
  911. breakDistance: 0,
  912. };
  913. this._viewMatrices[cascadeIndex] = Matrix.Zero();
  914. this._projectionMatrices[cascadeIndex] = Matrix.Zero();
  915. this._transformMatrices[cascadeIndex] = Matrix.Zero();
  916. this._cascadeMinExtents[cascadeIndex] = new Vector3();
  917. this._cascadeMaxExtents[cascadeIndex] = new Vector3();
  918. this._frustumCenter[cascadeIndex] = new Vector3();
  919. this._shadowCameraPos[cascadeIndex] = new Vector3();
  920. this._frustumCornersWorldSpace[cascadeIndex] = new Array(CascadedShadowGenerator.frustumCornersNDCSpace.length);
  921. for (let i = 0; i < CascadedShadowGenerator.frustumCornersNDCSpace.length; ++i) {
  922. this._frustumCornersWorldSpace[cascadeIndex][i] = new Vector3();
  923. }
  924. }
  925. // Render target
  926. let engine = this._scene.getEngine();
  927. const size = { width: this._mapSize, height: this._mapSize, layers: this.numCascades };
  928. this._shadowMap = new RenderTargetTexture(this._light.name + "_shadowMap", size, this._scene, false, true, this._textureType, false, undefined, false, false, undefined, Constants.TEXTUREFORMAT_RED);
  929. this._shadowMap.createDepthStencilTexture(Constants.LESS, true);
  930. this._shadowMap.wrapU = Texture.CLAMP_ADDRESSMODE;
  931. this._shadowMap.wrapV = Texture.CLAMP_ADDRESSMODE;
  932. this._shadowMap.anisotropicFilteringLevel = 4;
  933. this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  934. this._shadowMap.renderParticles = false;
  935. this._shadowMap.ignoreCameraViewport = true;
  936. if (this._storedUniqueId) {
  937. this._shadowMap.uniqueId = this._storedUniqueId;
  938. }
  939. this._shadowMap.onBeforeBindObservable.add(() => {
  940. if (this._breaksAreDirty) {
  941. this._splitFrustum();
  942. }
  943. });
  944. // Record Face Index before render.
  945. this._shadowMap.onBeforeRenderObservable.add((layer: number) => {
  946. this._currentLayer = layer;
  947. if (this._filter === CascadedShadowGenerator.FILTER_PCF) {
  948. engine.setColorWrite(this.debug);
  949. }
  950. });
  951. // Custom render function.
  952. this._shadowMap.customRenderFunction = this._renderForShadowMap.bind(this);
  953. // Restore state after bind.
  954. this._shadowMap.onAfterUnbindObservable.add(() => {
  955. if (this._filter === CascadedShadowGenerator.FILTER_PCF) {
  956. engine.setColorWrite(true);
  957. }
  958. });
  959. // Clear according to the chosen filter.
  960. this._shadowMap.onClearObservable.add((engine) => {
  961. if (this._filter === CascadedShadowGenerator.FILTER_PCF) {
  962. engine.clear(CascadedShadowGenerator._CLEARONE, this.debug, true, false);
  963. }
  964. else {
  965. engine.clear(CascadedShadowGenerator._CLEARONE, true, true, false);
  966. }
  967. });
  968. // Recreate on resize.
  969. this._shadowMap.onResizeObservable.add((RTT) => {
  970. this._storedUniqueId = this._shadowMap!.uniqueId;
  971. this._mapSize = RTT.getRenderSize();
  972. this._light._markMeshesAsLightDirty();
  973. this.recreateShadowMap();
  974. });
  975. this._splitFrustum();
  976. }
  977. private _renderForShadowMap(opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>): void {
  978. var index: number;
  979. let engine = this._scene.getEngine();
  980. const colorWrite = engine.getColorWrite();
  981. if (depthOnlySubMeshes.length) {
  982. engine.setColorWrite(false);
  983. for (index = 0; index < depthOnlySubMeshes.length; index++) {
  984. this._renderSubMeshForShadowMap(depthOnlySubMeshes.data[index]);
  985. }
  986. engine.setColorWrite(colorWrite);
  987. }
  988. for (index = 0; index < opaqueSubMeshes.length; index++) {
  989. this._renderSubMeshForShadowMap(opaqueSubMeshes.data[index]);
  990. }
  991. for (index = 0; index < alphaTestSubMeshes.length; index++) {
  992. this._renderSubMeshForShadowMap(alphaTestSubMeshes.data[index]);
  993. }
  994. if (this._transparencyShadow) {
  995. for (index = 0; index < transparentSubMeshes.length; index++) {
  996. this._renderSubMeshForShadowMap(transparentSubMeshes.data[index]);
  997. }
  998. }
  999. }
  1000. private _renderSubMeshForShadowMap(subMesh: SubMesh): void {
  1001. var mesh = subMesh.getRenderingMesh();
  1002. var scene = this._scene;
  1003. var engine = scene.getEngine();
  1004. let material = subMesh.getMaterial();
  1005. mesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
  1006. if (!material || subMesh.verticesCount === 0) {
  1007. return;
  1008. }
  1009. // Culling
  1010. engine.setState(material.backFaceCulling);
  1011. // Managing instances
  1012. var batch = mesh._getInstancesRenderList(subMesh._id);
  1013. if (batch.mustReturn) {
  1014. return;
  1015. }
  1016. var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
  1017. if (this.isReady(subMesh, hardwareInstancedRendering)) {
  1018. engine.enableEffect(this._effect);
  1019. mesh._bind(subMesh, this._effect, Material.TriangleFillMode);
  1020. this._effect.setFloat3("biasAndScale", this.bias, this.normalBias, 0);
  1021. this._effect.setMatrix("viewProjection", this.getCSMTransformMatrix(this._currentLayer));
  1022. this._effect.setVector3("lightData", this._cachedDirection);
  1023. if (scene.activeCamera) {
  1024. this._effect.setFloat2("depthValues", this.getLight().getDepthMinZ(scene.activeCamera), this.getLight().getDepthMinZ(scene.activeCamera) + this.getLight().getDepthMaxZ(scene.activeCamera));
  1025. }
  1026. // Alpha test
  1027. if (material && material.needAlphaTesting()) {
  1028. var alphaTexture = material.getAlphaTestTexture();
  1029. if (alphaTexture) {
  1030. this._effect.setTexture("diffuseSampler", alphaTexture);
  1031. this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix() || this._defaultTextureMatrix);
  1032. }
  1033. }
  1034. // Bones
  1035. if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
  1036. const skeleton = mesh.skeleton;
  1037. if (skeleton.isUsingTextureForMatrices) {
  1038. const boneTexture = skeleton.getTransformMatrixTexture(mesh);
  1039. if (!boneTexture) {
  1040. return;
  1041. }
  1042. this._effect.setTexture("boneSampler", boneTexture);
  1043. this._effect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
  1044. } else {
  1045. this._effect.setMatrices("mBones", skeleton.getTransformMatrices((mesh)));
  1046. }
  1047. }
  1048. // Morph targets
  1049. MaterialHelper.BindMorphTargetParameters(mesh, this._effect);
  1050. if (this.forceBackFacesOnly) {
  1051. engine.setState(true, 0, false, true);
  1052. }
  1053. // Observables
  1054. this.onBeforeShadowMapRenderMeshObservable.notifyObservers(mesh);
  1055. this.onBeforeShadowMapRenderObservable.notifyObservers(this._effect);
  1056. // Draw
  1057. mesh._processRendering(subMesh, this._effect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
  1058. (isInstance, world) => this._effect.setMatrix("world", world));
  1059. if (this.forceBackFacesOnly) {
  1060. engine.setState(true, 0, false, false);
  1061. }
  1062. // Observables
  1063. this.onAfterShadowMapRenderObservable.notifyObservers(this._effect);
  1064. this.onAfterShadowMapRenderMeshObservable.notifyObservers(mesh);
  1065. } else {
  1066. // Need to reset refresh rate of the shadowMap
  1067. if (this._shadowMap) {
  1068. this._shadowMap.resetRefreshCounter();
  1069. }
  1070. }
  1071. }
  1072. private _applyFilterValues(): void {
  1073. if (!this._shadowMap) {
  1074. return;
  1075. }
  1076. if (this.filter === CascadedShadowGenerator.FILTER_PCSS) {
  1077. this._shadowMap.updateSamplingMode(Texture.NEAREST_SAMPLINGMODE);
  1078. } else {
  1079. this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  1080. }
  1081. }
  1082. /**
  1083. * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
  1084. * @param onCompiled Callback triggered at the and of the effects compilation
  1085. * @param options Sets of optional options forcing the compilation with different modes
  1086. */
  1087. public forceCompilation(onCompiled?: (generator: IShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void {
  1088. let localOptions = {
  1089. useInstances: false,
  1090. ...options
  1091. };
  1092. let shadowMap = this.getShadowMap();
  1093. if (!shadowMap) {
  1094. if (onCompiled) {
  1095. onCompiled(this);
  1096. }
  1097. return;
  1098. }
  1099. let renderList = shadowMap.renderList;
  1100. if (!renderList) {
  1101. if (onCompiled) {
  1102. onCompiled(this);
  1103. }
  1104. return;
  1105. }
  1106. var subMeshes = new Array<SubMesh>();
  1107. for (var mesh of renderList) {
  1108. subMeshes.push(...mesh.subMeshes);
  1109. }
  1110. if (subMeshes.length === 0) {
  1111. if (onCompiled) {
  1112. onCompiled(this);
  1113. }
  1114. return;
  1115. }
  1116. var currentIndex = 0;
  1117. var checkReady = () => {
  1118. if (!this._scene || !this._scene.getEngine()) {
  1119. return;
  1120. }
  1121. while (this.isReady(subMeshes[currentIndex], localOptions.useInstances)) {
  1122. currentIndex++;
  1123. if (currentIndex >= subMeshes.length) {
  1124. if (onCompiled) {
  1125. onCompiled(this);
  1126. }
  1127. return;
  1128. }
  1129. }
  1130. setTimeout(checkReady, 16);
  1131. };
  1132. checkReady();
  1133. }
  1134. /**
  1135. * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
  1136. * @param options Sets of optional options forcing the compilation with different modes
  1137. * @returns A promise that resolves when the compilation completes
  1138. */
  1139. public forceCompilationAsync(options?: Partial<{ useInstances: boolean }>): Promise<void> {
  1140. return new Promise((resolve) => {
  1141. this.forceCompilation(() => {
  1142. resolve();
  1143. }, options);
  1144. });
  1145. }
  1146. /**
  1147. * Determine wheter the shadow generator is ready or not (mainly all effects and related post processes needs to be ready).
  1148. * @param subMesh The submesh we want to render in the shadow map
  1149. * @param useInstances Defines wether will draw in the map using instances
  1150. * @returns true if ready otherwise, false
  1151. */
  1152. public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
  1153. var defines = [];
  1154. if (this._textureType !== Constants.TEXTURETYPE_UNSIGNED_INT) {
  1155. defines.push("#define FLOAT");
  1156. }
  1157. defines.push("#define DEPTHTEXTURE");
  1158. if (this._depthClamp && this._filter !== CascadedShadowGenerator.FILTER_PCSS) {
  1159. defines.push("#define DEPTHCLAMP");
  1160. }
  1161. var attribs = [VertexBuffer.PositionKind];
  1162. var mesh = subMesh.getMesh();
  1163. var material = subMesh.getMaterial();
  1164. // Normal bias.
  1165. if (this.normalBias && mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
  1166. attribs.push(VertexBuffer.NormalKind);
  1167. defines.push("#define NORMAL");
  1168. defines.push("#define DIRECTIONINLIGHTDATA");
  1169. }
  1170. // Alpha test
  1171. if (material && material.needAlphaTesting()) {
  1172. var alphaTexture = material.getAlphaTestTexture();
  1173. if (alphaTexture) {
  1174. defines.push("#define ALPHATEST");
  1175. if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  1176. attribs.push(VertexBuffer.UVKind);
  1177. defines.push("#define UV1");
  1178. }
  1179. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
  1180. if (alphaTexture.coordinatesIndex === 1) {
  1181. attribs.push(VertexBuffer.UV2Kind);
  1182. defines.push("#define UV2");
  1183. }
  1184. }
  1185. }
  1186. }
  1187. // Bones
  1188. const fallbacks = new EffectFallbacks();
  1189. if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
  1190. attribs.push(VertexBuffer.MatricesIndicesKind);
  1191. attribs.push(VertexBuffer.MatricesWeightsKind);
  1192. if (mesh.numBoneInfluencers > 4) {
  1193. attribs.push(VertexBuffer.MatricesIndicesExtraKind);
  1194. attribs.push(VertexBuffer.MatricesWeightsExtraKind);
  1195. }
  1196. const skeleton = mesh.skeleton;
  1197. defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
  1198. if (mesh.numBoneInfluencers > 0) {
  1199. fallbacks.addCPUSkinningFallback(0, mesh);
  1200. }
  1201. if (skeleton.isUsingTextureForMatrices) {
  1202. defines.push("#define BONETEXTURE");
  1203. } else {
  1204. defines.push("#define BonesPerMesh " + (skeleton.bones.length + 1));
  1205. }
  1206. } else {
  1207. defines.push("#define NUM_BONE_INFLUENCERS 0");
  1208. }
  1209. // Morph targets
  1210. var manager = (<Mesh>mesh).morphTargetManager;
  1211. let morphInfluencers = 0;
  1212. if (manager) {
  1213. if (manager.numInfluencers > 0) {
  1214. defines.push("#define MORPHTARGETS");
  1215. morphInfluencers = manager.numInfluencers;
  1216. defines.push("#define NUM_MORPH_INFLUENCERS " + morphInfluencers);
  1217. MaterialHelper.PrepareAttributesForMorphTargetsInfluencers(attribs, mesh, morphInfluencers);
  1218. }
  1219. }
  1220. // Instances
  1221. if (useInstances) {
  1222. defines.push("#define INSTANCES");
  1223. MaterialHelper.PushAttributesForInstances(attribs);
  1224. }
  1225. // Get correct effect
  1226. var join = defines.join("\n");
  1227. if (this._cachedDefines !== join) {
  1228. this._cachedDefines = join;
  1229. let shaderName = "shadowMap";
  1230. let uniforms = ["world", "mBones", "viewProjection", "diffuseMatrix", "lightData", "depthValues", "biasAndScale", "morphTargetInfluences", "boneTextureWidth"];
  1231. let samplers = ["diffuseSampler", "boneSampler"];
  1232. this._effect = this._scene.getEngine().createEffect(shaderName,
  1233. attribs, uniforms,
  1234. samplers, join,
  1235. fallbacks, undefined, undefined, { maxSimultaneousMorphTargets: morphInfluencers });
  1236. }
  1237. if (!this._effect.isReady()) {
  1238. return false;
  1239. }
  1240. return true;
  1241. }
  1242. /**
  1243. * Prepare all the defines in a material relying on a shadow map at the specified light index.
  1244. * @param defines Defines of the material we want to update
  1245. * @param lightIndex Index of the light in the enabled light list of the material
  1246. */
  1247. public prepareDefines(defines: any, lightIndex: number): void {
  1248. var scene = this._scene;
  1249. var light = this._light;
  1250. if (!scene.shadowsEnabled || !light.shadowEnabled) {
  1251. return;
  1252. }
  1253. defines["SHADOW" + lightIndex] = true;
  1254. defines["SHADOWCSM" + lightIndex] = true;
  1255. defines["SHADOWCSMDEBUG" + lightIndex] = this.debug;
  1256. defines["SHADOWCSMNUM_CASCADES" + lightIndex] = this.numCascades;
  1257. const camera = scene.activeCamera;
  1258. if (camera && this._shadowMaxZ < camera.maxZ) {
  1259. defines["SHADOWCSMUSESHADOWMAXZ" + lightIndex] = true;
  1260. }
  1261. if (this.useContactHardeningShadow) {
  1262. defines["SHADOWPCSS" + lightIndex] = true;
  1263. if (this._filteringQuality === CascadedShadowGenerator.QUALITY_LOW) {
  1264. defines["SHADOWLOWQUALITY" + lightIndex] = true;
  1265. }
  1266. else if (this._filteringQuality === CascadedShadowGenerator.QUALITY_MEDIUM) {
  1267. defines["SHADOWMEDIUMQUALITY" + lightIndex] = true;
  1268. }
  1269. // else default to high.
  1270. }
  1271. else if (this.usePercentageCloserFiltering) {
  1272. defines["SHADOWPCF" + lightIndex] = true;
  1273. if (this._filteringQuality === CascadedShadowGenerator.QUALITY_LOW) {
  1274. defines["SHADOWLOWQUALITY" + lightIndex] = true;
  1275. }
  1276. else if (this._filteringQuality === CascadedShadowGenerator.QUALITY_MEDIUM) {
  1277. defines["SHADOWMEDIUMQUALITY" + lightIndex] = true;
  1278. }
  1279. // else default to high.
  1280. }
  1281. }
  1282. /**
  1283. * Binds the shadow related information inside of an effect (information like near, far, darkness...
  1284. * defined in the generator but impacting the effect).
  1285. * @param lightIndex Index of the light in the enabled light list of the material owning the effect
  1286. * @param effect The effect we are binfing the information for
  1287. */
  1288. public bindShadowLight(lightIndex: string, effect: Effect): void {
  1289. const light = this._light;
  1290. const scene = this._scene;
  1291. if (!scene.shadowsEnabled || !light.shadowEnabled) {
  1292. return;
  1293. }
  1294. const camera = scene.activeCamera;
  1295. if (!camera) {
  1296. return;
  1297. }
  1298. const shadowMap = this.getShadowMap();
  1299. if (!shadowMap) {
  1300. return;
  1301. }
  1302. const width = shadowMap.getSize().width;
  1303. effect.setMatrices("lightMatrix" + lightIndex, this._transformMatricesAsArray);
  1304. effect.setArray("viewFrustumZ" + lightIndex, this._viewSpaceFrustumsZ);
  1305. effect.setFloat("cascadeBlendFactor" + lightIndex, this.cascadeBlendPercentage === 0 ? 10000 : 1 / this.cascadeBlendPercentage);
  1306. effect.setArray("frustumLengths" + lightIndex, this._frustumLengths);
  1307. // Only PCF uses depth stencil texture.
  1308. if (this._filter === CascadedShadowGenerator.FILTER_PCF) {
  1309. effect.setDepthStencilTexture("shadowSampler" + lightIndex, shadowMap);
  1310. light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), width, 1 / width, this.frustumEdgeFalloff, lightIndex);
  1311. } else if (this._filter === CascadedShadowGenerator.FILTER_PCSS) {
  1312. for (let cascadeIndex = 0; cascadeIndex < this._numCascades; ++cascadeIndex) {
  1313. this._lightSizeUVCorrection[cascadeIndex * 2 + 0] = cascadeIndex === 0 ? 1 : (this._cascadeMaxExtents[0].x - this._cascadeMinExtents[0].x) / (this._cascadeMaxExtents[cascadeIndex].x - this._cascadeMinExtents[cascadeIndex].x); // x correction
  1314. this._lightSizeUVCorrection[cascadeIndex * 2 + 1] = cascadeIndex === 0 ? 1 : (this._cascadeMaxExtents[0].y - this._cascadeMinExtents[0].y) / (this._cascadeMaxExtents[cascadeIndex].y - this._cascadeMinExtents[cascadeIndex].y); // y correction
  1315. this._depthCorrection[cascadeIndex] = cascadeIndex === 0 ? 1 : (this._cascadeMaxExtents[cascadeIndex].z - this._cascadeMinExtents[cascadeIndex].z) / (this._cascadeMaxExtents[0].z - this._cascadeMinExtents[0].z);
  1316. }
  1317. effect.setDepthStencilTexture("shadowSampler" + lightIndex, shadowMap);
  1318. effect.setTexture("depthSampler" + lightIndex, shadowMap);
  1319. effect.setArray2("lightSizeUVCorrection" + lightIndex, this._lightSizeUVCorrection);
  1320. effect.setArray("depthCorrection" + lightIndex, this._depthCorrection);
  1321. effect.setFloat("penumbraDarkness" + lightIndex, this.penumbraDarkness);
  1322. light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), 1 / width, this._contactHardeningLightSizeUVRatio * width, this.frustumEdgeFalloff, lightIndex);
  1323. }
  1324. else {
  1325. effect.setTexture("shadowSampler" + lightIndex, shadowMap);
  1326. light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), width, 1 / width, this.frustumEdgeFalloff, lightIndex);
  1327. }
  1328. light._uniformBuffer.updateFloat2("depthValues", this.getLight().getDepthMinZ(camera), this.getLight().getDepthMinZ(camera) + this.getLight().getDepthMaxZ(camera), lightIndex);
  1329. }
  1330. /**
  1331. * Gets the transformation matrix of the first cascade used to project the meshes into the map from the light point of view.
  1332. * (eq to view projection * shadow projection matrices)
  1333. * @returns The transform matrix used to create the shadow map
  1334. */
  1335. public getTransformMatrix(): Matrix {
  1336. return this.getCSMTransformMatrix(0);
  1337. }
  1338. /**
  1339. * Recreates the shadow map dependencies like RTT and post processes. This can be used during the switch between
  1340. * Cube and 2D textures for instance.
  1341. */
  1342. public recreateShadowMap(): void {
  1343. let shadowMap = this._shadowMap;
  1344. if (!shadowMap) {
  1345. return;
  1346. }
  1347. // Track render list.
  1348. var renderList = shadowMap.renderList;
  1349. // Clean up existing data.
  1350. this._disposeRTT();
  1351. // Reinitializes.
  1352. this._initializeGenerator();
  1353. // Reaffect the filter to ensure a correct fallback if necessary.
  1354. this.filter = this.filter;
  1355. // Reaffect the filter.
  1356. this._applyFilterValues();
  1357. // Reaffect Render List.
  1358. this._shadowMap!.renderList = renderList;
  1359. }
  1360. private _disposeRTT(): void {
  1361. if (this._shadowMap) {
  1362. this._shadowMap.dispose();
  1363. this._shadowMap = null;
  1364. }
  1365. }
  1366. /**
  1367. * Disposes the ShadowGenerator.
  1368. * Returns nothing.
  1369. */
  1370. public dispose(): void {
  1371. this._disposeRTT();
  1372. if (this._light) {
  1373. this._light._shadowGenerator = null;
  1374. this._light._markMeshesAsLightDirty();
  1375. }
  1376. this.onBeforeShadowMapRenderMeshObservable.clear();
  1377. this.onBeforeShadowMapRenderObservable.clear();
  1378. this.onAfterShadowMapRenderMeshObservable.clear();
  1379. this.onAfterShadowMapRenderObservable.clear();
  1380. if (this._freezeShadowCastersBoundingInfoObservable) {
  1381. this._scene.onBeforeRenderObservable.remove(this._freezeShadowCastersBoundingInfoObservable);
  1382. this._freezeShadowCastersBoundingInfoObservable = null;
  1383. }
  1384. }
  1385. /**
  1386. * Serializes the shadow generator setup to a json object.
  1387. * @returns The serialized JSON object
  1388. */
  1389. public serialize(): any {
  1390. var serializationObject: any = {};
  1391. var shadowMap = this.getShadowMap();
  1392. if (!shadowMap) {
  1393. return serializationObject;
  1394. }
  1395. serializationObject.lightId = this._light.id;
  1396. serializationObject.mapSize = shadowMap.getRenderSize();
  1397. serializationObject.forceBackFacesOnly = this.forceBackFacesOnly;
  1398. serializationObject.darkness = this.getDarkness();
  1399. serializationObject.transparencyShadow = this._transparencyShadow;
  1400. serializationObject.frustumEdgeFalloff = this.frustumEdgeFalloff;
  1401. serializationObject.numCascades = this._numCascades;
  1402. serializationObject.stabilizeCascades = this.stabilizeCascades;
  1403. serializationObject.depthClamp = this._depthClamp;
  1404. serializationObject.lambda = this._lambda;
  1405. serializationObject.freezeShadowCastersBoundingInfo = this._freezeShadowCastersBoundingInfo;
  1406. serializationObject.shadowMaxZ = this._shadowMaxZ;
  1407. serializationObject.cascadeBlendPercentage = this.cascadeBlendPercentage;
  1408. serializationObject.bias = this.bias;
  1409. serializationObject.normalBias = this.normalBias;
  1410. serializationObject.usePercentageCloserFiltering = this.usePercentageCloserFiltering;
  1411. serializationObject.useContactHardeningShadow = this.useContactHardeningShadow;
  1412. serializationObject.filteringQuality = this.filteringQuality;
  1413. serializationObject.contactHardeningLightSizeUVRatio = this.contactHardeningLightSizeUVRatio;
  1414. serializationObject.penumbraDarkness = this.penumbraDarkness;
  1415. serializationObject.renderList = [];
  1416. if (shadowMap.renderList) {
  1417. for (var meshIndex = 0; meshIndex < shadowMap.renderList.length; meshIndex++) {
  1418. var mesh = shadowMap.renderList[meshIndex];
  1419. serializationObject.renderList.push(mesh.id);
  1420. }
  1421. }
  1422. return serializationObject;
  1423. }
  1424. /**
  1425. * Parses a serialized ShadowGenerator and returns a new ShadowGenerator.
  1426. * @param parsedShadowGenerator The JSON object to parse
  1427. * @param scene The scene to create the shadow map for
  1428. * @returns The parsed shadow generator
  1429. */
  1430. public static Parse(parsedShadowGenerator: any, scene: Scene): CascadedShadowGenerator {
  1431. var light = <DirectionalLight>scene.getLightByID(parsedShadowGenerator.lightId);
  1432. var shadowGenerator = new CascadedShadowGenerator(parsedShadowGenerator.mapSize, light);
  1433. var shadowMap = shadowGenerator.getShadowMap();
  1434. for (var meshIndex = 0; meshIndex < parsedShadowGenerator.renderList.length; meshIndex++) {
  1435. var meshes = scene.getMeshesByID(parsedShadowGenerator.renderList[meshIndex]);
  1436. meshes.forEach(function(mesh) {
  1437. if (!shadowMap) {
  1438. return;
  1439. }
  1440. if (!shadowMap.renderList) {
  1441. shadowMap.renderList = [];
  1442. }
  1443. shadowMap.renderList.push(mesh);
  1444. });
  1445. }
  1446. if (parsedShadowGenerator.usePercentageCloserFiltering) {
  1447. shadowGenerator.usePercentageCloserFiltering = true;
  1448. }
  1449. else if (parsedShadowGenerator.useContactHardeningShadow) {
  1450. shadowGenerator.useContactHardeningShadow = true;
  1451. }
  1452. if (parsedShadowGenerator.filteringQuality !== undefined) {
  1453. shadowGenerator.filteringQuality = parsedShadowGenerator.filteringQuality;
  1454. }
  1455. if (parsedShadowGenerator.contactHardeningLightSizeUVRatio !== undefined) {
  1456. shadowGenerator.contactHardeningLightSizeUVRatio = parsedShadowGenerator.contactHardeningLightSizeUVRatio;
  1457. }
  1458. if (parsedShadowGenerator.bias !== undefined) {
  1459. shadowGenerator.bias = parsedShadowGenerator.bias;
  1460. }
  1461. if (parsedShadowGenerator.normalBias !== undefined) {
  1462. shadowGenerator.normalBias = parsedShadowGenerator.normalBias;
  1463. }
  1464. if (parsedShadowGenerator.frustumEdgeFalloff !== undefined) {
  1465. shadowGenerator.frustumEdgeFalloff = parsedShadowGenerator.frustumEdgeFalloff;
  1466. }
  1467. if (parsedShadowGenerator.darkness !== undefined) {
  1468. shadowGenerator.setDarkness(parsedShadowGenerator.darkness);
  1469. }
  1470. if (parsedShadowGenerator.transparencyShadow) {
  1471. shadowGenerator.setTransparencyShadow(true);
  1472. }
  1473. shadowGenerator.forceBackFacesOnly = !!parsedShadowGenerator.forceBackFacesOnly;
  1474. if (parsedShadowGenerator.stabilizeCascades !== undefined) {
  1475. shadowGenerator.stabilizeCascades = parsedShadowGenerator.stabilizeCascades;
  1476. }
  1477. if (parsedShadowGenerator.depthClamp !== undefined) {
  1478. shadowGenerator.depthClamp = parsedShadowGenerator.depthClamp;
  1479. }
  1480. if (parsedShadowGenerator.lambda !== undefined) {
  1481. shadowGenerator.lambda = parsedShadowGenerator.lambda;
  1482. }
  1483. if (parsedShadowGenerator.freezeShadowCastersBoundingInfo !== undefined) {
  1484. shadowGenerator.freezeShadowCastersBoundingInfo = parsedShadowGenerator.freezeShadowCastersBoundingInfo;
  1485. }
  1486. if (parsedShadowGenerator.shadowMaxZ !== undefined) {
  1487. shadowGenerator.shadowMaxZ = parsedShadowGenerator.shadowMaxZ;
  1488. }
  1489. if (parsedShadowGenerator.cascadeBlendPercentage !== undefined) {
  1490. shadowGenerator.cascadeBlendPercentage = parsedShadowGenerator.cascadeBlendPercentage;
  1491. }
  1492. if (parsedShadowGenerator.penumbraDarkness !== undefined) {
  1493. shadowGenerator.penumbraDarkness = parsedShadowGenerator.penumbraDarkness;
  1494. }
  1495. if (parsedShadowGenerator.numCascades !== undefined) {
  1496. shadowGenerator.numCascades = parsedShadowGenerator.numCascades;
  1497. }
  1498. return shadowGenerator;
  1499. }
  1500. }