babylon.shadowGenerator.ts 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270
  1. module BABYLON {
  2. /**
  3. * Interface to implement to create a shadow generator compatible with BJS.
  4. */
  5. export interface IShadowGenerator {
  6. /**
  7. * Gets the main RTT containing the shadow map (usually storing depth from the light point of view).
  8. * @returns The render target texture if present otherwise, null
  9. */
  10. getShadowMap(): Nullable<RenderTargetTexture>;
  11. /**
  12. * Gets the RTT used during rendering (can be a blurred version of the shadow map or the shadow map itself).
  13. * @returns The render target texture if the shadow map is present otherwise, null
  14. */
  15. getShadowMapForRendering(): Nullable<RenderTargetTexture>;
  16. /**
  17. * Determine wheter the shadow generator is ready or not (mainly all effects and related post processes needs to be ready).
  18. * @param subMesh The submesh we want to render in the shadow map
  19. * @param useInstances Defines wether will draw in the map using instances
  20. * @returns true if ready otherwise, false
  21. */
  22. isReady(subMesh: SubMesh, useInstances: boolean): boolean;
  23. /**
  24. * Prepare all the defines in a material relying on a shadow map at the specified light index.
  25. * @param defines Defines of the material we want to update
  26. * @param lightIndex Index of the light in the enabled light list of the material
  27. */
  28. prepareDefines(defines: MaterialDefines, lightIndex: number): void;
  29. /**
  30. * Binds the shadow related information inside of an effect (information like near, far, darkness...
  31. * defined in the generator but impacting the effect).
  32. * It implies the unifroms available on the materials are the standard BJS ones.
  33. * @param lightIndex Index of the light in the enabled light list of the material owning the effect
  34. * @param effect The effect we are binfing the information for
  35. */
  36. bindShadowLight(lightIndex: string, effect: Effect): void;
  37. /**
  38. * Gets the transformation matrix used to project the meshes into the map from the light point of view.
  39. * (eq to shadow prjection matrix * light transform matrix)
  40. * @returns The transform matrix used to create the shadow map
  41. */
  42. getTransformMatrix(): Matrix;
  43. /**
  44. * Recreates the shadow map dependencies like RTT and post processes. This can be used during the switch between
  45. * Cube and 2D textures for instance.
  46. */
  47. recreateShadowMap(): void;
  48. /**
  49. * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
  50. * @param onCompiled Callback triggered at the and of the effects compilation
  51. * @param options Sets of optional options forcing the compilation with different modes
  52. */
  53. forceCompilation(onCompiled?: (generator: ShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void;
  54. /**
  55. * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
  56. * @param options Sets of optional options forcing the compilation with different modes
  57. * @returns A promise that resolves when the compilation completes
  58. */
  59. forceCompilationAsync(options?: Partial<{ useInstances: boolean }>): Promise<void>;
  60. /**
  61. * Serializes the shadow generator setup to a json object.
  62. * @returns The serialized JSON object
  63. */
  64. serialize(): any;
  65. /**
  66. * Disposes the Shadow map and related Textures and effects.
  67. */
  68. dispose(): void;
  69. }
  70. /**
  71. * Default implementation of @see IShadowGenerator.
  72. * This is the main object responsible of generating shadows in the framework.
  73. * Documentation: https://doc.babylonjs.com/babylon101/shadows
  74. */
  75. export class ShadowGenerator implements IShadowGenerator {
  76. private static _FILTER_NONE = 0;
  77. private static _FILTER_EXPONENTIALSHADOWMAP = 1;
  78. private static _FILTER_POISSONSAMPLING = 2;
  79. private static _FILTER_BLUREXPONENTIALSHADOWMAP = 3;
  80. private static _FILTER_CLOSEEXPONENTIALSHADOWMAP = 4;
  81. private static _FILTER_BLURCLOSEEXPONENTIALSHADOWMAP = 5;
  82. /**
  83. * Shadow generator mode None: no filtering applied.
  84. */
  85. public static get FILTER_NONE(): number {
  86. return ShadowGenerator._FILTER_NONE;
  87. }
  88. /**
  89. * Shadow generator mode Poisson Sampling: Percentage Closer Filtering.
  90. * (Multiple Tap around evenly distributed around the pixel are used to evaluate the shadow strength)
  91. */
  92. public static get FILTER_POISSONSAMPLING(): number {
  93. return ShadowGenerator._FILTER_POISSONSAMPLING;
  94. }
  95. /**
  96. * Shadow generator mode ESM: Exponential Shadow Mapping.
  97. * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
  98. */
  99. public static get FILTER_EXPONENTIALSHADOWMAP(): number {
  100. return ShadowGenerator._FILTER_EXPONENTIALSHADOWMAP;
  101. }
  102. /**
  103. * Shadow generator mode ESM: Blurred Exponential Shadow Mapping.
  104. * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
  105. */
  106. public static get FILTER_BLUREXPONENTIALSHADOWMAP(): number {
  107. return ShadowGenerator._FILTER_BLUREXPONENTIALSHADOWMAP;
  108. }
  109. /**
  110. * Shadow generator mode ESM: Exponential Shadow Mapping using the inverse of the exponential preventing
  111. * edge artifacts on steep falloff.
  112. * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
  113. */
  114. public static get FILTER_CLOSEEXPONENTIALSHADOWMAP(): number {
  115. return ShadowGenerator._FILTER_CLOSEEXPONENTIALSHADOWMAP;
  116. }
  117. /**
  118. * Shadow generator mode ESM: Blurred Exponential Shadow Mapping using the inverse of the exponential preventing
  119. * edge artifacts on steep falloff.
  120. * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
  121. */
  122. public static get FILTER_BLURCLOSEEXPONENTIALSHADOWMAP(): number {
  123. return ShadowGenerator._FILTER_BLURCLOSEEXPONENTIALSHADOWMAP;
  124. }
  125. private _bias = 0.00005;
  126. /**
  127. * Gets the bias: offset applied on the depth preventing acnea.
  128. */
  129. public get bias(): number {
  130. return this._bias;
  131. }
  132. /**
  133. * Sets the bias: offset applied on the depth preventing acnea.
  134. */
  135. public set bias(bias: number) {
  136. this._bias = bias;
  137. }
  138. private _blurBoxOffset = 1;
  139. /**
  140. * Gets the blur box offset: offset applied during the blur pass.
  141. * Only usefull if useKernelBlur = false
  142. */
  143. public get blurBoxOffset(): number {
  144. return this._blurBoxOffset;
  145. }
  146. /**
  147. * Sets the blur box offset: offset applied during the blur pass.
  148. * Only usefull if useKernelBlur = false
  149. */
  150. public set blurBoxOffset(value: number) {
  151. if (this._blurBoxOffset === value) {
  152. return;
  153. }
  154. this._blurBoxOffset = value;
  155. this._disposeBlurPostProcesses();
  156. }
  157. private _blurScale = 2;
  158. /**
  159. * Gets the blur scale: scale of the blurred texture compared to the main shadow map.
  160. * 2 means half of the size.
  161. */
  162. public get blurScale(): number {
  163. return this._blurScale;
  164. }
  165. /**
  166. * Sets the blur scale: scale of the blurred texture compared to the main shadow map.
  167. * 2 means half of the size.
  168. */
  169. public set blurScale(value: number) {
  170. if (this._blurScale === value) {
  171. return;
  172. }
  173. this._blurScale = value;
  174. this._disposeBlurPostProcesses();
  175. }
  176. private _blurKernel = 1;
  177. /**
  178. * Gets the blur kernel: kernel size of the blur pass.
  179. * Only usefull if useKernelBlur = true
  180. */
  181. public get blurKernel(): number {
  182. return this._blurKernel;
  183. }
  184. /**
  185. * Sets the blur kernel: kernel size of the blur pass.
  186. * Only usefull if useKernelBlur = true
  187. */
  188. public set blurKernel(value: number) {
  189. if (this._blurKernel === value) {
  190. return;
  191. }
  192. this._blurKernel = value;
  193. this._disposeBlurPostProcesses();
  194. }
  195. private _useKernelBlur = false;
  196. /**
  197. * Gets whether the blur pass is a kernel blur (if true) or box blur.
  198. * Only usefull in filtered mode (useBlurExponentialShadowMap...)
  199. */
  200. public get useKernelBlur(): boolean {
  201. return this._useKernelBlur;
  202. }
  203. /**
  204. * Sets whether the blur pass is a kernel blur (if true) or box blur.
  205. * Only usefull in filtered mode (useBlurExponentialShadowMap...)
  206. */
  207. public set useKernelBlur(value: boolean) {
  208. if (this._useKernelBlur === value) {
  209. return;
  210. }
  211. this._useKernelBlur = value;
  212. this._disposeBlurPostProcesses();
  213. }
  214. private _depthScale: number;
  215. /**
  216. * Gets the depth scale used in ESM mode.
  217. */
  218. public get depthScale(): number {
  219. return this._depthScale !== undefined ? this._depthScale : this._light.getDepthScale();
  220. }
  221. /**
  222. * Sets the depth scale used in ESM mode.
  223. * This can override the scale stored on the light.
  224. */
  225. public set depthScale(value: number) {
  226. this._depthScale = value;
  227. }
  228. private _filter = ShadowGenerator.FILTER_NONE;
  229. /**
  230. * Gets the current mode of the shadow generator (normal, PCF, ESM...).
  231. * The returned value is a number equal to one of the available mode defined in ShadowMap.FILTER_x like _FILTER_NONE
  232. */
  233. public get filter(): number {
  234. return this._filter;
  235. }
  236. /**
  237. * Sets the current mode of the shadow generator (normal, PCF, ESM...).
  238. * The returned value is a number equal to one of the available mode defined in ShadowMap.FILTER_x like _FILTER_NONE
  239. */
  240. public set filter(value: number) {
  241. // Blurring the cubemap is going to be too expensive. Reverting to unblurred version
  242. if (this._light.needCube()) {
  243. if (value === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP) {
  244. this.useExponentialShadowMap = true;
  245. return;
  246. }
  247. else if (value === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) {
  248. this.useCloseExponentialShadowMap = true;
  249. return;
  250. }
  251. }
  252. if (this._filter === value) {
  253. return;
  254. }
  255. this._filter = value;
  256. this._disposeBlurPostProcesses();
  257. this._applyFilterValues();
  258. this._light._markMeshesAsLightDirty();
  259. }
  260. /**
  261. * Gets if the current filter is set to Poisson Sampling aka PCF.
  262. */
  263. public get usePoissonSampling(): boolean {
  264. return this.filter === ShadowGenerator.FILTER_POISSONSAMPLING;
  265. }
  266. /**
  267. * Sets the current filter to Poisson Sampling aka PCF.
  268. */
  269. public set usePoissonSampling(value: boolean) {
  270. if (!value && this.filter !== ShadowGenerator.FILTER_POISSONSAMPLING) {
  271. return;
  272. }
  273. this.filter = (value ? ShadowGenerator.FILTER_POISSONSAMPLING : ShadowGenerator.FILTER_NONE);
  274. }
  275. /**
  276. * Gets if the current filter is set to VSM.
  277. * DEPRECATED. Should use useExponentialShadowMap instead.
  278. */
  279. public get useVarianceShadowMap(): boolean {
  280. Tools.Warn("VSM are now replaced by ESM. Please use useExponentialShadowMap instead.");
  281. return this.useExponentialShadowMap;
  282. }
  283. /**
  284. * Sets the current filter is to VSM.
  285. * DEPRECATED. Should use useExponentialShadowMap instead.
  286. */
  287. public set useVarianceShadowMap(value: boolean) {
  288. Tools.Warn("VSM are now replaced by ESM. Please use useExponentialShadowMap instead.");
  289. this.useExponentialShadowMap = value;
  290. }
  291. /**
  292. * Gets if the current filter is set to blurred VSM.
  293. * DEPRECATED. Should use useBlurExponentialShadowMap instead.
  294. */
  295. public get useBlurVarianceShadowMap(): boolean {
  296. Tools.Warn("VSM are now replaced by ESM. Please use useBlurExponentialShadowMap instead.");
  297. return this.useBlurExponentialShadowMap;
  298. }
  299. /**
  300. * Sets the current filter is to blurred VSM.
  301. * DEPRECATED. Should use useBlurExponentialShadowMap instead.
  302. */
  303. public set useBlurVarianceShadowMap(value: boolean) {
  304. Tools.Warn("VSM are now replaced by ESM. Please use useBlurExponentialShadowMap instead.");
  305. this.useBlurExponentialShadowMap = value;
  306. }
  307. /**
  308. * Gets if the current filter is set to ESM.
  309. */
  310. public get useExponentialShadowMap(): boolean {
  311. return this.filter === ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP;
  312. }
  313. /**
  314. * Sets the current filter is to ESM.
  315. */
  316. public set useExponentialShadowMap(value: boolean) {
  317. if (!value && this.filter !== ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP) {
  318. return;
  319. }
  320. this.filter = (value ? ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
  321. }
  322. /**
  323. * Gets if the current filter is set to filtered ESM.
  324. */
  325. public get useBlurExponentialShadowMap(): boolean {
  326. return this.filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP;
  327. }
  328. /**
  329. * Gets if the current filter is set to filtered ESM.
  330. */
  331. public set useBlurExponentialShadowMap(value: boolean) {
  332. if (!value && this.filter !== ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP) {
  333. return;
  334. }
  335. this.filter = (value ? ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
  336. }
  337. /**
  338. * Gets if the current filter is set to "close ESM" (using the inverse of the
  339. * exponential to prevent steep falloff artifacts).
  340. */
  341. public get useCloseExponentialShadowMap(): boolean {
  342. return this.filter === ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP;
  343. }
  344. /**
  345. * Sets the current filter to "close ESM" (using the inverse of the
  346. * exponential to prevent steep falloff artifacts).
  347. */
  348. public set useCloseExponentialShadowMap(value: boolean) {
  349. if (!value && this.filter !== ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP) {
  350. return;
  351. }
  352. this.filter = (value ? ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
  353. }
  354. /**
  355. * Gets if the current filter is set to filtered "close ESM" (using the inverse of the
  356. * exponential to prevent steep falloff artifacts).
  357. */
  358. public get useBlurCloseExponentialShadowMap(): boolean {
  359. return this.filter === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP;
  360. }
  361. /**
  362. * Sets the current filter to fileterd "close ESM" (using the inverse of the
  363. * exponential to prevent steep falloff artifacts).
  364. */
  365. public set useBlurCloseExponentialShadowMap(value: boolean) {
  366. if (!value && this.filter !== ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) {
  367. return;
  368. }
  369. this.filter = (value ? ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
  370. }
  371. private _darkness = 0;
  372. /**
  373. * Returns the darkness value (float). This can only decrease the actual darkness of a shadow.
  374. * 0 means strongest and 1 would means no shadow.
  375. * @returns the darkness.
  376. */
  377. public getDarkness(): number {
  378. return this._darkness;
  379. }
  380. /**
  381. * Sets the darkness value (float). This can only decrease the actual darkness of a shadow.
  382. * @param darkness The darkness value 0 means strongest and 1 would means no shadow.
  383. * @returns the shadow generator allowing fluent coding.
  384. */
  385. public setDarkness(darkness: number): ShadowGenerator {
  386. if (darkness >= 1.0)
  387. this._darkness = 1.0;
  388. else if (darkness <= 0.0)
  389. this._darkness = 0.0;
  390. else
  391. this._darkness = darkness;
  392. return this;
  393. }
  394. private _transparencyShadow = false;
  395. /**
  396. * Sets the ability to have transparent shadow (boolean).
  397. * @param transparent True if transparent else False
  398. * @returns the shadow generator allowing fluent coding
  399. */
  400. public setTransparencyShadow(transparent: boolean): ShadowGenerator {
  401. this._transparencyShadow = transparent;
  402. return this;
  403. }
  404. private _shadowMap: Nullable<RenderTargetTexture>;
  405. private _shadowMap2: Nullable<RenderTargetTexture>;
  406. /**
  407. * Gets the main RTT containing the shadow map (usually storing depth from the light point of view).
  408. * @returns The render target texture if present otherwise, null
  409. */
  410. public getShadowMap(): Nullable<RenderTargetTexture> {
  411. return this._shadowMap;
  412. }
  413. /**
  414. * Gets the RTT used during rendering (can be a blurred version of the shadow map or the shadow map itself).
  415. * @returns The render target texture if the shadow map is present otherwise, null
  416. */
  417. public getShadowMapForRendering(): Nullable<RenderTargetTexture> {
  418. if (this._shadowMap2) {
  419. return this._shadowMap2;
  420. }
  421. return this._shadowMap;
  422. }
  423. /**
  424. * Helper function to add a mesh and its descendants to the list of shadow casters.
  425. * @param mesh Mesh to add
  426. * @param includeDescendants boolean indicating if the descendants should be added. Default to true
  427. * @returns the Shadow Generator itself
  428. */
  429. public addShadowCaster(mesh: AbstractMesh, includeDescendants = true): ShadowGenerator {
  430. if (!this._shadowMap) {
  431. return this;
  432. }
  433. if (!this._shadowMap.renderList) {
  434. this._shadowMap.renderList = [];
  435. }
  436. this._shadowMap.renderList.push(mesh);
  437. if (includeDescendants) {
  438. this._shadowMap.renderList.push(...mesh.getChildMeshes());
  439. }
  440. return this;
  441. }
  442. /**
  443. * Helper function to remove a mesh and its descendants from the list of shadow casters
  444. * @param mesh Mesh to remove
  445. * @param includeDescendants boolean indicating if the descendants should be removed. Default to true
  446. * @returns the Shadow Generator itself
  447. */
  448. public removeShadowCaster(mesh: AbstractMesh, includeDescendants = true): ShadowGenerator {
  449. if (!this._shadowMap || !this._shadowMap.renderList) {
  450. return this;
  451. }
  452. var index = this._shadowMap.renderList.indexOf(mesh);
  453. if (index !== -1) {
  454. this._shadowMap.renderList.splice(index, 1);
  455. }
  456. if (includeDescendants) {
  457. for (var child of mesh.getChildren()) {
  458. this.removeShadowCaster(<any>child);
  459. }
  460. }
  461. return this;
  462. }
  463. /**
  464. * Controls the extent to which the shadows fade out at the edge of the frustum
  465. * Used only by directionals and spots
  466. */
  467. public frustumEdgeFalloff = 0;
  468. private _light: IShadowLight;
  469. /**
  470. * Returns the associated light object.
  471. * @returns the light generating the shadow
  472. */
  473. public getLight(): IShadowLight {
  474. return this._light;
  475. }
  476. /**
  477. * If true the shadow map is generated by rendering the back face of the mesh instead of the front face.
  478. * This can help with self-shadowing as the geometry making up the back of objects is slightly offset.
  479. * It might on the other hand introduce peter panning.
  480. */
  481. public forceBackFacesOnly = false;
  482. private _scene: Scene;
  483. private _lightDirection = Vector3.Zero();
  484. private _effect: Effect;
  485. private _viewMatrix = Matrix.Zero();
  486. private _projectionMatrix = Matrix.Zero();
  487. private _transformMatrix = Matrix.Zero();
  488. private _cachedPosition: Vector3;
  489. private _cachedDirection: Vector3;
  490. private _cachedDefines: string;
  491. private _currentRenderID: number;
  492. private _boxBlurPostprocess: Nullable<PostProcess>;
  493. private _kernelBlurXPostprocess: Nullable<PostProcess>;
  494. private _kernelBlurYPostprocess: Nullable<PostProcess>;
  495. private _blurPostProcesses: PostProcess[];
  496. private _mapSize: number;
  497. private _currentFaceIndex = 0;
  498. private _currentFaceIndexCache = 0;
  499. private _textureType: number;
  500. private _defaultTextureMatrix = Matrix.Identity();
  501. /**
  502. * Creates a ShadowGenerator object.
  503. * A ShadowGenerator is the required tool to use the shadows.
  504. * Each light casting shadows needs to use its own ShadowGenerator.
  505. * Documentation : http://doc.babylonjs.com/tutorials/shadows
  506. * @param mapSize The size of the texture what stores the shadows. Example : 1024.
  507. * @param light The light object generating the shadows.
  508. * @param useFullFloatFirst 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.
  509. */
  510. constructor(mapSize: number, light: IShadowLight, useFullFloatFirst?: boolean) {
  511. this._mapSize = mapSize;
  512. this._light = light;
  513. this._scene = light.getScene();
  514. light._shadowGenerator = this;
  515. // Texture type fallback from float to int if not supported.
  516. var caps = this._scene.getEngine().getCaps();
  517. if (!useFullFloatFirst) {
  518. if (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering) {
  519. this._textureType = Engine.TEXTURETYPE_HALF_FLOAT;
  520. }
  521. else if (caps.textureFloatRender && caps.textureFloatLinearFiltering) {
  522. this._textureType = Engine.TEXTURETYPE_FLOAT;
  523. }
  524. else {
  525. this._textureType = Engine.TEXTURETYPE_UNSIGNED_INT;
  526. }
  527. } else {
  528. if (caps.textureFloatRender && caps.textureFloatLinearFiltering) {
  529. this._textureType = Engine.TEXTURETYPE_FLOAT;
  530. }
  531. else if (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering) {
  532. this._textureType = Engine.TEXTURETYPE_HALF_FLOAT;
  533. }
  534. else {
  535. this._textureType = Engine.TEXTURETYPE_UNSIGNED_INT;
  536. }
  537. }
  538. this._initializeGenerator();
  539. }
  540. private _initializeGenerator(): void {
  541. this._light._markMeshesAsLightDirty();
  542. this._initializeShadowMap();
  543. }
  544. private _initializeShadowMap(): void {
  545. // Render target
  546. this._shadowMap = new RenderTargetTexture(this._light.name + "_shadowMap", this._mapSize, this._scene, false, true, this._textureType, this._light.needCube());
  547. this._shadowMap.wrapU = Texture.CLAMP_ADDRESSMODE;
  548. this._shadowMap.wrapV = Texture.CLAMP_ADDRESSMODE;
  549. this._shadowMap.anisotropicFilteringLevel = 1;
  550. this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  551. this._shadowMap.renderParticles = false;
  552. this._shadowMap.ignoreCameraViewport = true;
  553. // Record Face Index before render.
  554. this._shadowMap.onBeforeRenderObservable.add((faceIndex: number) => {
  555. this._currentFaceIndex = faceIndex;
  556. });
  557. // Custom render function.
  558. this._shadowMap.customRenderFunction = this._renderForShadowMap.bind(this);
  559. // Blur if required afer render.
  560. this._shadowMap.onAfterUnbindObservable.add(() => {
  561. if (!this.useBlurExponentialShadowMap && !this.useBlurCloseExponentialShadowMap) {
  562. return;
  563. }
  564. let shadowMap = this.getShadowMapForRendering();
  565. if (shadowMap) {
  566. this._scene.postProcessManager.directRender(this._blurPostProcesses, shadowMap.getInternalTexture(), true);
  567. }
  568. });
  569. // Clear according to the chosen filter.
  570. this._shadowMap.onClearObservable.add((engine: Engine) => {
  571. if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
  572. engine.clear(new Color4(0, 0, 0, 0), true, true, true);
  573. }
  574. else {
  575. engine.clear(new Color4(1.0, 1.0, 1.0, 1.0), true, true, true);
  576. }
  577. });
  578. }
  579. private _initializeBlurRTTAndPostProcesses(): void {
  580. var engine = this._scene.getEngine();
  581. var targetSize = this._mapSize / this.blurScale;
  582. if (!this.useKernelBlur || this.blurScale !== 1.0) {
  583. this._shadowMap2 = new RenderTargetTexture(this._light.name + "_shadowMap2", targetSize, this._scene, false, true, this._textureType);
  584. this._shadowMap2.wrapU = Texture.CLAMP_ADDRESSMODE;
  585. this._shadowMap2.wrapV = Texture.CLAMP_ADDRESSMODE;
  586. this._shadowMap2.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  587. }
  588. if (this.useKernelBlur) {
  589. this._kernelBlurXPostprocess = new BlurPostProcess(this._light.name + "KernelBlurX", new Vector2(1, 0), this.blurKernel, 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._textureType);
  590. this._kernelBlurXPostprocess.width = targetSize;
  591. this._kernelBlurXPostprocess.height = targetSize;
  592. this._kernelBlurXPostprocess.onApplyObservable.add(effect => {
  593. effect.setTexture("textureSampler", this._shadowMap);
  594. });
  595. this._kernelBlurYPostprocess = new BlurPostProcess(this._light.name + "KernelBlurY", new Vector2(0, 1), this.blurKernel, 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._textureType);
  596. this._kernelBlurXPostprocess.autoClear = false;
  597. this._kernelBlurYPostprocess.autoClear = false;
  598. if (this._textureType === Engine.TEXTURETYPE_UNSIGNED_INT) {
  599. (<BlurPostProcess>this._kernelBlurXPostprocess).packedFloat = true;
  600. (<BlurPostProcess>this._kernelBlurYPostprocess).packedFloat = true;
  601. }
  602. this._blurPostProcesses = [this._kernelBlurXPostprocess, this._kernelBlurYPostprocess];
  603. }
  604. else {
  605. this._boxBlurPostprocess = new PostProcess(this._light.name + "DepthBoxBlur", "depthBoxBlur", ["screenSize", "boxOffset"], [], 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, "#define OFFSET " + this._blurBoxOffset, this._textureType);
  606. this._boxBlurPostprocess.onApplyObservable.add(effect => {
  607. effect.setFloat2("screenSize", targetSize, targetSize);
  608. effect.setTexture("textureSampler", this._shadowMap);
  609. });
  610. this._boxBlurPostprocess.autoClear = false;
  611. this._blurPostProcesses = [this._boxBlurPostprocess];
  612. }
  613. }
  614. private _renderForShadowMap(opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>): void {
  615. var index: number;
  616. let engine = this._scene.getEngine();
  617. if (depthOnlySubMeshes.length) {
  618. engine.setColorWrite(false);
  619. for (index = 0; index < depthOnlySubMeshes.length; index++) {
  620. this._renderSubMeshForShadowMap(depthOnlySubMeshes.data[index]);
  621. }
  622. engine.setColorWrite(true);
  623. }
  624. for (index = 0; index < opaqueSubMeshes.length; index++) {
  625. this._renderSubMeshForShadowMap(opaqueSubMeshes.data[index]);
  626. }
  627. for (index = 0; index < alphaTestSubMeshes.length; index++) {
  628. this._renderSubMeshForShadowMap(alphaTestSubMeshes.data[index]);
  629. }
  630. if (this._transparencyShadow) {
  631. for (index = 0; index < transparentSubMeshes.length; index++) {
  632. this._renderSubMeshForShadowMap(transparentSubMeshes.data[index]);
  633. }
  634. }
  635. }
  636. private _renderSubMeshForShadowMap(subMesh: SubMesh): void {
  637. var mesh = subMesh.getRenderingMesh();
  638. var scene = this._scene;
  639. var engine = scene.getEngine();
  640. let material = subMesh.getMaterial();
  641. if (!material) {
  642. return;
  643. }
  644. // Culling
  645. engine.setState(material.backFaceCulling);
  646. // Managing instances
  647. var batch = mesh._getInstancesRenderList(subMesh._id);
  648. if (batch.mustReturn) {
  649. return;
  650. }
  651. var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
  652. if (this.isReady(subMesh, hardwareInstancedRendering)) {
  653. engine.enableEffect(this._effect);
  654. mesh._bind(subMesh, this._effect, Material.TriangleFillMode);
  655. this._effect.setFloat2("biasAndScale", this.bias, this.depthScale);
  656. this._effect.setMatrix("viewProjection", this.getTransformMatrix());
  657. this._effect.setVector3("lightPosition", this.getLight().position);
  658. if (scene.activeCamera) {
  659. this._effect.setFloat2("depthValues", this.getLight().getDepthMinZ(scene.activeCamera), this.getLight().getDepthMinZ(scene.activeCamera) + this.getLight().getDepthMaxZ(scene.activeCamera));
  660. }
  661. // Alpha test
  662. if (material && material.needAlphaTesting()) {
  663. var alphaTexture = material.getAlphaTestTexture();
  664. if (alphaTexture) {
  665. this._effect.setTexture("diffuseSampler", alphaTexture);
  666. this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix() || this._defaultTextureMatrix);
  667. }
  668. }
  669. // Bones
  670. if (mesh.useBones && mesh.computeBonesUsingShaders) {
  671. this._effect.setMatrices("mBones", (<Skeleton>mesh.skeleton).getTransformMatrices((mesh)));
  672. }
  673. if (this.forceBackFacesOnly) {
  674. engine.setState(true, 0, false, true);
  675. }
  676. // Draw
  677. mesh._processRendering(subMesh, this._effect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
  678. (isInstance, world) => this._effect.setMatrix("world", world));
  679. if (this.forceBackFacesOnly) {
  680. engine.setState(true, 0, false, false);
  681. }
  682. } else {
  683. // Need to reset refresh rate of the shadowMap
  684. if (this._shadowMap) {
  685. this._shadowMap.resetRefreshCounter();
  686. }
  687. }
  688. }
  689. private _applyFilterValues(): void {
  690. if (!this._shadowMap) {
  691. return;
  692. }
  693. if (this.filter === ShadowGenerator.FILTER_NONE) {
  694. this._shadowMap.updateSamplingMode(Texture.NEAREST_SAMPLINGMODE);
  695. } else {
  696. this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  697. }
  698. }
  699. /**
  700. * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
  701. * @param onCompiled Callback triggered at the and of the effects compilation
  702. * @param options Sets of optional options forcing the compilation with different modes
  703. */
  704. public forceCompilation(onCompiled?: (generator: ShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void {
  705. let localOptions = {
  706. useInstances: false,
  707. ...options
  708. };
  709. let shadowMap = this.getShadowMap();
  710. if (!shadowMap) {
  711. if (onCompiled) {
  712. onCompiled(this);
  713. }
  714. return;
  715. }
  716. let renderList = shadowMap.renderList;
  717. if (!renderList) {
  718. if (onCompiled) {
  719. onCompiled(this);
  720. }
  721. return;
  722. }
  723. var subMeshes = new Array<SubMesh>();
  724. for (var mesh of renderList) {
  725. subMeshes.push(...mesh.subMeshes);
  726. }
  727. if (subMeshes.length === 0) {
  728. if (onCompiled) {
  729. onCompiled(this);
  730. }
  731. return;
  732. }
  733. var currentIndex = 0;
  734. var checkReady = () => {
  735. if (!this._scene || !this._scene.getEngine()) {
  736. return;
  737. }
  738. while (this.isReady(subMeshes[currentIndex], localOptions.useInstances)) {
  739. currentIndex++;
  740. if (currentIndex >= subMeshes.length) {
  741. if (onCompiled) {
  742. onCompiled(this);
  743. }
  744. return;
  745. }
  746. }
  747. setTimeout(checkReady, 16);
  748. };
  749. checkReady();
  750. }
  751. /**
  752. * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
  753. * @param options Sets of optional options forcing the compilation with different modes
  754. * @returns A promise that resolves when the compilation completes
  755. */
  756. public forceCompilationAsync(options?: Partial<{ useInstances: boolean }>): Promise<void> {
  757. return new Promise(resolve => {
  758. this.forceCompilation(() => {
  759. resolve();
  760. }, options);
  761. });
  762. }
  763. /**
  764. * Determine wheter the shadow generator is ready or not (mainly all effects and related post processes needs to be ready).
  765. * @param subMesh The submesh we want to render in the shadow map
  766. * @param useInstances Defines wether will draw in the map using instances
  767. * @returns true if ready otherwise, false
  768. */
  769. public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
  770. var defines = [];
  771. if (this._textureType !== Engine.TEXTURETYPE_UNSIGNED_INT) {
  772. defines.push("#define FLOAT");
  773. }
  774. if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
  775. defines.push("#define ESM");
  776. }
  777. var attribs = [VertexBuffer.PositionKind];
  778. var mesh = subMesh.getMesh();
  779. var material = subMesh.getMaterial();
  780. // Alpha test
  781. if (material && material.needAlphaTesting()) {
  782. var alphaTexture = material.getAlphaTestTexture();
  783. if (alphaTexture) {
  784. defines.push("#define ALPHATEST");
  785. if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  786. attribs.push(VertexBuffer.UVKind);
  787. defines.push("#define UV1");
  788. }
  789. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
  790. if (alphaTexture.coordinatesIndex === 1) {
  791. attribs.push(VertexBuffer.UV2Kind);
  792. defines.push("#define UV2");
  793. }
  794. }
  795. }
  796. }
  797. // Bones
  798. if (mesh.useBones && mesh.computeBonesUsingShaders) {
  799. attribs.push(VertexBuffer.MatricesIndicesKind);
  800. attribs.push(VertexBuffer.MatricesWeightsKind);
  801. if (mesh.numBoneInfluencers > 4) {
  802. attribs.push(VertexBuffer.MatricesIndicesExtraKind);
  803. attribs.push(VertexBuffer.MatricesWeightsExtraKind);
  804. }
  805. defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
  806. defines.push("#define BonesPerMesh " + ((<Skeleton>mesh.skeleton).bones.length + 1));
  807. } else {
  808. defines.push("#define NUM_BONE_INFLUENCERS 0");
  809. }
  810. // Instances
  811. if (useInstances) {
  812. defines.push("#define INSTANCES");
  813. attribs.push("world0");
  814. attribs.push("world1");
  815. attribs.push("world2");
  816. attribs.push("world3");
  817. }
  818. // Get correct effect
  819. var join = defines.join("\n");
  820. if (this._cachedDefines !== join) {
  821. this._cachedDefines = join;
  822. this._effect = this._scene.getEngine().createEffect("shadowMap",
  823. attribs,
  824. ["world", "mBones", "viewProjection", "diffuseMatrix", "lightPosition", "depthValues", "biasAndScale"],
  825. ["diffuseSampler"], join);
  826. }
  827. if (!this._effect.isReady()) {
  828. return false;
  829. }
  830. if (this.useBlurExponentialShadowMap || this.useBlurCloseExponentialShadowMap) {
  831. if (!this._blurPostProcesses || !this._blurPostProcesses.length) {
  832. this._initializeBlurRTTAndPostProcesses();
  833. }
  834. }
  835. if (this._kernelBlurXPostprocess && !this._kernelBlurXPostprocess.isReady()) {
  836. return false;
  837. }
  838. if (this._kernelBlurYPostprocess && !this._kernelBlurYPostprocess.isReady()) {
  839. return false;
  840. }
  841. if (this._boxBlurPostprocess && !this._boxBlurPostprocess.isReady()) {
  842. return false;
  843. }
  844. return true;
  845. }
  846. /**
  847. * Prepare all the defines in a material relying on a shadow map at the specified light index.
  848. * @param defines Defines of the material we want to update
  849. * @param lightIndex Index of the light in the enabled light list of the material
  850. */
  851. public prepareDefines(defines: any, lightIndex: number): void {
  852. var scene = this._scene;
  853. var light = this._light;
  854. if (!scene.shadowsEnabled || !light.shadowEnabled) {
  855. return;
  856. }
  857. defines["SHADOW" + lightIndex] = true;
  858. if (this.usePoissonSampling) {
  859. defines["SHADOWPCF" + lightIndex] = true;
  860. }
  861. else if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
  862. defines["SHADOWESM" + lightIndex] = true;
  863. }
  864. else if (this.useCloseExponentialShadowMap || this.useBlurCloseExponentialShadowMap) {
  865. defines["SHADOWCLOSEESM" + lightIndex] = true;
  866. }
  867. if (light.needCube()) {
  868. defines["SHADOWCUBE" + lightIndex] = true;
  869. }
  870. }
  871. /**
  872. * Binds the shadow related information inside of an effect (information like near, far, darkness...
  873. * defined in the generator but impacting the effect).
  874. * @param lightIndex Index of the light in the enabled light list of the material owning the effect
  875. * @param effect The effect we are binfing the information for
  876. */
  877. public bindShadowLight(lightIndex: string, effect: Effect): void {
  878. var light = this._light;
  879. var scene = this._scene;
  880. if (!scene.shadowsEnabled || !light.shadowEnabled) {
  881. return;
  882. }
  883. let camera = scene.activeCamera;
  884. if (!camera) {
  885. return;
  886. }
  887. let shadowMap = this.getShadowMap();
  888. if (!shadowMap) {
  889. return;
  890. }
  891. if (!light.needCube()) {
  892. effect.setMatrix("lightMatrix" + lightIndex, this.getTransformMatrix());
  893. }
  894. effect.setTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
  895. light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), this.blurScale / shadowMap.getSize().width, this.depthScale, this.frustumEdgeFalloff, lightIndex);
  896. light._uniformBuffer.updateFloat2("depthValues", this.getLight().getDepthMinZ(camera), this.getLight().getDepthMinZ(camera) + this.getLight().getDepthMaxZ(camera), lightIndex);
  897. }
  898. /**
  899. * Gets the transformation matrix used to project the meshes into the map from the light point of view.
  900. * (eq to shadow prjection matrix * light transform matrix)
  901. * @returns The transform matrix used to create the shadow map
  902. */
  903. public getTransformMatrix(): Matrix {
  904. var scene = this._scene;
  905. if (this._currentRenderID === scene.getRenderId() && this._currentFaceIndexCache === this._currentFaceIndex) {
  906. return this._transformMatrix;
  907. }
  908. this._currentRenderID = scene.getRenderId();
  909. this._currentFaceIndexCache = this._currentFaceIndex;
  910. var lightPosition = this._light.position;
  911. if (this._light.computeTransformedInformation()) {
  912. lightPosition = this._light.transformedPosition;
  913. }
  914. Vector3.NormalizeToRef(this._light.getShadowDirection(this._currentFaceIndex), this._lightDirection);
  915. if (Math.abs(Vector3.Dot(this._lightDirection, Vector3.Up())) === 1.0) {
  916. this._lightDirection.z = 0.0000000000001; // Required to avoid perfectly perpendicular light
  917. }
  918. if (this._light.needProjectionMatrixCompute() || !this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !this._lightDirection.equals(this._cachedDirection)) {
  919. this._cachedPosition = lightPosition.clone();
  920. this._cachedDirection = this._lightDirection.clone();
  921. Matrix.LookAtLHToRef(lightPosition, lightPosition.add(this._lightDirection), Vector3.Up(), this._viewMatrix);
  922. let shadowMap = this.getShadowMap();
  923. if (shadowMap) {
  924. let renderList = shadowMap.renderList;
  925. if (renderList) {
  926. this._light.setShadowProjectionMatrix(this._projectionMatrix, this._viewMatrix, renderList);
  927. }
  928. }
  929. this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
  930. }
  931. return this._transformMatrix;
  932. }
  933. /**
  934. * Recreates the shadow map dependencies like RTT and post processes. This can be used during the switch between
  935. * Cube and 2D textures for instance.
  936. */
  937. public recreateShadowMap(): void {
  938. let shadowMap = this._shadowMap;
  939. if (!shadowMap) {
  940. return;
  941. }
  942. // Track render list.
  943. var renderList = shadowMap.renderList;
  944. // Clean up existing data.
  945. this._disposeRTTandPostProcesses();
  946. // Reinitializes.
  947. this._initializeGenerator();
  948. // Reaffect the filter to ensure a correct fallback if necessary.
  949. this.filter = this.filter;
  950. // Reaffect the filter.
  951. this._applyFilterValues();
  952. // Reaffect Render List.
  953. this._shadowMap!.renderList = renderList;
  954. }
  955. private _disposeBlurPostProcesses(): void {
  956. if (this._shadowMap2) {
  957. this._shadowMap2.dispose();
  958. this._shadowMap2 = null;
  959. }
  960. if (this._boxBlurPostprocess) {
  961. this._boxBlurPostprocess.dispose();
  962. this._boxBlurPostprocess = null;
  963. }
  964. if (this._kernelBlurXPostprocess) {
  965. this._kernelBlurXPostprocess.dispose();
  966. this._kernelBlurXPostprocess = null;
  967. }
  968. if (this._kernelBlurYPostprocess) {
  969. this._kernelBlurYPostprocess.dispose();
  970. this._kernelBlurYPostprocess = null;
  971. }
  972. this._blurPostProcesses = [];
  973. }
  974. private _disposeRTTandPostProcesses(): void {
  975. if (this._shadowMap) {
  976. this._shadowMap.dispose();
  977. this._shadowMap = null;
  978. }
  979. this._disposeBlurPostProcesses();
  980. }
  981. /**
  982. * Disposes the ShadowGenerator.
  983. * Returns nothing.
  984. */
  985. public dispose(): void {
  986. this._disposeRTTandPostProcesses();
  987. if (this._light) {
  988. this._light._shadowGenerator = null;
  989. this._light._markMeshesAsLightDirty();
  990. }
  991. }
  992. /**
  993. * Serializes the shadow generator setup to a json object.
  994. * @returns The serialized JSON object
  995. */
  996. public serialize(): any {
  997. var serializationObject: any = {};
  998. var shadowMap = this.getShadowMap();
  999. if (!shadowMap) {
  1000. return serializationObject;
  1001. }
  1002. serializationObject.lightId = this._light.id;
  1003. serializationObject.mapSize = shadowMap.getRenderSize();
  1004. serializationObject.useExponentialShadowMap = this.useExponentialShadowMap;
  1005. serializationObject.useBlurExponentialShadowMap = this.useBlurExponentialShadowMap;
  1006. serializationObject.useCloseExponentialShadowMap = this.useBlurExponentialShadowMap;
  1007. serializationObject.useBlurCloseExponentialShadowMap = this.useBlurExponentialShadowMap;
  1008. serializationObject.usePoissonSampling = this.usePoissonSampling;
  1009. serializationObject.forceBackFacesOnly = this.forceBackFacesOnly;
  1010. serializationObject.depthScale = this.depthScale;
  1011. serializationObject.darkness = this.getDarkness();
  1012. serializationObject.blurBoxOffset = this.blurBoxOffset;
  1013. serializationObject.blurKernel = this.blurKernel;
  1014. serializationObject.blurScale = this.blurScale;
  1015. serializationObject.useKernelBlur = this.useKernelBlur;
  1016. serializationObject.transparencyShadow = this._transparencyShadow;
  1017. serializationObject.renderList = [];
  1018. if (shadowMap.renderList) {
  1019. for (var meshIndex = 0; meshIndex < shadowMap.renderList.length; meshIndex++) {
  1020. var mesh = shadowMap.renderList[meshIndex];
  1021. serializationObject.renderList.push(mesh.id);
  1022. }
  1023. }
  1024. return serializationObject;
  1025. }
  1026. /**
  1027. * Parses a serialized ShadowGenerator and returns a new ShadowGenerator.
  1028. * @param parsedShadowGenerator The JSON object to parse
  1029. * @param scene The scene to create the shadow map for
  1030. * @returns The parsed shadow generator
  1031. */
  1032. public static Parse(parsedShadowGenerator: any, scene: Scene): ShadowGenerator {
  1033. //casting to point light, as light is missing the position attr and typescript complains.
  1034. var light = <PointLight>scene.getLightByID(parsedShadowGenerator.lightId);
  1035. var shadowGenerator = new ShadowGenerator(parsedShadowGenerator.mapSize, light);
  1036. var shadowMap = shadowGenerator.getShadowMap();
  1037. for (var meshIndex = 0; meshIndex < parsedShadowGenerator.renderList.length; meshIndex++) {
  1038. var meshes = scene.getMeshesByID(parsedShadowGenerator.renderList[meshIndex]);
  1039. meshes.forEach(function (mesh) {
  1040. if (!shadowMap) {
  1041. return;
  1042. }
  1043. if (!shadowMap.renderList) {
  1044. shadowMap.renderList = [];
  1045. }
  1046. shadowMap.renderList.push(mesh);
  1047. });
  1048. }
  1049. if (parsedShadowGenerator.usePoissonSampling) {
  1050. shadowGenerator.usePoissonSampling = true;
  1051. }
  1052. else if (parsedShadowGenerator.useExponentialShadowMap) {
  1053. shadowGenerator.useExponentialShadowMap = true;
  1054. }
  1055. else if (parsedShadowGenerator.useBlurExponentialShadowMap) {
  1056. shadowGenerator.useBlurExponentialShadowMap = true;
  1057. }
  1058. else if (parsedShadowGenerator.useCloseExponentialShadowMap) {
  1059. shadowGenerator.useCloseExponentialShadowMap = true;
  1060. }
  1061. else if (parsedShadowGenerator.useBlurCloseExponentialShadowMap) {
  1062. shadowGenerator.useBlurCloseExponentialShadowMap = true;
  1063. }
  1064. // Backward compat
  1065. else if (parsedShadowGenerator.useVarianceShadowMap) {
  1066. shadowGenerator.useExponentialShadowMap = true;
  1067. }
  1068. else if (parsedShadowGenerator.useBlurVarianceShadowMap) {
  1069. shadowGenerator.useBlurExponentialShadowMap = true;
  1070. }
  1071. if (parsedShadowGenerator.depthScale) {
  1072. shadowGenerator.depthScale = parsedShadowGenerator.depthScale;
  1073. }
  1074. if (parsedShadowGenerator.blurScale) {
  1075. shadowGenerator.blurScale = parsedShadowGenerator.blurScale;
  1076. }
  1077. if (parsedShadowGenerator.blurBoxOffset) {
  1078. shadowGenerator.blurBoxOffset = parsedShadowGenerator.blurBoxOffset;
  1079. }
  1080. if (parsedShadowGenerator.useKernelBlur) {
  1081. shadowGenerator.useKernelBlur = parsedShadowGenerator.useKernelBlur;
  1082. }
  1083. if (parsedShadowGenerator.blurKernel) {
  1084. shadowGenerator.blurKernel = parsedShadowGenerator.blurKernel;
  1085. }
  1086. if (parsedShadowGenerator.bias !== undefined) {
  1087. shadowGenerator.bias = parsedShadowGenerator.bias;
  1088. }
  1089. if (parsedShadowGenerator.darkness) {
  1090. shadowGenerator.setDarkness(parsedShadowGenerator.darkness);
  1091. }
  1092. if (parsedShadowGenerator.transparencyShadow) {
  1093. shadowGenerator.setTransparencyShadow(true);
  1094. }
  1095. shadowGenerator.forceBackFacesOnly = parsedShadowGenerator.forceBackFacesOnly;
  1096. return shadowGenerator;
  1097. }
  1098. }
  1099. }