shadowGenerator.ts 73 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855
  1. import { SmartArray } from "../../Misc/smartArray";
  2. import { Nullable } from "../../types";
  3. import { Scene } from "../../scene";
  4. import { Matrix, Vector3, Vector2 } 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 { IShadowLight } from "../../Lights/shadowLight";
  11. import { Light } from "../../Lights/light";
  12. import { MaterialDefines } from "../../Materials/materialDefines";
  13. import { MaterialHelper } from "../../Materials/materialHelper";
  14. import { Effect } from "../../Materials/effect";
  15. import { Texture } from "../../Materials/Textures/texture";
  16. import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture";
  17. import { PostProcess } from "../../PostProcesses/postProcess";
  18. import { BlurPostProcess } from "../../PostProcesses/blurPostProcess";
  19. import { Constants } from "../../Engines/constants";
  20. import "../../Shaders/shadowMap.fragment";
  21. import "../../Shaders/shadowMap.vertex";
  22. import "../../Shaders/depthBoxBlur.fragment";
  23. import "../../Shaders/ShadersInclude/shadowMapFragmentSoftTransparentShadow";
  24. import { Observable } from '../../Misc/observable';
  25. import { _DevTools } from '../../Misc/devTools';
  26. import { EffectFallbacks } from '../../Materials/effectFallbacks';
  27. import { RenderingManager } from '../../Rendering/renderingManager';
  28. const tmpMatrix = new Matrix(),
  29. tmpMatrix2 = new Matrix();
  30. /**
  31. * Defines the options associated with the creation of a custom shader for a shadow generator.
  32. */
  33. export interface ICustomShaderOptions {
  34. /**
  35. * Gets or sets the custom shader name to use
  36. */
  37. shaderName: string;
  38. /**
  39. * The list of attribute names used in the shader
  40. */
  41. attributes?: string[];
  42. /**
  43. * The list of unifrom names used in the shader
  44. */
  45. uniforms?: string[];
  46. /**
  47. * The list of sampler names used in the shader
  48. */
  49. samplers?: string[];
  50. /**
  51. * The list of defines used in the shader
  52. */
  53. defines?: string[];
  54. }
  55. /**
  56. * Interface to implement to create a shadow generator compatible with BJS.
  57. */
  58. export interface IShadowGenerator {
  59. /**
  60. * Gets the main RTT containing the shadow map (usually storing depth from the light point of view).
  61. * @returns The render target texture if present otherwise, null
  62. */
  63. getShadowMap(): Nullable<RenderTargetTexture>;
  64. /**
  65. * Determine wheter the shadow generator is ready or not (mainly all effects and related post processes needs to be ready).
  66. * @param subMesh The submesh we want to render in the shadow map
  67. * @param useInstances Defines wether will draw in the map using instances
  68. * @param isTransparent Indicates that isReady is called for a transparent subMesh
  69. * @returns true if ready otherwise, false
  70. */
  71. isReady(subMesh: SubMesh, useInstances: boolean, isTransparent: boolean): boolean;
  72. /**
  73. * Prepare all the defines in a material relying on a shadow map at the specified light index.
  74. * @param defines Defines of the material we want to update
  75. * @param lightIndex Index of the light in the enabled light list of the material
  76. */
  77. prepareDefines(defines: MaterialDefines, lightIndex: number): void;
  78. /**
  79. * Binds the shadow related information inside of an effect (information like near, far, darkness...
  80. * defined in the generator but impacting the effect).
  81. * It implies the unifroms available on the materials are the standard BJS ones.
  82. * @param lightIndex Index of the light in the enabled light list of the material owning the effect
  83. * @param effect The effect we are binfing the information for
  84. */
  85. bindShadowLight(lightIndex: string, effect: Effect): void;
  86. /**
  87. * Gets the transformation matrix used to project the meshes into the map from the light point of view.
  88. * (eq to shadow prjection matrix * light transform matrix)
  89. * @returns The transform matrix used to create the shadow map
  90. */
  91. getTransformMatrix(): Matrix;
  92. /**
  93. * Recreates the shadow map dependencies like RTT and post processes. This can be used during the switch between
  94. * Cube and 2D textures for instance.
  95. */
  96. recreateShadowMap(): void;
  97. /**
  98. * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
  99. * @param onCompiled Callback triggered at the and of the effects compilation
  100. * @param options Sets of optional options forcing the compilation with different modes
  101. */
  102. forceCompilation(onCompiled?: (generator: IShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void;
  103. /**
  104. * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
  105. * @param options Sets of optional options forcing the compilation with different modes
  106. * @returns A promise that resolves when the compilation completes
  107. */
  108. forceCompilationAsync(options?: Partial<{ useInstances: boolean }>): Promise<void>;
  109. /**
  110. * Serializes the shadow generator setup to a json object.
  111. * @returns The serialized JSON object
  112. */
  113. serialize(): any;
  114. /**
  115. * Disposes the Shadow map and related Textures and effects.
  116. */
  117. dispose(): void;
  118. }
  119. /**
  120. * Default implementation IShadowGenerator.
  121. * This is the main object responsible of generating shadows in the framework.
  122. * Documentation: https://doc.babylonjs.com/babylon101/shadows
  123. */
  124. export class ShadowGenerator implements IShadowGenerator {
  125. /**
  126. * Name of the shadow generator class
  127. */
  128. public static CLASSNAME = "ShadowGenerator";
  129. /**
  130. * Shadow generator mode None: no filtering applied.
  131. */
  132. public static readonly FILTER_NONE = 0;
  133. /**
  134. * Shadow generator mode ESM: Exponential Shadow Mapping.
  135. * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
  136. */
  137. public static readonly FILTER_EXPONENTIALSHADOWMAP = 1;
  138. /**
  139. * Shadow generator mode Poisson Sampling: Percentage Closer Filtering.
  140. * (Multiple Tap around evenly distributed around the pixel are used to evaluate the shadow strength)
  141. */
  142. public static readonly FILTER_POISSONSAMPLING = 2;
  143. /**
  144. * Shadow generator mode ESM: Blurred Exponential Shadow Mapping.
  145. * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
  146. */
  147. public static readonly FILTER_BLUREXPONENTIALSHADOWMAP = 3;
  148. /**
  149. * Shadow generator mode ESM: Exponential Shadow Mapping using the inverse of the exponential preventing
  150. * edge artifacts on steep falloff.
  151. * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
  152. */
  153. public static readonly FILTER_CLOSEEXPONENTIALSHADOWMAP = 4;
  154. /**
  155. * Shadow generator mode ESM: Blurred Exponential Shadow Mapping using the inverse of the exponential preventing
  156. * edge artifacts on steep falloff.
  157. * (http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf)
  158. */
  159. public static readonly FILTER_BLURCLOSEEXPONENTIALSHADOWMAP = 5;
  160. /**
  161. * Shadow generator mode PCF: Percentage Closer Filtering
  162. * benefits from Webgl 2 shadow samplers. Fallback to Poisson Sampling in Webgl 1
  163. * (https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch11.html)
  164. */
  165. public static readonly FILTER_PCF = 6;
  166. /**
  167. * Shadow generator mode PCSS: Percentage Closering Soft Shadow.
  168. * benefits from Webgl 2 shadow samplers. Fallback to Poisson Sampling in Webgl 1
  169. * Contact Hardening
  170. */
  171. public static readonly FILTER_PCSS = 7;
  172. /**
  173. * Reserved for PCF and PCSS
  174. * Highest Quality.
  175. *
  176. * Execute PCF on a 5*5 kernel improving a lot the shadow aliasing artifacts.
  177. *
  178. * Execute PCSS with 32 taps blocker search and 64 taps PCF.
  179. */
  180. public static readonly QUALITY_HIGH = 0;
  181. /**
  182. * Reserved for PCF and PCSS
  183. * Good tradeoff for quality/perf cross devices
  184. *
  185. * Execute PCF on a 3*3 kernel.
  186. *
  187. * Execute PCSS with 16 taps blocker search and 32 taps PCF.
  188. */
  189. public static readonly QUALITY_MEDIUM = 1;
  190. /**
  191. * Reserved for PCF and PCSS
  192. * The lowest quality but the fastest.
  193. *
  194. * Execute PCF on a 1*1 kernel.
  195. *
  196. * Execute PCSS with 16 taps blocker search and 16 taps PCF.
  197. */
  198. public static readonly QUALITY_LOW = 2;
  199. /** Gets or sets the custom shader name to use */
  200. public customShaderOptions: ICustomShaderOptions;
  201. /**
  202. * Observable triggered before the shadow is rendered. Can be used to update internal effect state
  203. */
  204. public onBeforeShadowMapRenderObservable = new Observable<Effect>();
  205. /**
  206. * Observable triggered after the shadow is rendered. Can be used to restore internal effect state
  207. */
  208. public onAfterShadowMapRenderObservable = new Observable<Effect>();
  209. /**
  210. * Observable triggered before a mesh is rendered in the shadow map.
  211. * Can be used to update internal effect state (that you can get from the onBeforeShadowMapRenderObservable)
  212. */
  213. public onBeforeShadowMapRenderMeshObservable = new Observable<Mesh>();
  214. /**
  215. * Observable triggered after a mesh is rendered in the shadow map.
  216. * Can be used to update internal effect state (that you can get from the onAfterShadowMapRenderObservable)
  217. */
  218. public onAfterShadowMapRenderMeshObservable = new Observable<Mesh>();
  219. protected _bias = 0.00005;
  220. /**
  221. * Gets the bias: offset applied on the depth preventing acnea (in light direction).
  222. */
  223. public get bias(): number {
  224. return this._bias;
  225. }
  226. /**
  227. * Sets the bias: offset applied on the depth preventing acnea (in light direction).
  228. */
  229. public set bias(bias: number) {
  230. this._bias = bias;
  231. }
  232. protected _normalBias = 0;
  233. /**
  234. * Gets the normalBias: offset applied on the depth preventing acnea (along side the normal direction and proportinal to the light/normal angle).
  235. */
  236. public get normalBias(): number {
  237. return this._normalBias;
  238. }
  239. /**
  240. * Sets the normalBias: offset applied on the depth preventing acnea (along side the normal direction and proportinal to the light/normal angle).
  241. */
  242. public set normalBias(normalBias: number) {
  243. this._normalBias = normalBias;
  244. }
  245. protected _blurBoxOffset = 1;
  246. /**
  247. * Gets the blur box offset: offset applied during the blur pass.
  248. * Only useful if useKernelBlur = false
  249. */
  250. public get blurBoxOffset(): number {
  251. return this._blurBoxOffset;
  252. }
  253. /**
  254. * Sets the blur box offset: offset applied during the blur pass.
  255. * Only useful if useKernelBlur = false
  256. */
  257. public set blurBoxOffset(value: number) {
  258. if (this._blurBoxOffset === value) {
  259. return;
  260. }
  261. this._blurBoxOffset = value;
  262. this._disposeBlurPostProcesses();
  263. }
  264. protected _blurScale = 2;
  265. /**
  266. * Gets the blur scale: scale of the blurred texture compared to the main shadow map.
  267. * 2 means half of the size.
  268. */
  269. public get blurScale(): number {
  270. return this._blurScale;
  271. }
  272. /**
  273. * Sets the blur scale: scale of the blurred texture compared to the main shadow map.
  274. * 2 means half of the size.
  275. */
  276. public set blurScale(value: number) {
  277. if (this._blurScale === value) {
  278. return;
  279. }
  280. this._blurScale = value;
  281. this._disposeBlurPostProcesses();
  282. }
  283. protected _blurKernel = 1;
  284. /**
  285. * Gets the blur kernel: kernel size of the blur pass.
  286. * Only useful if useKernelBlur = true
  287. */
  288. public get blurKernel(): number {
  289. return this._blurKernel;
  290. }
  291. /**
  292. * Sets the blur kernel: kernel size of the blur pass.
  293. * Only useful if useKernelBlur = true
  294. */
  295. public set blurKernel(value: number) {
  296. if (this._blurKernel === value) {
  297. return;
  298. }
  299. this._blurKernel = value;
  300. this._disposeBlurPostProcesses();
  301. }
  302. protected _useKernelBlur = false;
  303. /**
  304. * Gets whether the blur pass is a kernel blur (if true) or box blur.
  305. * Only useful in filtered mode (useBlurExponentialShadowMap...)
  306. */
  307. public get useKernelBlur(): boolean {
  308. return this._useKernelBlur;
  309. }
  310. /**
  311. * Sets whether the blur pass is a kernel blur (if true) or box blur.
  312. * Only useful in filtered mode (useBlurExponentialShadowMap...)
  313. */
  314. public set useKernelBlur(value: boolean) {
  315. if (this._useKernelBlur === value) {
  316. return;
  317. }
  318. this._useKernelBlur = value;
  319. this._disposeBlurPostProcesses();
  320. }
  321. protected _depthScale: number;
  322. /**
  323. * Gets the depth scale used in ESM mode.
  324. */
  325. public get depthScale(): number {
  326. return this._depthScale !== undefined ? this._depthScale : this._light.getDepthScale();
  327. }
  328. /**
  329. * Sets the depth scale used in ESM mode.
  330. * This can override the scale stored on the light.
  331. */
  332. public set depthScale(value: number) {
  333. this._depthScale = value;
  334. }
  335. protected _validateFilter(filter: number): number {
  336. return filter;
  337. }
  338. protected _filter = ShadowGenerator.FILTER_NONE;
  339. /**
  340. * Gets the current mode of the shadow generator (normal, PCF, ESM...).
  341. * The returned value is a number equal to one of the available mode defined in ShadowMap.FILTER_x like _FILTER_NONE
  342. */
  343. public get filter(): number {
  344. return this._filter;
  345. }
  346. /**
  347. * Sets the current mode of the shadow generator (normal, PCF, ESM...).
  348. * The returned value is a number equal to one of the available mode defined in ShadowMap.FILTER_x like _FILTER_NONE
  349. */
  350. public set filter(value: number) {
  351. value = this._validateFilter(value);
  352. // Blurring the cubemap is going to be too expensive. Reverting to unblurred version
  353. if (this._light.needCube()) {
  354. if (value === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP) {
  355. this.useExponentialShadowMap = true;
  356. return;
  357. }
  358. else if (value === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) {
  359. this.useCloseExponentialShadowMap = true;
  360. return;
  361. }
  362. // PCF on cubemap would also be expensive
  363. else if (value === ShadowGenerator.FILTER_PCF || value === ShadowGenerator.FILTER_PCSS) {
  364. this.usePoissonSampling = true;
  365. return;
  366. }
  367. }
  368. // Weblg1 fallback for PCF.
  369. if (value === ShadowGenerator.FILTER_PCF || value === ShadowGenerator.FILTER_PCSS) {
  370. if (this._scene.getEngine().webGLVersion === 1) {
  371. this.usePoissonSampling = true;
  372. return;
  373. }
  374. }
  375. if (this._filter === value) {
  376. return;
  377. }
  378. this._filter = value;
  379. this._disposeBlurPostProcesses();
  380. this._applyFilterValues();
  381. this._light._markMeshesAsLightDirty();
  382. }
  383. /**
  384. * Gets if the current filter is set to Poisson Sampling.
  385. */
  386. public get usePoissonSampling(): boolean {
  387. return this.filter === ShadowGenerator.FILTER_POISSONSAMPLING;
  388. }
  389. /**
  390. * Sets the current filter to Poisson Sampling.
  391. */
  392. public set usePoissonSampling(value: boolean) {
  393. let filter = this._validateFilter(ShadowGenerator.FILTER_POISSONSAMPLING);
  394. if (!value && this.filter !== ShadowGenerator.FILTER_POISSONSAMPLING) {
  395. return;
  396. }
  397. this.filter = (value ? filter : ShadowGenerator.FILTER_NONE);
  398. }
  399. /**
  400. * Gets if the current filter is set to ESM.
  401. */
  402. public get useExponentialShadowMap(): boolean {
  403. return this.filter === ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP;
  404. }
  405. /**
  406. * Sets the current filter is to ESM.
  407. */
  408. public set useExponentialShadowMap(value: boolean) {
  409. let filter = this._validateFilter(ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP);
  410. if (!value && this.filter !== ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP) {
  411. return;
  412. }
  413. this.filter = (value ? filter : ShadowGenerator.FILTER_NONE);
  414. }
  415. /**
  416. * Gets if the current filter is set to filtered ESM.
  417. */
  418. public get useBlurExponentialShadowMap(): boolean {
  419. return this.filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP;
  420. }
  421. /**
  422. * Gets if the current filter is set to filtered ESM.
  423. */
  424. public set useBlurExponentialShadowMap(value: boolean) {
  425. let filter = this._validateFilter(ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP);
  426. if (!value && this.filter !== ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP) {
  427. return;
  428. }
  429. this.filter = (value ? filter : ShadowGenerator.FILTER_NONE);
  430. }
  431. /**
  432. * Gets if the current filter is set to "close ESM" (using the inverse of the
  433. * exponential to prevent steep falloff artifacts).
  434. */
  435. public get useCloseExponentialShadowMap(): boolean {
  436. return this.filter === ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP;
  437. }
  438. /**
  439. * Sets the current filter to "close ESM" (using the inverse of the
  440. * exponential to prevent steep falloff artifacts).
  441. */
  442. public set useCloseExponentialShadowMap(value: boolean) {
  443. let filter = this._validateFilter(ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP);
  444. if (!value && this.filter !== ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP) {
  445. return;
  446. }
  447. this.filter = (value ? filter : ShadowGenerator.FILTER_NONE);
  448. }
  449. /**
  450. * Gets if the current filter is set to filtered "close ESM" (using the inverse of the
  451. * exponential to prevent steep falloff artifacts).
  452. */
  453. public get useBlurCloseExponentialShadowMap(): boolean {
  454. return this.filter === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP;
  455. }
  456. /**
  457. * Sets the current filter to filtered "close ESM" (using the inverse of the
  458. * exponential to prevent steep falloff artifacts).
  459. */
  460. public set useBlurCloseExponentialShadowMap(value: boolean) {
  461. let filter = this._validateFilter(ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP);
  462. if (!value && this.filter !== ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) {
  463. return;
  464. }
  465. this.filter = (value ? filter : ShadowGenerator.FILTER_NONE);
  466. }
  467. /**
  468. * Gets if the current filter is set to "PCF" (percentage closer filtering).
  469. */
  470. public get usePercentageCloserFiltering(): boolean {
  471. return this.filter === ShadowGenerator.FILTER_PCF;
  472. }
  473. /**
  474. * Sets the current filter to "PCF" (percentage closer filtering).
  475. */
  476. public set usePercentageCloserFiltering(value: boolean) {
  477. let filter = this._validateFilter(ShadowGenerator.FILTER_PCF);
  478. if (!value && this.filter !== ShadowGenerator.FILTER_PCF) {
  479. return;
  480. }
  481. this.filter = (value ? filter : ShadowGenerator.FILTER_NONE);
  482. }
  483. protected _filteringQuality = ShadowGenerator.QUALITY_HIGH;
  484. /**
  485. * Gets the PCF or PCSS Quality.
  486. * Only valid if usePercentageCloserFiltering or usePercentageCloserFiltering is true.
  487. */
  488. public get filteringQuality(): number {
  489. return this._filteringQuality;
  490. }
  491. /**
  492. * Sets the PCF or PCSS Quality.
  493. * Only valid if usePercentageCloserFiltering or usePercentageCloserFiltering is true.
  494. */
  495. public set filteringQuality(filteringQuality: number) {
  496. if (this._filteringQuality === filteringQuality) {
  497. return;
  498. }
  499. this._filteringQuality = filteringQuality;
  500. this._disposeBlurPostProcesses();
  501. this._applyFilterValues();
  502. this._light._markMeshesAsLightDirty();
  503. }
  504. /**
  505. * Gets if the current filter is set to "PCSS" (contact hardening).
  506. */
  507. public get useContactHardeningShadow(): boolean {
  508. return this.filter === ShadowGenerator.FILTER_PCSS;
  509. }
  510. /**
  511. * Sets the current filter to "PCSS" (contact hardening).
  512. */
  513. public set useContactHardeningShadow(value: boolean) {
  514. let filter = this._validateFilter(ShadowGenerator.FILTER_PCSS);
  515. if (!value && this.filter !== ShadowGenerator.FILTER_PCSS) {
  516. return;
  517. }
  518. this.filter = (value ? filter : ShadowGenerator.FILTER_NONE);
  519. }
  520. protected _contactHardeningLightSizeUVRatio = 0.1;
  521. /**
  522. * Gets the Light Size (in shadow map uv unit) used in PCSS to determine the blocker search area and the penumbra size.
  523. * Using a ratio helps keeping shape stability independently of the map size.
  524. *
  525. * It does not account for the light projection as it was having too much
  526. * instability during the light setup or during light position changes.
  527. *
  528. * Only valid if useContactHardeningShadow is true.
  529. */
  530. public get contactHardeningLightSizeUVRatio(): number {
  531. return this._contactHardeningLightSizeUVRatio;
  532. }
  533. /**
  534. * Sets the Light Size (in shadow map uv unit) used in PCSS to determine the blocker search area and the penumbra size.
  535. * Using a ratio helps keeping shape stability independently of the map size.
  536. *
  537. * It does not account for the light projection as it was having too much
  538. * instability during the light setup or during light position changes.
  539. *
  540. * Only valid if useContactHardeningShadow is true.
  541. */
  542. public set contactHardeningLightSizeUVRatio(contactHardeningLightSizeUVRatio: number) {
  543. this._contactHardeningLightSizeUVRatio = contactHardeningLightSizeUVRatio;
  544. }
  545. protected _darkness = 0;
  546. /** Gets or sets the actual darkness of a shadow */
  547. public get darkness() {
  548. return this._darkness;
  549. }
  550. public set darkness(value: number) {
  551. this.setDarkness(value);
  552. }
  553. /**
  554. * Returns the darkness value (float). This can only decrease the actual darkness of a shadow.
  555. * 0 means strongest and 1 would means no shadow.
  556. * @returns the darkness.
  557. */
  558. public getDarkness(): number {
  559. return this._darkness;
  560. }
  561. /**
  562. * Sets the darkness value (float). This can only decrease the actual darkness of a shadow.
  563. * @param darkness The darkness value 0 means strongest and 1 would means no shadow.
  564. * @returns the shadow generator allowing fluent coding.
  565. */
  566. public setDarkness(darkness: number): ShadowGenerator {
  567. if (darkness >= 1.0) {
  568. this._darkness = 1.0;
  569. }
  570. else if (darkness <= 0.0) {
  571. this._darkness = 0.0;
  572. }
  573. else {
  574. this._darkness = darkness;
  575. }
  576. return this;
  577. }
  578. protected _transparencyShadow = false;
  579. /** Gets or sets the ability to have transparent shadow */
  580. public get transparencyShadow() {
  581. return this._transparencyShadow;
  582. }
  583. public set transparencyShadow(value: boolean) {
  584. this.setTransparencyShadow(value);
  585. }
  586. /**
  587. * Sets the ability to have transparent shadow (boolean).
  588. * @param transparent True if transparent else False
  589. * @returns the shadow generator allowing fluent coding
  590. */
  591. public setTransparencyShadow(transparent: boolean): ShadowGenerator {
  592. this._transparencyShadow = transparent;
  593. return this;
  594. }
  595. /**
  596. * Enables or disables shadows with varying strength based on the transparency
  597. * When it is enabled, the strength of the shadow is taken equal to mesh.visibility
  598. * If you enabled an alpha texture on your material, the alpha value red from the texture is also combined to compute the strength:
  599. * mesh.visibility * alphaTexture.a
  600. * Note that by definition transparencyShadow must be set to true for enableSoftTransparentShadow to work!
  601. */
  602. public enableSoftTransparentShadow: boolean = false;
  603. protected _shadowMap: Nullable<RenderTargetTexture>;
  604. protected _shadowMap2: Nullable<RenderTargetTexture>;
  605. /**
  606. * Gets the main RTT containing the shadow map (usually storing depth from the light point of view).
  607. * @returns The render target texture if present otherwise, null
  608. */
  609. public getShadowMap(): Nullable<RenderTargetTexture> {
  610. return this._shadowMap;
  611. }
  612. /**
  613. * Gets the RTT used during rendering (can be a blurred version of the shadow map or the shadow map itself).
  614. * @returns The render target texture if the shadow map is present otherwise, null
  615. */
  616. public getShadowMapForRendering(): Nullable<RenderTargetTexture> {
  617. if (this._shadowMap2) {
  618. return this._shadowMap2;
  619. }
  620. return this._shadowMap;
  621. }
  622. /**
  623. * Gets the class name of that object
  624. * @returns "ShadowGenerator"
  625. */
  626. public getClassName(): string {
  627. return ShadowGenerator.CLASSNAME;
  628. }
  629. /**
  630. * Helper function to add a mesh and its descendants to the list of shadow casters.
  631. * @param mesh Mesh to add
  632. * @param includeDescendants boolean indicating if the descendants should be added. Default to true
  633. * @returns the Shadow Generator itself
  634. */
  635. public addShadowCaster(mesh: AbstractMesh, includeDescendants = true): ShadowGenerator {
  636. if (!this._shadowMap) {
  637. return this;
  638. }
  639. if (!this._shadowMap.renderList) {
  640. this._shadowMap.renderList = [];
  641. }
  642. this._shadowMap.renderList.push(mesh);
  643. if (includeDescendants) {
  644. this._shadowMap.renderList.push(...mesh.getChildMeshes());
  645. }
  646. return this;
  647. }
  648. /**
  649. * Helper function to remove a mesh and its descendants from the list of shadow casters
  650. * @param mesh Mesh to remove
  651. * @param includeDescendants boolean indicating if the descendants should be removed. Default to true
  652. * @returns the Shadow Generator itself
  653. */
  654. public removeShadowCaster(mesh: AbstractMesh, includeDescendants = true): ShadowGenerator {
  655. if (!this._shadowMap || !this._shadowMap.renderList) {
  656. return this;
  657. }
  658. var index = this._shadowMap.renderList.indexOf(mesh);
  659. if (index !== -1) {
  660. this._shadowMap.renderList.splice(index, 1);
  661. }
  662. if (includeDescendants) {
  663. for (var child of mesh.getChildren()) {
  664. this.removeShadowCaster(<any>child);
  665. }
  666. }
  667. return this;
  668. }
  669. /**
  670. * Controls the extent to which the shadows fade out at the edge of the frustum
  671. */
  672. public frustumEdgeFalloff = 0;
  673. protected _light: IShadowLight;
  674. /**
  675. * Returns the associated light object.
  676. * @returns the light generating the shadow
  677. */
  678. public getLight(): IShadowLight {
  679. return this._light;
  680. }
  681. /**
  682. * If true the shadow map is generated by rendering the back face of the mesh instead of the front face.
  683. * This can help with self-shadowing as the geometry making up the back of objects is slightly offset.
  684. * It might on the other hand introduce peter panning.
  685. */
  686. public forceBackFacesOnly = false;
  687. protected _scene: Scene;
  688. protected _lightDirection = Vector3.Zero();
  689. protected _effect: Effect;
  690. protected _viewMatrix = Matrix.Zero();
  691. protected _projectionMatrix = Matrix.Zero();
  692. protected _transformMatrix = Matrix.Zero();
  693. protected _cachedPosition: Vector3 = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  694. protected _cachedDirection: Vector3 = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  695. protected _cachedDefines: string;
  696. protected _currentRenderID: number;
  697. protected _boxBlurPostprocess: Nullable<PostProcess>;
  698. protected _kernelBlurXPostprocess: Nullable<PostProcess>;
  699. protected _kernelBlurYPostprocess: Nullable<PostProcess>;
  700. protected _blurPostProcesses: PostProcess[];
  701. protected _mapSize: number;
  702. protected _currentFaceIndex = 0;
  703. protected _currentFaceIndexCache = 0;
  704. protected _textureType: number;
  705. protected _defaultTextureMatrix = Matrix.Identity();
  706. protected _storedUniqueId: Nullable<number>;
  707. /** @hidden */
  708. public static _SceneComponentInitialization: (scene: Scene) => void = (_) => {
  709. throw _DevTools.WarnImport("ShadowGeneratorSceneComponent");
  710. }
  711. /**
  712. * Creates a ShadowGenerator object.
  713. * A ShadowGenerator is the required tool to use the shadows.
  714. * Each light casting shadows needs to use its own ShadowGenerator.
  715. * Documentation : https://doc.babylonjs.com/babylon101/shadows
  716. * @param mapSize The size of the texture what stores the shadows. Example : 1024.
  717. * @param light The light object generating the shadows.
  718. * @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.
  719. */
  720. constructor(mapSize: number, light: IShadowLight, usefulFloatFirst?: boolean) {
  721. this._mapSize = mapSize;
  722. this._light = light;
  723. this._scene = light.getScene();
  724. light._shadowGenerator = this;
  725. ShadowGenerator._SceneComponentInitialization(this._scene);
  726. // Texture type fallback from float to int if not supported.
  727. var caps = this._scene.getEngine().getCaps();
  728. if (!usefulFloatFirst) {
  729. if (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering) {
  730. this._textureType = Constants.TEXTURETYPE_HALF_FLOAT;
  731. }
  732. else if (caps.textureFloatRender && caps.textureFloatLinearFiltering) {
  733. this._textureType = Constants.TEXTURETYPE_FLOAT;
  734. }
  735. else {
  736. this._textureType = Constants.TEXTURETYPE_UNSIGNED_INT;
  737. }
  738. } else {
  739. if (caps.textureFloatRender && caps.textureFloatLinearFiltering) {
  740. this._textureType = Constants.TEXTURETYPE_FLOAT;
  741. }
  742. else if (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering) {
  743. this._textureType = Constants.TEXTURETYPE_HALF_FLOAT;
  744. }
  745. else {
  746. this._textureType = Constants.TEXTURETYPE_UNSIGNED_INT;
  747. }
  748. }
  749. this._initializeGenerator();
  750. this._applyFilterValues();
  751. }
  752. protected _initializeGenerator(): void {
  753. this._light._markMeshesAsLightDirty();
  754. this._initializeShadowMap();
  755. }
  756. protected _createTargetRenderTexture(): void {
  757. let engine = this._scene.getEngine();
  758. if (engine.webGLVersion > 1) {
  759. this._shadowMap = new RenderTargetTexture(this._light.name + "_shadowMap", this._mapSize, this._scene, false, true, this._textureType, this._light.needCube(), undefined, false, false);
  760. this._shadowMap.createDepthStencilTexture(Constants.LESS, true);
  761. }
  762. else {
  763. this._shadowMap = new RenderTargetTexture(this._light.name + "_shadowMap", this._mapSize, this._scene, false, true, this._textureType, this._light.needCube());
  764. }
  765. }
  766. protected _initializeShadowMap(): void {
  767. this._createTargetRenderTexture();
  768. if (this._shadowMap === null) {
  769. return;
  770. }
  771. this._shadowMap.wrapU = Texture.CLAMP_ADDRESSMODE;
  772. this._shadowMap.wrapV = Texture.CLAMP_ADDRESSMODE;
  773. this._shadowMap.anisotropicFilteringLevel = 1;
  774. this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  775. this._shadowMap.renderParticles = false;
  776. this._shadowMap.ignoreCameraViewport = true;
  777. if (this._storedUniqueId) {
  778. this._shadowMap.uniqueId = this._storedUniqueId;
  779. }
  780. // Custom render function.
  781. this._shadowMap.customRenderFunction = this._renderForShadowMap.bind(this);
  782. // Force the mesh is ready funcion to true as we are double checking it
  783. // in the custom render function. Also it prevents side effects and useless
  784. // shader variations in DEPTHPREPASS mode.
  785. this._shadowMap.customIsReadyFunction = (m: AbstractMesh, r: number) => {
  786. return true;
  787. };
  788. let engine = this._scene.getEngine();
  789. // Record Face Index before render.
  790. this._shadowMap.onBeforeRenderObservable.add((faceIndex: number) => {
  791. this._currentFaceIndex = faceIndex;
  792. if (this._filter === ShadowGenerator.FILTER_PCF) {
  793. engine.setColorWrite(false);
  794. }
  795. if (this._scene.getSceneUniformBuffer().useUbo) {
  796. const sceneUBO = this._scene.getSceneUniformBuffer();
  797. sceneUBO.updateMatrix("viewProjection", this.getTransformMatrix());
  798. sceneUBO.updateMatrix("view", this._viewMatrix);
  799. sceneUBO.update();
  800. }
  801. });
  802. // Blur if required afer render.
  803. this._shadowMap.onAfterUnbindObservable.add(() => {
  804. if (this._scene.getSceneUniformBuffer().useUbo) {
  805. const sceneUBO = this._scene.getSceneUniformBuffer();
  806. sceneUBO.updateMatrix("viewProjection", this._scene.getTransformMatrix());
  807. sceneUBO.updateMatrix("view", this._scene.getViewMatrix());
  808. sceneUBO.update();
  809. }
  810. if (this._filter === ShadowGenerator.FILTER_PCF) {
  811. engine.setColorWrite(true);
  812. }
  813. if (!this.useBlurExponentialShadowMap && !this.useBlurCloseExponentialShadowMap) {
  814. return;
  815. }
  816. let shadowMap = this.getShadowMapForRendering();
  817. if (shadowMap) {
  818. const texture = shadowMap.getInternalTexture()!;
  819. this._scene.postProcessManager.directRender(this._blurPostProcesses, texture, true);
  820. engine.unBindFramebuffer(texture, true);
  821. }
  822. });
  823. // Clear according to the chosen filter.
  824. var clearZero = new Color4(0, 0, 0, 0);
  825. var clearOne = new Color4(1.0, 1.0, 1.0, 1.0);
  826. this._shadowMap.onClearObservable.add((engine) => {
  827. if (this._filter === ShadowGenerator.FILTER_PCF) {
  828. engine.clear(clearOne, false, true, false);
  829. }
  830. else if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
  831. engine.clear(clearZero, true, true, false);
  832. }
  833. else {
  834. engine.clear(clearOne, true, true, false);
  835. }
  836. });
  837. // Recreate on resize.
  838. this._shadowMap.onResizeObservable.add((RTT) => {
  839. this._storedUniqueId = this._shadowMap!.uniqueId;
  840. this._mapSize = RTT.getRenderSize();
  841. this._light._markMeshesAsLightDirty();
  842. this.recreateShadowMap();
  843. });
  844. // Ensures rendering groupids do not erase the depth buffer
  845. // or we would lose the shadows information.
  846. for (let i = RenderingManager.MIN_RENDERINGGROUPS; i < RenderingManager.MAX_RENDERINGGROUPS; i++) {
  847. this._shadowMap.setRenderingAutoClearDepthStencil(i, false);
  848. }
  849. }
  850. protected _initializeBlurRTTAndPostProcesses(): void {
  851. var engine = this._scene.getEngine();
  852. var targetSize = this._mapSize / this.blurScale;
  853. if (!this.useKernelBlur || this.blurScale !== 1.0) {
  854. this._shadowMap2 = new RenderTargetTexture(this._light.name + "_shadowMap2", targetSize, this._scene, false, true, this._textureType);
  855. this._shadowMap2.wrapU = Texture.CLAMP_ADDRESSMODE;
  856. this._shadowMap2.wrapV = Texture.CLAMP_ADDRESSMODE;
  857. this._shadowMap2.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  858. }
  859. if (this.useKernelBlur) {
  860. this._kernelBlurXPostprocess = new BlurPostProcess(this._light.name + "KernelBlurX", new Vector2(1, 0), this.blurKernel, 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._textureType);
  861. this._kernelBlurXPostprocess.width = targetSize;
  862. this._kernelBlurXPostprocess.height = targetSize;
  863. this._kernelBlurXPostprocess.onApplyObservable.add((effect) => {
  864. effect.setTexture("textureSampler", this._shadowMap);
  865. });
  866. this._kernelBlurYPostprocess = new BlurPostProcess(this._light.name + "KernelBlurY", new Vector2(0, 1), this.blurKernel, 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._textureType);
  867. this._kernelBlurXPostprocess.autoClear = false;
  868. this._kernelBlurYPostprocess.autoClear = false;
  869. if (this._textureType === Constants.TEXTURETYPE_UNSIGNED_INT) {
  870. (<BlurPostProcess>this._kernelBlurXPostprocess).packedFloat = true;
  871. (<BlurPostProcess>this._kernelBlurYPostprocess).packedFloat = true;
  872. }
  873. this._blurPostProcesses = [this._kernelBlurXPostprocess, this._kernelBlurYPostprocess];
  874. }
  875. else {
  876. 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);
  877. this._boxBlurPostprocess.onApplyObservable.add((effect) => {
  878. effect.setFloat2("screenSize", targetSize, targetSize);
  879. effect.setTexture("textureSampler", this._shadowMap);
  880. });
  881. this._boxBlurPostprocess.autoClear = false;
  882. this._blurPostProcesses = [this._boxBlurPostprocess];
  883. }
  884. }
  885. protected _renderForShadowMap(opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, depthOnlySubMeshes: SmartArray<SubMesh>): void {
  886. var index: number;
  887. let engine = this._scene.getEngine();
  888. const colorWrite = engine.getColorWrite();
  889. if (depthOnlySubMeshes.length) {
  890. engine.setColorWrite(false);
  891. for (index = 0; index < depthOnlySubMeshes.length; index++) {
  892. this._renderSubMeshForShadowMap(depthOnlySubMeshes.data[index]);
  893. }
  894. engine.setColorWrite(colorWrite);
  895. }
  896. for (index = 0; index < opaqueSubMeshes.length; index++) {
  897. this._renderSubMeshForShadowMap(opaqueSubMeshes.data[index]);
  898. }
  899. for (index = 0; index < alphaTestSubMeshes.length; index++) {
  900. this._renderSubMeshForShadowMap(alphaTestSubMeshes.data[index]);
  901. }
  902. if (this._transparencyShadow) {
  903. for (index = 0; index < transparentSubMeshes.length; index++) {
  904. this._renderSubMeshForShadowMap(transparentSubMeshes.data[index], true);
  905. }
  906. }
  907. }
  908. protected _bindCustomEffectForRenderSubMeshForShadowMap(subMesh: SubMesh, effect: Effect, matriceNames: any, mesh: AbstractMesh): void {
  909. effect.setMatrix(matriceNames?.viewProjection ?? "viewProjection", this.getTransformMatrix());
  910. effect.setMatrix(matriceNames?.view ?? "view", this._viewMatrix);
  911. effect.setMatrix(matriceNames?.projection ?? "projection", this._projectionMatrix);
  912. const world = mesh.getWorldMatrix();
  913. effect.setMatrix(matriceNames?.world ?? "world", world);
  914. world.multiplyToRef(this.getTransformMatrix(), tmpMatrix);
  915. effect.setMatrix(matriceNames?.worldViewProjection ?? "worldViewProjection", tmpMatrix);
  916. world.multiplyToRef(this._viewMatrix, tmpMatrix2);
  917. effect.setMatrix(matriceNames?.worldView ?? "worldView", tmpMatrix2);
  918. }
  919. protected _renderSubMeshForShadowMap(subMesh: SubMesh, isTransparent: boolean = false): void {
  920. var renderingMesh = subMesh.getRenderingMesh();
  921. var effectiveMesh = subMesh.getEffectiveMesh();
  922. var scene = this._scene;
  923. var engine = scene.getEngine();
  924. let material = subMesh.getMaterial();
  925. effectiveMesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
  926. if (!material || subMesh.verticesCount === 0) {
  927. return;
  928. }
  929. // Culling
  930. engine.setState(material.backFaceCulling);
  931. // Managing instances
  932. var batch = renderingMesh._getInstancesRenderList(subMesh._id, !!subMesh.getReplacementMesh());
  933. if (batch.mustReturn) {
  934. return;
  935. }
  936. var hardwareInstancedRendering = engine.getCaps().instancedArrays && (batch.visibleInstances[subMesh._id] !== null && batch.visibleInstances[subMesh._id] !== undefined || renderingMesh.hasThinInstances);
  937. if (this.isReady(subMesh, hardwareInstancedRendering, isTransparent)) {
  938. const shadowDepthWrapper = renderingMesh.material?.shadowDepthWrapper;
  939. let effect = shadowDepthWrapper?.getEffect(subMesh, this) ?? this._effect;
  940. engine.enableEffect(effect);
  941. renderingMesh._bind(subMesh, effect, material.fillMode);
  942. this.getTransformMatrix(); // make sur _cachedDirection et _cachedPosition are up to date
  943. effect.setFloat3("biasAndScaleSM", this.bias, this.normalBias, this.depthScale);
  944. if (this.getLight().getTypeID() === Light.LIGHTTYPEID_DIRECTIONALLIGHT) {
  945. effect.setVector3("lightDataSM", this._cachedDirection);
  946. }
  947. else {
  948. effect.setVector3("lightDataSM", this._cachedPosition);
  949. }
  950. if (scene.activeCamera) {
  951. effect.setFloat2("depthValuesSM", this.getLight().getDepthMinZ(scene.activeCamera), this.getLight().getDepthMinZ(scene.activeCamera) + this.getLight().getDepthMaxZ(scene.activeCamera));
  952. }
  953. if (isTransparent && this.enableSoftTransparentShadow) {
  954. effect.setFloat("softTransparentShadowSM", effectiveMesh.visibility);
  955. }
  956. if (shadowDepthWrapper) {
  957. subMesh._effectOverride = effect;
  958. if (shadowDepthWrapper.standalone) {
  959. shadowDepthWrapper.baseMaterial.bindForSubMesh(effectiveMesh.getWorldMatrix(), renderingMesh, subMesh);
  960. } else {
  961. material.bindForSubMesh(effectiveMesh.getWorldMatrix(), renderingMesh, subMesh);
  962. }
  963. subMesh._effectOverride = null;
  964. } else {
  965. effect.setMatrix("viewProjection", this.getTransformMatrix());
  966. // Alpha test
  967. if (material && material.needAlphaTesting()) {
  968. var alphaTexture = material.getAlphaTestTexture();
  969. if (alphaTexture) {
  970. effect.setTexture("diffuseSampler", alphaTexture);
  971. effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix() || this._defaultTextureMatrix);
  972. }
  973. }
  974. // Bones
  975. if (renderingMesh.useBones && renderingMesh.computeBonesUsingShaders && renderingMesh.skeleton) {
  976. const skeleton = renderingMesh.skeleton;
  977. if (skeleton.isUsingTextureForMatrices) {
  978. const boneTexture = skeleton.getTransformMatrixTexture(renderingMesh);
  979. if (!boneTexture) {
  980. return;
  981. }
  982. effect.setTexture("boneSampler", boneTexture);
  983. effect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
  984. } else {
  985. effect.setMatrices("mBones", skeleton.getTransformMatrices((renderingMesh)));
  986. }
  987. }
  988. // Morph targets
  989. MaterialHelper.BindMorphTargetParameters(renderingMesh, effect);
  990. // Clip planes
  991. MaterialHelper.BindClipPlane(effect, scene);
  992. }
  993. this._bindCustomEffectForRenderSubMeshForShadowMap(subMesh, effect, shadowDepthWrapper?._matriceNames, effectiveMesh);
  994. if (this.forceBackFacesOnly) {
  995. engine.setState(true, 0, false, true);
  996. }
  997. // Observables
  998. this.onBeforeShadowMapRenderMeshObservable.notifyObservers(renderingMesh);
  999. this.onBeforeShadowMapRenderObservable.notifyObservers(effect);
  1000. // Draw
  1001. renderingMesh._processRendering(effectiveMesh, subMesh, effect, material.fillMode, batch, hardwareInstancedRendering,
  1002. (isInstance, world) => effect.setMatrix("world", world));
  1003. if (this.forceBackFacesOnly) {
  1004. engine.setState(true, 0, false, false);
  1005. }
  1006. // Observables
  1007. this.onAfterShadowMapRenderObservable.notifyObservers(effect);
  1008. this.onAfterShadowMapRenderMeshObservable.notifyObservers(renderingMesh);
  1009. } else {
  1010. // Need to reset refresh rate of the shadowMap
  1011. if (this._shadowMap) {
  1012. this._shadowMap.resetRefreshCounter();
  1013. }
  1014. }
  1015. }
  1016. protected _applyFilterValues(): void {
  1017. if (!this._shadowMap) {
  1018. return;
  1019. }
  1020. if (this.filter === ShadowGenerator.FILTER_NONE || this.filter === ShadowGenerator.FILTER_PCSS) {
  1021. this._shadowMap.updateSamplingMode(Texture.NEAREST_SAMPLINGMODE);
  1022. } else {
  1023. this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  1024. }
  1025. }
  1026. /**
  1027. * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
  1028. * @param onCompiled Callback triggered at the and of the effects compilation
  1029. * @param options Sets of optional options forcing the compilation with different modes
  1030. */
  1031. public forceCompilation(onCompiled?: (generator: IShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void {
  1032. let localOptions = {
  1033. useInstances: false,
  1034. ...options
  1035. };
  1036. let shadowMap = this.getShadowMap();
  1037. if (!shadowMap) {
  1038. if (onCompiled) {
  1039. onCompiled(this);
  1040. }
  1041. return;
  1042. }
  1043. let renderList = shadowMap.renderList;
  1044. if (!renderList) {
  1045. if (onCompiled) {
  1046. onCompiled(this);
  1047. }
  1048. return;
  1049. }
  1050. var subMeshes = new Array<SubMesh>();
  1051. for (var mesh of renderList) {
  1052. subMeshes.push(...mesh.subMeshes);
  1053. }
  1054. if (subMeshes.length === 0) {
  1055. if (onCompiled) {
  1056. onCompiled(this);
  1057. }
  1058. return;
  1059. }
  1060. var currentIndex = 0;
  1061. var checkReady = () => {
  1062. if (!this._scene || !this._scene.getEngine()) {
  1063. return;
  1064. }
  1065. while (this.isReady(subMeshes[currentIndex], localOptions.useInstances, subMeshes[currentIndex].getMaterial()?.needAlphaBlendingForMesh(subMeshes[currentIndex].getMesh()) ?? false)) {
  1066. currentIndex++;
  1067. if (currentIndex >= subMeshes.length) {
  1068. if (onCompiled) {
  1069. onCompiled(this);
  1070. }
  1071. return;
  1072. }
  1073. }
  1074. setTimeout(checkReady, 16);
  1075. };
  1076. checkReady();
  1077. }
  1078. /**
  1079. * Forces all the attached effect to compile to enable rendering only once ready vs. lazyly compiling effects.
  1080. * @param options Sets of optional options forcing the compilation with different modes
  1081. * @returns A promise that resolves when the compilation completes
  1082. */
  1083. public forceCompilationAsync(options?: Partial<{ useInstances: boolean }>): Promise<void> {
  1084. return new Promise((resolve) => {
  1085. this.forceCompilation(() => {
  1086. resolve();
  1087. }, options);
  1088. });
  1089. }
  1090. protected _isReadyCustomDefines(defines: any, subMesh: SubMesh, useInstances: boolean): void {
  1091. }
  1092. private _prepareShadowDefines(subMesh: SubMesh, useInstances: boolean, defines: string[], isTransparent: boolean): string[] {
  1093. defines.push("#define SM_FLOAT " + (this._textureType !== Constants.TEXTURETYPE_UNSIGNED_INT ? "1" : "0"));
  1094. defines.push("#define SM_ESM " + (this.useExponentialShadowMap || this.useBlurExponentialShadowMap ? "1" : "0"));
  1095. defines.push("#define SM_DEPTHTEXTURE " + (this.usePercentageCloserFiltering || this.useContactHardeningShadow ? "1" : "0"));
  1096. var mesh = subMesh.getMesh();
  1097. // Normal bias.
  1098. defines.push("#define SM_NORMALBIAS " + (this.normalBias && mesh.isVerticesDataPresent(VertexBuffer.NormalKind) ? "1" : "0"));
  1099. defines.push("#define SM_DIRECTIONINLIGHTDATA " + (this.getLight().getTypeID() === Light.LIGHTTYPEID_DIRECTIONALLIGHT ? "1" : "0"));
  1100. // Point light
  1101. defines.push("#define SM_USEDISTANCE " + (this._light.needCube() ? "1" : "0"));
  1102. // Soft transparent shadows
  1103. defines.push("#define SM_SOFTTRANSPARENTSHADOW " + (this.enableSoftTransparentShadow && isTransparent ? "1" : "0"));
  1104. this._isReadyCustomDefines(defines, subMesh, useInstances);
  1105. return defines;
  1106. }
  1107. /**
  1108. * Determine wheter the shadow generator is ready or not (mainly all effects and related post processes needs to be ready).
  1109. * @param subMesh The submesh we want to render in the shadow map
  1110. * @param useInstances Defines wether will draw in the map using instances
  1111. * @param isTransparent Indicates that isReady is called for a transparent subMesh
  1112. * @returns true if ready otherwise, false
  1113. */
  1114. public isReady(subMesh: SubMesh, useInstances: boolean, isTransparent: boolean): boolean {
  1115. const material = subMesh.getMaterial(),
  1116. shadowDepthWrapper = material?.shadowDepthWrapper;
  1117. const defines: string[] = [];
  1118. this._prepareShadowDefines(subMesh, useInstances, defines, isTransparent);
  1119. if (shadowDepthWrapper) {
  1120. if (!shadowDepthWrapper.isReadyForSubMesh(subMesh, defines, this, useInstances)) {
  1121. return false;
  1122. }
  1123. } else {
  1124. var attribs = [VertexBuffer.PositionKind];
  1125. var mesh = subMesh.getMesh();
  1126. // Normal bias.
  1127. if (this.normalBias && mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
  1128. attribs.push(VertexBuffer.NormalKind);
  1129. defines.push("#define NORMAL");
  1130. if (mesh.nonUniformScaling) {
  1131. defines.push("#define NONUNIFORMSCALING");
  1132. }
  1133. }
  1134. // Alpha test
  1135. if (material && material.needAlphaTesting()) {
  1136. var alphaTexture = material.getAlphaTestTexture();
  1137. if (alphaTexture) {
  1138. defines.push("#define ALPHATEST");
  1139. if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  1140. attribs.push(VertexBuffer.UVKind);
  1141. defines.push("#define UV1");
  1142. }
  1143. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
  1144. if (alphaTexture.coordinatesIndex === 1) {
  1145. attribs.push(VertexBuffer.UV2Kind);
  1146. defines.push("#define UV2");
  1147. }
  1148. }
  1149. }
  1150. }
  1151. // Bones
  1152. const fallbacks = new EffectFallbacks();
  1153. if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
  1154. attribs.push(VertexBuffer.MatricesIndicesKind);
  1155. attribs.push(VertexBuffer.MatricesWeightsKind);
  1156. if (mesh.numBoneInfluencers > 4) {
  1157. attribs.push(VertexBuffer.MatricesIndicesExtraKind);
  1158. attribs.push(VertexBuffer.MatricesWeightsExtraKind);
  1159. }
  1160. const skeleton = mesh.skeleton;
  1161. defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
  1162. if (mesh.numBoneInfluencers > 0) {
  1163. fallbacks.addCPUSkinningFallback(0, mesh);
  1164. }
  1165. if (skeleton.isUsingTextureForMatrices) {
  1166. defines.push("#define BONETEXTURE");
  1167. } else {
  1168. defines.push("#define BonesPerMesh " + (skeleton.bones.length + 1));
  1169. }
  1170. } else {
  1171. defines.push("#define NUM_BONE_INFLUENCERS 0");
  1172. }
  1173. // Morph targets
  1174. var manager = (<Mesh>mesh).morphTargetManager;
  1175. let morphInfluencers = 0;
  1176. if (manager) {
  1177. if (manager.numInfluencers > 0) {
  1178. defines.push("#define MORPHTARGETS");
  1179. morphInfluencers = manager.numInfluencers;
  1180. defines.push("#define NUM_MORPH_INFLUENCERS " + morphInfluencers);
  1181. MaterialHelper.PrepareAttributesForMorphTargetsInfluencers(attribs, mesh, morphInfluencers);
  1182. }
  1183. }
  1184. // ClipPlanes
  1185. const scene = this._scene;
  1186. if (scene.clipPlane) {
  1187. defines.push("#define CLIPPLANE");
  1188. }
  1189. if (scene.clipPlane2) {
  1190. defines.push("#define CLIPPLANE2");
  1191. }
  1192. if (scene.clipPlane3) {
  1193. defines.push("#define CLIPPLANE3");
  1194. }
  1195. if (scene.clipPlane4) {
  1196. defines.push("#define CLIPPLANE4");
  1197. }
  1198. if (scene.clipPlane5) {
  1199. defines.push("#define CLIPPLANE5");
  1200. }
  1201. if (scene.clipPlane6) {
  1202. defines.push("#define CLIPPLANE6");
  1203. }
  1204. // Instances
  1205. if (useInstances) {
  1206. defines.push("#define INSTANCES");
  1207. MaterialHelper.PushAttributesForInstances(attribs);
  1208. if (subMesh.getRenderingMesh().hasThinInstances) {
  1209. defines.push("#define THIN_INSTANCES");
  1210. }
  1211. }
  1212. if (this.customShaderOptions) {
  1213. if (this.customShaderOptions.defines) {
  1214. for (var define of this.customShaderOptions.defines) {
  1215. if (defines.indexOf(define) === -1) {
  1216. defines.push(define);
  1217. }
  1218. }
  1219. }
  1220. }
  1221. // Get correct effect
  1222. var join = defines.join("\n");
  1223. if (this._cachedDefines !== join) {
  1224. this._cachedDefines = join;
  1225. let shaderName = "shadowMap";
  1226. let uniforms = ["world", "mBones", "viewProjection", "diffuseMatrix", "lightDataSM", "depthValuesSM", "biasAndScaleSM", "morphTargetInfluences", "boneTextureWidth",
  1227. "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "vClipPlane5", "vClipPlane6", "softTransparentShadowSM"];
  1228. let samplers = ["diffuseSampler", "boneSampler"];
  1229. // Custom shader?
  1230. if (this.customShaderOptions) {
  1231. shaderName = this.customShaderOptions.shaderName;
  1232. if (this.customShaderOptions.attributes) {
  1233. for (var attrib of this.customShaderOptions.attributes) {
  1234. if (attribs.indexOf(attrib) === -1) {
  1235. attribs.push(attrib);
  1236. }
  1237. }
  1238. }
  1239. if (this.customShaderOptions.uniforms) {
  1240. for (var uniform of this.customShaderOptions.uniforms) {
  1241. if (uniforms.indexOf(uniform) === -1) {
  1242. uniforms.push(uniform);
  1243. }
  1244. }
  1245. }
  1246. if (this.customShaderOptions.samplers) {
  1247. for (var sampler of this.customShaderOptions.samplers) {
  1248. if (samplers.indexOf(sampler) === -1) {
  1249. samplers.push(sampler);
  1250. }
  1251. }
  1252. }
  1253. }
  1254. this._effect = this._scene.getEngine().createEffect(shaderName,
  1255. attribs, uniforms,
  1256. samplers, join,
  1257. fallbacks, undefined, undefined, { maxSimultaneousMorphTargets: morphInfluencers });
  1258. }
  1259. if (!this._effect.isReady()) {
  1260. return false;
  1261. }
  1262. }
  1263. if (this.useBlurExponentialShadowMap || this.useBlurCloseExponentialShadowMap) {
  1264. if (!this._blurPostProcesses || !this._blurPostProcesses.length) {
  1265. this._initializeBlurRTTAndPostProcesses();
  1266. }
  1267. }
  1268. if (this._kernelBlurXPostprocess && !this._kernelBlurXPostprocess.isReady()) {
  1269. return false;
  1270. }
  1271. if (this._kernelBlurYPostprocess && !this._kernelBlurYPostprocess.isReady()) {
  1272. return false;
  1273. }
  1274. if (this._boxBlurPostprocess && !this._boxBlurPostprocess.isReady()) {
  1275. return false;
  1276. }
  1277. return true;
  1278. }
  1279. /**
  1280. * Prepare all the defines in a material relying on a shadow map at the specified light index.
  1281. * @param defines Defines of the material we want to update
  1282. * @param lightIndex Index of the light in the enabled light list of the material
  1283. */
  1284. public prepareDefines(defines: any, lightIndex: number): void {
  1285. var scene = this._scene;
  1286. var light = this._light;
  1287. if (!scene.shadowsEnabled || !light.shadowEnabled) {
  1288. return;
  1289. }
  1290. defines["SHADOW" + lightIndex] = true;
  1291. if (this.useContactHardeningShadow) {
  1292. defines["SHADOWPCSS" + lightIndex] = true;
  1293. if (this._filteringQuality === ShadowGenerator.QUALITY_LOW) {
  1294. defines["SHADOWLOWQUALITY" + lightIndex] = true;
  1295. }
  1296. else if (this._filteringQuality === ShadowGenerator.QUALITY_MEDIUM) {
  1297. defines["SHADOWMEDIUMQUALITY" + lightIndex] = true;
  1298. }
  1299. // else default to high.
  1300. }
  1301. else if (this.usePercentageCloserFiltering) {
  1302. defines["SHADOWPCF" + lightIndex] = true;
  1303. if (this._filteringQuality === ShadowGenerator.QUALITY_LOW) {
  1304. defines["SHADOWLOWQUALITY" + lightIndex] = true;
  1305. }
  1306. else if (this._filteringQuality === ShadowGenerator.QUALITY_MEDIUM) {
  1307. defines["SHADOWMEDIUMQUALITY" + lightIndex] = true;
  1308. }
  1309. // else default to high.
  1310. }
  1311. else if (this.usePoissonSampling) {
  1312. defines["SHADOWPOISSON" + lightIndex] = true;
  1313. }
  1314. else if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
  1315. defines["SHADOWESM" + lightIndex] = true;
  1316. }
  1317. else if (this.useCloseExponentialShadowMap || this.useBlurCloseExponentialShadowMap) {
  1318. defines["SHADOWCLOSEESM" + lightIndex] = true;
  1319. }
  1320. if (light.needCube()) {
  1321. defines["SHADOWCUBE" + lightIndex] = true;
  1322. }
  1323. }
  1324. /**
  1325. * Binds the shadow related information inside of an effect (information like near, far, darkness...
  1326. * defined in the generator but impacting the effect).
  1327. * @param lightIndex Index of the light in the enabled light list of the material owning the effect
  1328. * @param effect The effect we are binfing the information for
  1329. */
  1330. public bindShadowLight(lightIndex: string, effect: Effect): void {
  1331. const light = this._light;
  1332. const scene = this._scene;
  1333. if (!scene.shadowsEnabled || !light.shadowEnabled) {
  1334. return;
  1335. }
  1336. const camera = scene.activeCamera;
  1337. if (!camera) {
  1338. return;
  1339. }
  1340. const shadowMap = this.getShadowMap();
  1341. if (!shadowMap) {
  1342. return;
  1343. }
  1344. if (!light.needCube()) {
  1345. effect.setMatrix("lightMatrix" + lightIndex, this.getTransformMatrix());
  1346. }
  1347. // Only PCF uses depth stencil texture.
  1348. if (this._filter === ShadowGenerator.FILTER_PCF) {
  1349. effect.setDepthStencilTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
  1350. light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), shadowMap.getSize().width, 1 / shadowMap.getSize().width, this.frustumEdgeFalloff, lightIndex);
  1351. }
  1352. else if (this._filter === ShadowGenerator.FILTER_PCSS) {
  1353. effect.setDepthStencilTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
  1354. effect.setTexture("depthSampler" + lightIndex, this.getShadowMapForRendering());
  1355. light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), 1 / shadowMap.getSize().width, this._contactHardeningLightSizeUVRatio * shadowMap.getSize().width, this.frustumEdgeFalloff, lightIndex);
  1356. }
  1357. else {
  1358. effect.setTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
  1359. light._uniformBuffer.updateFloat4("shadowsInfo", this.getDarkness(), this.blurScale / shadowMap.getSize().width, this.depthScale, this.frustumEdgeFalloff, lightIndex);
  1360. }
  1361. light._uniformBuffer.updateFloat2("depthValues", this.getLight().getDepthMinZ(camera), this.getLight().getDepthMinZ(camera) + this.getLight().getDepthMaxZ(camera), lightIndex);
  1362. }
  1363. /**
  1364. * Gets the transformation matrix used to project the meshes into the map from the light point of view.
  1365. * (eq to shadow prjection matrix * light transform matrix)
  1366. * @returns The transform matrix used to create the shadow map
  1367. */
  1368. public getTransformMatrix(): Matrix {
  1369. var scene = this._scene;
  1370. if (this._currentRenderID === scene.getRenderId() && this._currentFaceIndexCache === this._currentFaceIndex) {
  1371. return this._transformMatrix;
  1372. }
  1373. this._currentRenderID = scene.getRenderId();
  1374. this._currentFaceIndexCache = this._currentFaceIndex;
  1375. var lightPosition = this._light.position;
  1376. if (this._light.computeTransformedInformation()) {
  1377. lightPosition = this._light.transformedPosition;
  1378. }
  1379. Vector3.NormalizeToRef(this._light.getShadowDirection(this._currentFaceIndex), this._lightDirection);
  1380. if (Math.abs(Vector3.Dot(this._lightDirection, Vector3.Up())) === 1.0) {
  1381. this._lightDirection.z = 0.0000000000001; // Required to avoid perfectly perpendicular light
  1382. }
  1383. if (this._light.needProjectionMatrixCompute() || !this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !this._lightDirection.equals(this._cachedDirection)) {
  1384. this._cachedPosition.copyFrom(lightPosition);
  1385. this._cachedDirection.copyFrom(this._lightDirection);
  1386. Matrix.LookAtLHToRef(lightPosition, lightPosition.add(this._lightDirection), Vector3.Up(), this._viewMatrix);
  1387. let shadowMap = this.getShadowMap();
  1388. if (shadowMap) {
  1389. let renderList = shadowMap.renderList;
  1390. if (renderList) {
  1391. this._light.setShadowProjectionMatrix(this._projectionMatrix, this._viewMatrix, renderList);
  1392. }
  1393. }
  1394. this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
  1395. }
  1396. return this._transformMatrix;
  1397. }
  1398. /**
  1399. * Recreates the shadow map dependencies like RTT and post processes. This can be used during the switch between
  1400. * Cube and 2D textures for instance.
  1401. */
  1402. public recreateShadowMap(): void {
  1403. let shadowMap = this._shadowMap;
  1404. if (!shadowMap) {
  1405. return;
  1406. }
  1407. // Track render list.
  1408. var renderList = shadowMap.renderList;
  1409. // Clean up existing data.
  1410. this._disposeRTTandPostProcesses();
  1411. // Reinitializes.
  1412. this._initializeGenerator();
  1413. // Reaffect the filter to ensure a correct fallback if necessary.
  1414. this.filter = this.filter;
  1415. // Reaffect the filter.
  1416. this._applyFilterValues();
  1417. // Reaffect Render List.
  1418. this._shadowMap!.renderList = renderList;
  1419. }
  1420. protected _disposeBlurPostProcesses(): void {
  1421. if (this._shadowMap2) {
  1422. this._shadowMap2.dispose();
  1423. this._shadowMap2 = null;
  1424. }
  1425. if (this._boxBlurPostprocess) {
  1426. this._boxBlurPostprocess.dispose();
  1427. this._boxBlurPostprocess = null;
  1428. }
  1429. if (this._kernelBlurXPostprocess) {
  1430. this._kernelBlurXPostprocess.dispose();
  1431. this._kernelBlurXPostprocess = null;
  1432. }
  1433. if (this._kernelBlurYPostprocess) {
  1434. this._kernelBlurYPostprocess.dispose();
  1435. this._kernelBlurYPostprocess = null;
  1436. }
  1437. this._blurPostProcesses = [];
  1438. }
  1439. protected _disposeRTTandPostProcesses(): void {
  1440. if (this._shadowMap) {
  1441. this._shadowMap.dispose();
  1442. this._shadowMap = null;
  1443. }
  1444. this._disposeBlurPostProcesses();
  1445. }
  1446. /**
  1447. * Disposes the ShadowGenerator.
  1448. * Returns nothing.
  1449. */
  1450. public dispose(): void {
  1451. this._disposeRTTandPostProcesses();
  1452. if (this._light) {
  1453. this._light._shadowGenerator = null;
  1454. this._light._markMeshesAsLightDirty();
  1455. }
  1456. this.onBeforeShadowMapRenderMeshObservable.clear();
  1457. this.onBeforeShadowMapRenderObservable.clear();
  1458. this.onAfterShadowMapRenderMeshObservable.clear();
  1459. this.onAfterShadowMapRenderObservable.clear();
  1460. }
  1461. /**
  1462. * Serializes the shadow generator setup to a json object.
  1463. * @returns The serialized JSON object
  1464. */
  1465. public serialize(): any {
  1466. var serializationObject: any = {};
  1467. var shadowMap = this.getShadowMap();
  1468. if (!shadowMap) {
  1469. return serializationObject;
  1470. }
  1471. serializationObject.className = this.getClassName();
  1472. serializationObject.lightId = this._light.id;
  1473. serializationObject.mapSize = shadowMap.getRenderSize();
  1474. serializationObject.forceBackFacesOnly = this.forceBackFacesOnly;
  1475. serializationObject.darkness = this.getDarkness();
  1476. serializationObject.transparencyShadow = this._transparencyShadow;
  1477. serializationObject.frustumEdgeFalloff = this.frustumEdgeFalloff;
  1478. serializationObject.bias = this.bias;
  1479. serializationObject.normalBias = this.normalBias;
  1480. serializationObject.usePercentageCloserFiltering = this.usePercentageCloserFiltering;
  1481. serializationObject.useContactHardeningShadow = this.useContactHardeningShadow;
  1482. serializationObject.contactHardeningLightSizeUVRatio = this.contactHardeningLightSizeUVRatio;
  1483. serializationObject.filteringQuality = this.filteringQuality;
  1484. serializationObject.useExponentialShadowMap = this.useExponentialShadowMap;
  1485. serializationObject.useBlurExponentialShadowMap = this.useBlurExponentialShadowMap;
  1486. serializationObject.useCloseExponentialShadowMap = this.useBlurExponentialShadowMap;
  1487. serializationObject.useBlurCloseExponentialShadowMap = this.useBlurExponentialShadowMap;
  1488. serializationObject.usePoissonSampling = this.usePoissonSampling;
  1489. serializationObject.depthScale = this.depthScale;
  1490. serializationObject.blurBoxOffset = this.blurBoxOffset;
  1491. serializationObject.blurKernel = this.blurKernel;
  1492. serializationObject.blurScale = this.blurScale;
  1493. serializationObject.useKernelBlur = this.useKernelBlur;
  1494. serializationObject.renderList = [];
  1495. if (shadowMap.renderList) {
  1496. for (var meshIndex = 0; meshIndex < shadowMap.renderList.length; meshIndex++) {
  1497. var mesh = shadowMap.renderList[meshIndex];
  1498. serializationObject.renderList.push(mesh.id);
  1499. }
  1500. }
  1501. return serializationObject;
  1502. }
  1503. /**
  1504. * Parses a serialized ShadowGenerator and returns a new ShadowGenerator.
  1505. * @param parsedShadowGenerator The JSON object to parse
  1506. * @param scene The scene to create the shadow map for
  1507. * @param constr A function that builds a shadow generator or undefined to create an instance of the default shadow generator
  1508. * @returns The parsed shadow generator
  1509. */
  1510. public static Parse(parsedShadowGenerator: any, scene: Scene, constr?: (mapSize: number, light: IShadowLight) => ShadowGenerator): ShadowGenerator {
  1511. var light = <IShadowLight>scene.getLightByID(parsedShadowGenerator.lightId);
  1512. var shadowGenerator = constr ? constr(parsedShadowGenerator.mapSize, light) : new ShadowGenerator(parsedShadowGenerator.mapSize, light);
  1513. var shadowMap = shadowGenerator.getShadowMap();
  1514. for (var meshIndex = 0; meshIndex < parsedShadowGenerator.renderList.length; meshIndex++) {
  1515. var meshes = scene.getMeshesByID(parsedShadowGenerator.renderList[meshIndex]);
  1516. meshes.forEach(function(mesh) {
  1517. if (!shadowMap) {
  1518. return;
  1519. }
  1520. if (!shadowMap.renderList) {
  1521. shadowMap.renderList = [];
  1522. }
  1523. shadowMap.renderList.push(mesh);
  1524. });
  1525. }
  1526. shadowGenerator.forceBackFacesOnly = !!parsedShadowGenerator.forceBackFacesOnly;
  1527. if (parsedShadowGenerator.darkness !== undefined) {
  1528. shadowGenerator.setDarkness(parsedShadowGenerator.darkness);
  1529. }
  1530. if (parsedShadowGenerator.transparencyShadow) {
  1531. shadowGenerator.setTransparencyShadow(true);
  1532. }
  1533. if (parsedShadowGenerator.frustumEdgeFalloff !== undefined) {
  1534. shadowGenerator.frustumEdgeFalloff = parsedShadowGenerator.frustumEdgeFalloff;
  1535. }
  1536. if (parsedShadowGenerator.bias !== undefined) {
  1537. shadowGenerator.bias = parsedShadowGenerator.bias;
  1538. }
  1539. if (parsedShadowGenerator.normalBias !== undefined) {
  1540. shadowGenerator.normalBias = parsedShadowGenerator.normalBias;
  1541. }
  1542. if (parsedShadowGenerator.usePercentageCloserFiltering) {
  1543. shadowGenerator.usePercentageCloserFiltering = true;
  1544. } else if (parsedShadowGenerator.useContactHardeningShadow) {
  1545. shadowGenerator.useContactHardeningShadow = true;
  1546. } else if (parsedShadowGenerator.usePoissonSampling) {
  1547. shadowGenerator.usePoissonSampling = true;
  1548. } else if (parsedShadowGenerator.useExponentialShadowMap) {
  1549. shadowGenerator.useExponentialShadowMap = true;
  1550. } else if (parsedShadowGenerator.useBlurExponentialShadowMap) {
  1551. shadowGenerator.useBlurExponentialShadowMap = true;
  1552. } else if (parsedShadowGenerator.useCloseExponentialShadowMap) {
  1553. shadowGenerator.useCloseExponentialShadowMap = true;
  1554. } else if (parsedShadowGenerator.useBlurCloseExponentialShadowMap) {
  1555. shadowGenerator.useBlurCloseExponentialShadowMap = true;
  1556. } else
  1557. // Backward compat
  1558. if (parsedShadowGenerator.useVarianceShadowMap) {
  1559. shadowGenerator.useExponentialShadowMap = true;
  1560. } else if (parsedShadowGenerator.useBlurVarianceShadowMap) {
  1561. shadowGenerator.useBlurExponentialShadowMap = true;
  1562. }
  1563. if (parsedShadowGenerator.contactHardeningLightSizeUVRatio !== undefined) {
  1564. shadowGenerator.contactHardeningLightSizeUVRatio = parsedShadowGenerator.contactHardeningLightSizeUVRatio;
  1565. }
  1566. if (parsedShadowGenerator.filteringQuality !== undefined) {
  1567. shadowGenerator.filteringQuality = parsedShadowGenerator.filteringQuality;
  1568. }
  1569. if (parsedShadowGenerator.depthScale) {
  1570. shadowGenerator.depthScale = parsedShadowGenerator.depthScale;
  1571. }
  1572. if (parsedShadowGenerator.blurScale) {
  1573. shadowGenerator.blurScale = parsedShadowGenerator.blurScale;
  1574. }
  1575. if (parsedShadowGenerator.blurBoxOffset) {
  1576. shadowGenerator.blurBoxOffset = parsedShadowGenerator.blurBoxOffset;
  1577. }
  1578. if (parsedShadowGenerator.useKernelBlur) {
  1579. shadowGenerator.useKernelBlur = parsedShadowGenerator.useKernelBlur;
  1580. }
  1581. if (parsedShadowGenerator.blurKernel) {
  1582. shadowGenerator.blurKernel = parsedShadowGenerator.blurKernel;
  1583. }
  1584. return shadowGenerator;
  1585. }
  1586. }