babylon.shadowGenerator.ts 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. module BABYLON {
  2. /**
  3. * Interface to implement to create a shadow generator compatible with BJS.
  4. */
  5. export interface IShadowGenerator {
  6. getShadowMap(): RenderTargetTexture;
  7. getShadowMapForRendering(): RenderTargetTexture;
  8. isReady(subMesh: SubMesh, useInstances: boolean): boolean;
  9. prepareDefines(defines: MaterialDefines, lightIndex: number): void;
  10. bindShadowLight(lightIndex: string, effect: Effect): void;
  11. getTransformMatrix(): Matrix;
  12. recreateShadowMap(): void;
  13. serialize(): any;
  14. dispose(): void;
  15. }
  16. export class ShadowGenerator implements IShadowGenerator {
  17. private static _FILTER_NONE = 0;
  18. private static _FILTER_EXPONENTIALSHADOWMAP = 1;
  19. private static _FILTER_POISSONSAMPLING = 2;
  20. private static _FILTER_BLUREXPONENTIALSHADOWMAP = 3;
  21. private static _FILTER_CLOSEEXPONENTIALSHADOWMAP = 4;
  22. private static _FILTER_BLURCLOSEEXPONENTIALSHADOWMAP = 5;
  23. // Static
  24. public static get FILTER_NONE(): number {
  25. return ShadowGenerator._FILTER_NONE;
  26. }
  27. public static get FILTER_POISSONSAMPLING(): number {
  28. return ShadowGenerator._FILTER_POISSONSAMPLING;
  29. }
  30. public static get FILTER_EXPONENTIALSHADOWMAP(): number {
  31. return ShadowGenerator._FILTER_EXPONENTIALSHADOWMAP;
  32. }
  33. public static get FILTER_BLUREXPONENTIALSHADOWMAP(): number {
  34. return ShadowGenerator._FILTER_BLUREXPONENTIALSHADOWMAP;
  35. }
  36. public static get FILTER_CLOSEEXPONENTIALSHADOWMAP(): number {
  37. return ShadowGenerator._FILTER_CLOSEEXPONENTIALSHADOWMAP;
  38. }
  39. public static get FILTER_BLURCLOSEEXPONENTIALSHADOWMAP(): number {
  40. return ShadowGenerator._FILTER_BLURCLOSEEXPONENTIALSHADOWMAP;
  41. }
  42. // Members
  43. private _bias = 0.00005;
  44. public get bias(): number {
  45. return this._bias;
  46. }
  47. public set bias(bias: number) {
  48. this._bias = bias;
  49. }
  50. private _blurBoxOffset = 1;
  51. public get blurBoxOffset(): number {
  52. return this._blurBoxOffset;
  53. }
  54. public set blurBoxOffset(value: number) {
  55. if (this._blurBoxOffset === value) {
  56. return;
  57. }
  58. this._blurBoxOffset = value;
  59. this._disposeBlurPostProcesses();
  60. }
  61. private _blurScale = 2;
  62. public get blurScale(): number {
  63. return this._blurScale;
  64. }
  65. public set blurScale(value: number) {
  66. if (this._blurScale === value) {
  67. return;
  68. }
  69. this._blurScale = value;
  70. this._disposeBlurPostProcesses();
  71. }
  72. private _blurKernel = 1;
  73. public get blurKernel(): number {
  74. return this._blurKernel;
  75. }
  76. public set blurKernel(value: number) {
  77. if (this._blurKernel === value) {
  78. return;
  79. }
  80. this._blurKernel = value;
  81. this._disposeBlurPostProcesses();
  82. }
  83. private _useKernelBlur = false;
  84. public get useKernelBlur(): boolean {
  85. return this._useKernelBlur;
  86. }
  87. public set useKernelBlur(value: boolean) {
  88. if (this._useKernelBlur === value) {
  89. return;
  90. }
  91. this._useKernelBlur = value;
  92. this._disposeBlurPostProcesses();
  93. }
  94. private _depthScale: number;
  95. public get depthScale(): number {
  96. return this._depthScale !== undefined ? this._depthScale : this._light.getDepthScale();
  97. }
  98. public set depthScale(value: number) {
  99. this._depthScale = value;
  100. }
  101. private _filter = ShadowGenerator.FILTER_NONE;
  102. public get filter(): number {
  103. return this._filter;
  104. }
  105. public set filter(value: number) {
  106. // Blurring the cubemap is going to be too expensive. Reverting to unblurred version
  107. if (this._light.needCube()) {
  108. if (value === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP) {
  109. this.useExponentialShadowMap = true;
  110. }
  111. else if (value === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) {
  112. this.useCloseExponentialShadowMap = true;
  113. }
  114. }
  115. if (this._filter === value) {
  116. return;
  117. }
  118. this._filter = value;
  119. this._disposeBlurPostProcesses();
  120. this._applyFilterValues();
  121. this._light._markMeshesAsLightDirty();
  122. }
  123. public get usePoissonSampling(): boolean {
  124. return this.filter === ShadowGenerator.FILTER_POISSONSAMPLING;
  125. }
  126. public set usePoissonSampling(value: boolean) {
  127. this.filter = (value ? ShadowGenerator.FILTER_POISSONSAMPLING : ShadowGenerator.FILTER_NONE);
  128. }
  129. public get useVarianceShadowMap(): boolean {
  130. Tools.Warn("VSM are now replaced by ESM. Please use useExponentialShadowMap instead.");
  131. return this.useExponentialShadowMap;
  132. }
  133. public set useVarianceShadowMap(value: boolean) {
  134. Tools.Warn("VSM are now replaced by ESM. Please use useExponentialShadowMap instead.");
  135. this.useExponentialShadowMap = value;
  136. }
  137. public get useBlurVarianceShadowMap(): boolean {
  138. Tools.Warn("VSM are now replaced by ESM. Please use useBlurExponentialShadowMap instead.");
  139. return this.useBlurExponentialShadowMap;
  140. }
  141. public set useBlurVarianceShadowMap(value: boolean) {
  142. Tools.Warn("VSM are now replaced by ESM. Please use useBlurExponentialShadowMap instead.");
  143. this.useBlurExponentialShadowMap = value;
  144. }
  145. public get useExponentialShadowMap(): boolean {
  146. return this.filter === ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP;
  147. }
  148. public set useExponentialShadowMap(value: boolean) {
  149. this.filter = (value ? ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
  150. }
  151. public get useBlurExponentialShadowMap(): boolean {
  152. return this.filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP;
  153. }
  154. public set useBlurExponentialShadowMap(value: boolean) {
  155. this.filter = (value ? ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
  156. }
  157. public get useCloseExponentialShadowMap(): boolean {
  158. return this.filter === ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP;
  159. }
  160. public set useCloseExponentialShadowMap(value: boolean) {
  161. this.filter = (value ? ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
  162. }
  163. public get useBlurCloseExponentialShadowMap(): boolean {
  164. return this.filter === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP;
  165. }
  166. public set useBlurCloseExponentialShadowMap(value: boolean) {
  167. this.filter = (value ? ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP : ShadowGenerator.FILTER_NONE);
  168. }
  169. private _darkness = 0;
  170. /**
  171. * Returns the darkness value (float).
  172. */
  173. public getDarkness(): number {
  174. return this._darkness;
  175. }
  176. /**
  177. * Sets the ShadowGenerator darkness value (float <= 1.0).
  178. * Returns the ShadowGenerator.
  179. */
  180. public setDarkness(darkness: number): ShadowGenerator {
  181. if (darkness >= 1.0)
  182. this._darkness = 1.0;
  183. else if (darkness <= 0.0)
  184. this._darkness = 0.0;
  185. else
  186. this._darkness = darkness;
  187. return this;
  188. }
  189. private _transparencyShadow = false;
  190. /**
  191. * Sets the ability to have transparent shadow (boolean).
  192. * Returns the ShadowGenerator.
  193. */
  194. public setTransparencyShadow(hasShadow: boolean): ShadowGenerator {
  195. this._transparencyShadow = hasShadow;
  196. return this;
  197. }
  198. private _shadowMap: RenderTargetTexture;
  199. private _shadowMap2: RenderTargetTexture;
  200. /**
  201. * Returns a RenderTargetTexture object : the shadow map texture.
  202. */
  203. public getShadowMap(): RenderTargetTexture {
  204. return this._shadowMap;
  205. }
  206. /**
  207. * Returns the most ready computed shadow map as a RenderTargetTexture object.
  208. */
  209. public getShadowMapForRendering(): RenderTargetTexture {
  210. if (this._shadowMap2) {
  211. return this._shadowMap2;
  212. }
  213. return this._shadowMap;
  214. }
  215. private _light: IShadowLight;
  216. /**
  217. * Returns the associated light object.
  218. */
  219. public getLight(): IShadowLight {
  220. return this._light;
  221. }
  222. public forceBackFacesOnly = false;
  223. private _scene: Scene;
  224. private _lightDirection = Vector3.Zero();
  225. private _effect: Effect;
  226. private _viewMatrix = Matrix.Zero();
  227. private _projectionMatrix = Matrix.Zero();
  228. private _transformMatrix = Matrix.Zero();
  229. private _worldViewProjection = Matrix.Zero();
  230. private _cachedPosition: Vector3;
  231. private _cachedDirection: Vector3;
  232. private _cachedDefines: string;
  233. private _currentRenderID: number;
  234. private _downSamplePostprocess: PassPostProcess;
  235. private _boxBlurPostprocess: PostProcess;
  236. private _kernelBlurXPostprocess: PostProcess;
  237. private _kernelBlurYPostprocess: PostProcess;
  238. private _blurPostProcesses: PostProcess[];
  239. private _mapSize: number;
  240. private _currentFaceIndex = 0;
  241. private _currentFaceIndexCache = 0;
  242. private _textureType: number;
  243. private _isCube = false;
  244. /**
  245. * Creates a ShadowGenerator object.
  246. * A ShadowGenerator is the required tool to use the shadows.
  247. * Each light casting shadows needs to use its own ShadowGenerator.
  248. * Required parameters :
  249. * - `mapSize` (integer), the size of the texture what stores the shadows. Example : 1024.
  250. * - `light` : the light object generating the shadows.
  251. * Documentation : http://doc.babylonjs.com/tutorials/shadows
  252. */
  253. constructor(mapSize: number, light: IShadowLight) {
  254. this._mapSize = mapSize;
  255. this._light = light;
  256. this._scene = light.getScene();
  257. light._shadowGenerator = this;
  258. // Texture type fallback from float to int if not supported.
  259. var caps = this._scene.getEngine().getCaps();
  260. if (caps.textureFloatRender && caps.textureFloatLinearFiltering) {
  261. this._textureType = Engine.TEXTURETYPE_FLOAT;
  262. }
  263. else if (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering) {
  264. this._textureType = Engine.TEXTURETYPE_HALF_FLOAT;
  265. }
  266. else {
  267. this._textureType = Engine.TEXTURETYPE_UNSIGNED_INT;
  268. }
  269. this._initializeGenerator();
  270. }
  271. private _initializeGenerator(): void {
  272. this._light._markMeshesAsLightDirty();
  273. this._initializeShadowMap();
  274. }
  275. private _initializeShadowMap(): void {
  276. // Render target
  277. this._shadowMap = new RenderTargetTexture(this._light.name + "_shadowMap", this._mapSize, this._scene, false, true, this._textureType, this._light.needCube());
  278. this._shadowMap.wrapU = Texture.CLAMP_ADDRESSMODE;
  279. this._shadowMap.wrapV = Texture.CLAMP_ADDRESSMODE;
  280. this._shadowMap.anisotropicFilteringLevel = 1;
  281. this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  282. this._shadowMap.renderParticles = false;
  283. // Record Face Index before render.
  284. this._shadowMap.onBeforeRenderObservable.add((faceIndex: number) => {
  285. this._currentFaceIndex = faceIndex;
  286. });
  287. // Custom render function.
  288. this._shadowMap.customRenderFunction = this._renderForShadowMap.bind(this);
  289. // Blur if required afer render.
  290. this._shadowMap.onAfterUnbindObservable.add(() => {
  291. if (!this.useBlurExponentialShadowMap && !this.useBlurCloseExponentialShadowMap) {
  292. return;
  293. }
  294. if (!this._blurPostProcesses) {
  295. this._initializeBlurRTTAndPostProcesses();
  296. }
  297. this._scene.postProcessManager.directRender(this._blurPostProcesses, this.getShadowMapForRendering().getInternalTexture());
  298. });
  299. // Clear according to the chosen filter.
  300. this._shadowMap.onClearObservable.add((engine: Engine) => {
  301. if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
  302. engine.clear(new Color4(0, 0, 0, 0), true, true, true);
  303. }
  304. else {
  305. engine.clear(new Color4(1.0, 1.0, 1.0, 1.0), true, true, true);
  306. }
  307. });
  308. }
  309. private _initializeBlurRTTAndPostProcesses(): void {
  310. var engine = this._scene.getEngine();
  311. var targetSize = this._mapSize / this.blurScale;
  312. if (!this.useKernelBlur || this.blurScale !== 1.0) {
  313. this._shadowMap2 = new RenderTargetTexture(this._light.name + "_shadowMap2", targetSize, this._scene, false, true, this._textureType);
  314. this._shadowMap2.wrapU = Texture.CLAMP_ADDRESSMODE;
  315. this._shadowMap2.wrapV = Texture.CLAMP_ADDRESSMODE;
  316. this._shadowMap2.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  317. }
  318. if (this.useKernelBlur) {
  319. this._kernelBlurXPostprocess = new BlurPostProcess(this._light.name + "KernelBlurX", new Vector2(1, 0), this.blurKernel, 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._textureType);
  320. this._kernelBlurXPostprocess.width = targetSize;
  321. this._kernelBlurXPostprocess.height = targetSize;
  322. this._kernelBlurXPostprocess.onApplyObservable.add(effect => {
  323. effect.setTexture("textureSampler", this._shadowMap);
  324. });
  325. this._kernelBlurYPostprocess = new BlurPostProcess(this._light.name + "KernelBlurY", new Vector2(0, 1), this.blurKernel, 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._textureType);
  326. this._kernelBlurXPostprocess.autoClear = false;
  327. this._kernelBlurYPostprocess.autoClear = false;
  328. this._blurPostProcesses = [this._kernelBlurXPostprocess, this._kernelBlurYPostprocess];
  329. }
  330. else {
  331. 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);
  332. this._boxBlurPostprocess.onApplyObservable.add(effect => {
  333. effect.setFloat2("screenSize", targetSize, targetSize);
  334. effect.setTexture("textureSampler", this._shadowMap);
  335. });
  336. this._boxBlurPostprocess.autoClear = false;
  337. this._blurPostProcesses = [this._boxBlurPostprocess];
  338. }
  339. }
  340. private _renderForShadowMap(opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>): void {
  341. var index: number;
  342. for (index = 0; index < opaqueSubMeshes.length; index++) {
  343. this._renderSubMeshForShadowMap(opaqueSubMeshes.data[index]);
  344. }
  345. for (index = 0; index < alphaTestSubMeshes.length; index++) {
  346. this._renderSubMeshForShadowMap(alphaTestSubMeshes.data[index]);
  347. }
  348. if (this._transparencyShadow) {
  349. for (index = 0; index < transparentSubMeshes.length; index++) {
  350. this._renderSubMeshForShadowMap(transparentSubMeshes.data[index]);
  351. }
  352. }
  353. }
  354. private _renderSubMeshForShadowMap(subMesh: SubMesh): void {
  355. var mesh = subMesh.getRenderingMesh();
  356. var scene = this._scene;
  357. var engine = scene.getEngine();
  358. // Culling
  359. engine.setState(subMesh.getMaterial().backFaceCulling);
  360. // Managing instances
  361. var batch = mesh._getInstancesRenderList(subMesh._id);
  362. if (batch.mustReturn) {
  363. return;
  364. }
  365. var hardwareInstancedRendering = (engine.getCaps().instancedArrays !== null) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
  366. if (this.isReady(subMesh, hardwareInstancedRendering)) {
  367. engine.enableEffect(this._effect);
  368. mesh._bind(subMesh, this._effect, Material.TriangleFillMode);
  369. var material = subMesh.getMaterial();
  370. this._effect.setFloat2("biasAndScale", this.bias, this.depthScale);
  371. this._effect.setMatrix("viewProjection", this.getTransformMatrix());
  372. this._effect.setVector3("lightPosition", this.getLight().position);
  373. this._effect.setFloat2("depthValues", this.getLight().getDepthMinZ(scene.activeCamera), this.getLight().getDepthMinZ(scene.activeCamera) + this.getLight().getDepthMaxZ(scene.activeCamera));
  374. // Alpha test
  375. if (material && material.needAlphaTesting()) {
  376. var alphaTexture = material.getAlphaTestTexture();
  377. this._effect.setTexture("diffuseSampler", alphaTexture);
  378. this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
  379. }
  380. // Bones
  381. if (mesh.useBones && mesh.computeBonesUsingShaders) {
  382. this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
  383. }
  384. if (this.forceBackFacesOnly) {
  385. engine.setState(true, 0, false, true);
  386. }
  387. // Draw
  388. mesh._processRendering(subMesh, this._effect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
  389. (isInstance, world) => this._effect.setMatrix("world", world));
  390. if (this.forceBackFacesOnly) {
  391. engine.setState(true, 0, false, false);
  392. }
  393. } else {
  394. // Need to reset refresh rate of the shadowMap
  395. this._shadowMap.resetRefreshCounter();
  396. }
  397. }
  398. private _applyFilterValues(): void {
  399. if (this.filter === ShadowGenerator.FILTER_NONE) {
  400. this._shadowMap.anisotropicFilteringLevel = 1;
  401. this._shadowMap.updateSamplingMode(Texture.NEAREST_SAMPLINGMODE);
  402. } else {
  403. this._shadowMap.anisotropicFilteringLevel = 16;
  404. this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  405. }
  406. }
  407. /**
  408. * Boolean : true when the ShadowGenerator is finally computed.
  409. */
  410. public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
  411. var defines = [];
  412. if (this._textureType !== Engine.TEXTURETYPE_UNSIGNED_INT) {
  413. defines.push("#define FLOAT");
  414. }
  415. if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
  416. defines.push("#define ESM");
  417. }
  418. var attribs = [VertexBuffer.PositionKind];
  419. var mesh = subMesh.getMesh();
  420. var material = subMesh.getMaterial();
  421. // Alpha test
  422. if (material && material.needAlphaTesting()) {
  423. defines.push("#define ALPHATEST");
  424. if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
  425. attribs.push(VertexBuffer.UVKind);
  426. defines.push("#define UV1");
  427. }
  428. if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
  429. var alphaTexture = material.getAlphaTestTexture();
  430. if (alphaTexture.coordinatesIndex === 1) {
  431. attribs.push(VertexBuffer.UV2Kind);
  432. defines.push("#define UV2");
  433. }
  434. }
  435. }
  436. // Bones
  437. if (mesh.useBones && mesh.computeBonesUsingShaders) {
  438. attribs.push(VertexBuffer.MatricesIndicesKind);
  439. attribs.push(VertexBuffer.MatricesWeightsKind);
  440. if (mesh.numBoneInfluencers > 4) {
  441. attribs.push(VertexBuffer.MatricesIndicesExtraKind);
  442. attribs.push(VertexBuffer.MatricesWeightsExtraKind);
  443. }
  444. defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
  445. defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
  446. } else {
  447. defines.push("#define NUM_BONE_INFLUENCERS 0");
  448. }
  449. // Instances
  450. if (useInstances) {
  451. defines.push("#define INSTANCES");
  452. attribs.push("world0");
  453. attribs.push("world1");
  454. attribs.push("world2");
  455. attribs.push("world3");
  456. }
  457. // Get correct effect
  458. var join = defines.join("\n");
  459. if (this._cachedDefines !== join) {
  460. this._cachedDefines = join;
  461. this._effect = this._scene.getEngine().createEffect("shadowMap",
  462. attribs,
  463. ["world", "mBones", "viewProjection", "diffuseMatrix", "lightPosition", "depthValues", "biasAndScale"],
  464. ["diffuseSampler"], join);
  465. }
  466. return this._effect.isReady();
  467. }
  468. /**
  469. * This creates the defines related to the standard BJS materials.
  470. */
  471. public prepareDefines(defines: MaterialDefines, lightIndex: number): void {
  472. var scene = this._scene;
  473. var light = this._light;
  474. if (!scene.shadowsEnabled || !light.shadowEnabled) {
  475. return;
  476. }
  477. defines["SHADOW" + lightIndex] = true;
  478. if (this.usePoissonSampling) {
  479. defines["SHADOWPCF" + lightIndex] = true;
  480. }
  481. else if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
  482. defines["SHADOWESM" + lightIndex] = true;
  483. }
  484. else if (this.useCloseExponentialShadowMap || this.useBlurCloseExponentialShadowMap) {
  485. defines["SHADOWCLOSEESM" + lightIndex] = true;
  486. }
  487. if (light.needCube()) {
  488. defines["SHADOWCUBE" + lightIndex] = true;
  489. }
  490. }
  491. /**
  492. * This binds shadow lights related to the standard BJS materials.
  493. * It implies the unifroms available on the materials are the standard BJS ones.
  494. */
  495. public bindShadowLight(lightIndex: string, effect: Effect): void {
  496. var light = this._light;
  497. var scene = this._scene;
  498. if (!scene.shadowsEnabled || !light.shadowEnabled) {
  499. return;
  500. }
  501. if (!light.needCube()) {
  502. effect.setMatrix("lightMatrix" + lightIndex, this.getTransformMatrix());
  503. }
  504. effect.setTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
  505. light._uniformBuffer.updateFloat3("shadowsInfo", this.getDarkness(), this.blurScale / this.getShadowMap().getSize().width, this.depthScale, lightIndex);
  506. light._uniformBuffer.updateFloat2("depthValues", this.getLight().getDepthMinZ(scene.activeCamera), this.getLight().getDepthMinZ(scene.activeCamera) + this.getLight().getDepthMaxZ(scene.activeCamera), lightIndex);
  507. }
  508. // Methods
  509. /**
  510. * Returns a Matrix object : the updated transformation matrix.
  511. */
  512. public getTransformMatrix(): Matrix {
  513. var scene = this._scene;
  514. if (this._currentRenderID === scene.getRenderId() && this._currentFaceIndexCache === this._currentFaceIndex) {
  515. return this._transformMatrix;
  516. }
  517. this._currentRenderID = scene.getRenderId();
  518. this._currentFaceIndexCache = this._currentFaceIndex;
  519. var lightPosition = this._light.position;
  520. if (this._light.computeTransformedInformation()) {
  521. lightPosition = this._light.transformedPosition;
  522. }
  523. Vector3.NormalizeToRef(this._light.getShadowDirection(this._currentFaceIndex), this._lightDirection);
  524. if (Math.abs(Vector3.Dot(this._lightDirection, Vector3.Up())) === 1.0) {
  525. this._lightDirection.z = 0.0000000000001; // Required to avoid perfectly perpendicular light
  526. }
  527. if (this._light.needProjectionMatrixCompute() || !this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !this._lightDirection.equals(this._cachedDirection)) {
  528. this._cachedPosition = lightPosition.clone();
  529. this._cachedDirection = this._lightDirection.clone();
  530. Matrix.LookAtLHToRef(lightPosition, lightPosition.add(this._lightDirection), Vector3.Up(), this._viewMatrix);
  531. this._light.setShadowProjectionMatrix(this._projectionMatrix, this._viewMatrix, this.getShadowMap().renderList);
  532. this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
  533. }
  534. return this._transformMatrix;
  535. }
  536. public recreateShadowMap(): void {
  537. // Track render list.
  538. var renderList = this._shadowMap.renderList;
  539. // Clean up existing data.
  540. this._disposeRTTandPostProcesses();
  541. // Reinitializes.
  542. this._initializeGenerator();
  543. // Reaffect the filter to ensure a correct fallback if necessary.
  544. this.filter = this.filter;
  545. // Reaffect the filter.
  546. this._applyFilterValues();
  547. // Reaffect Render List.
  548. this._shadowMap.renderList = renderList;
  549. }
  550. private _disposeBlurPostProcesses(): void {
  551. if (this._shadowMap2) {
  552. this._shadowMap2.dispose();
  553. this._shadowMap2 = null;
  554. }
  555. if (this._downSamplePostprocess) {
  556. this._downSamplePostprocess.dispose();
  557. this._downSamplePostprocess = null;
  558. }
  559. if (this._boxBlurPostprocess) {
  560. this._boxBlurPostprocess.dispose();
  561. this._boxBlurPostprocess = null;
  562. }
  563. if (this._kernelBlurXPostprocess) {
  564. this._kernelBlurXPostprocess.dispose();
  565. this._kernelBlurXPostprocess = null;
  566. }
  567. if (this._kernelBlurYPostprocess) {
  568. this._kernelBlurYPostprocess.dispose();
  569. this._kernelBlurYPostprocess = null;
  570. }
  571. this._blurPostProcesses = null;
  572. }
  573. private _disposeRTTandPostProcesses(): void {
  574. if (this._shadowMap) {
  575. this._shadowMap.dispose();
  576. this._shadowMap = null;
  577. }
  578. this._disposeBlurPostProcesses();
  579. }
  580. /**
  581. * Disposes the ShadowGenerator.
  582. * Returns nothing.
  583. */
  584. public dispose(): void {
  585. this._disposeRTTandPostProcesses();
  586. this._light._shadowGenerator = null;
  587. this._light._markMeshesAsLightDirty();
  588. }
  589. /**
  590. * Serializes the ShadowGenerator and returns a serializationObject.
  591. */
  592. public serialize(): any {
  593. var serializationObject: any = {};
  594. var shadowMap = this.getShadowMap();
  595. serializationObject.lightId = this._light.id;
  596. serializationObject.mapSize = shadowMap.getRenderSize();
  597. serializationObject.useExponentialShadowMap = this.useExponentialShadowMap;
  598. serializationObject.useBlurExponentialShadowMap = this.useBlurExponentialShadowMap;
  599. serializationObject.useCloseExponentialShadowMap = this.useBlurExponentialShadowMap;
  600. serializationObject.useBlurCloseExponentialShadowMap = this.useBlurExponentialShadowMap;
  601. serializationObject.usePoissonSampling = this.usePoissonSampling;
  602. serializationObject.forceBackFacesOnly = this.forceBackFacesOnly;
  603. serializationObject.depthScale = this.depthScale;
  604. serializationObject.darkness = this.getDarkness();
  605. serializationObject.blurBoxOffset = this.blurBoxOffset;
  606. serializationObject.blurKernel = this.blurKernel;
  607. serializationObject.blurScale = this.blurScale;
  608. serializationObject.useKernelBlur = this.useKernelBlur;
  609. serializationObject.transparencyShadow = this._transparencyShadow;
  610. serializationObject.renderList = [];
  611. for (var meshIndex = 0; meshIndex < shadowMap.renderList.length; meshIndex++) {
  612. var mesh = shadowMap.renderList[meshIndex];
  613. serializationObject.renderList.push(mesh.id);
  614. }
  615. return serializationObject;
  616. }
  617. /**
  618. * Parses a serialized ShadowGenerator and returns a new ShadowGenerator.
  619. */
  620. public static Parse(parsedShadowGenerator: any, scene: Scene): ShadowGenerator {
  621. //casting to point light, as light is missing the position attr and typescript complains.
  622. var light = <PointLight>scene.getLightByID(parsedShadowGenerator.lightId);
  623. var shadowGenerator = new ShadowGenerator(parsedShadowGenerator.mapSize, light);
  624. var shadowMap = shadowGenerator.getShadowMap();
  625. for (var meshIndex = 0; meshIndex < parsedShadowGenerator.renderList.length; meshIndex++) {
  626. var meshes = scene.getMeshesByID(parsedShadowGenerator.renderList[meshIndex]);
  627. meshes.forEach(function (mesh) {
  628. shadowMap.renderList.push(mesh);
  629. });
  630. }
  631. if (parsedShadowGenerator.usePoissonSampling) {
  632. shadowGenerator.usePoissonSampling = true;
  633. }
  634. else if (parsedShadowGenerator.useExponentialShadowMap) {
  635. shadowGenerator.useExponentialShadowMap = true;
  636. }
  637. else if (parsedShadowGenerator.useBlurExponentialShadowMap) {
  638. shadowGenerator.useBlurExponentialShadowMap = true;
  639. }
  640. else if (parsedShadowGenerator.useCloseExponentialShadowMap) {
  641. shadowGenerator.useCloseExponentialShadowMap = true;
  642. }
  643. else if (parsedShadowGenerator.useBlurExponentialShadowMap) {
  644. shadowGenerator.useBlurCloseExponentialShadowMap = true;
  645. }
  646. // Backward compat
  647. else if (parsedShadowGenerator.useVarianceShadowMap) {
  648. shadowGenerator.useExponentialShadowMap = true;
  649. }
  650. else if (parsedShadowGenerator.useBlurVarianceShadowMap) {
  651. shadowGenerator.useBlurExponentialShadowMap = true;
  652. }
  653. if (parsedShadowGenerator.depthScale) {
  654. shadowGenerator.depthScale = parsedShadowGenerator.depthScale;
  655. }
  656. if (parsedShadowGenerator.blurScale) {
  657. shadowGenerator.blurScale = parsedShadowGenerator.blurScale;
  658. }
  659. if (parsedShadowGenerator.blurBoxOffset) {
  660. shadowGenerator.blurBoxOffset = parsedShadowGenerator.blurBoxOffset;
  661. }
  662. if (parsedShadowGenerator.useKernelBlur) {
  663. shadowGenerator.useKernelBlur = parsedShadowGenerator.useKernelBlur;
  664. }
  665. if (parsedShadowGenerator.blurKernel) {
  666. shadowGenerator.blurKernel = parsedShadowGenerator.blurKernel;
  667. }
  668. if (parsedShadowGenerator.bias !== undefined) {
  669. shadowGenerator.bias = parsedShadowGenerator.bias;
  670. }
  671. if (parsedShadowGenerator.darkness) {
  672. shadowGenerator.setDarkness(parsedShadowGenerator.darkness);
  673. }
  674. if (parsedShadowGenerator.transparencyShadow) {
  675. shadowGenerator.setTransparencyShadow(true);
  676. }
  677. shadowGenerator.forceBackFacesOnly = parsedShadowGenerator.forceBackFacesOnly;
  678. return shadowGenerator;
  679. }
  680. }
  681. }