prePassRenderer.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. import { PrePassRenderTarget } from "../Materials/Textures/prePassRenderTarget";
  2. import { Scene } from "../scene";
  3. import { Engine } from "../Engines/engine";
  4. import { Constants } from "../Engines/constants";
  5. import { PostProcess } from "../PostProcesses/postProcess";
  6. import { Effect } from "../Materials/effect";
  7. import { _DevTools } from '../Misc/devTools';
  8. import { Color4 } from "../Maths/math.color";
  9. import { Nullable } from "../types";
  10. import { AbstractMesh } from '../Meshes/abstractMesh';
  11. import { Camera } from '../Cameras/camera';
  12. import { Material } from '../Materials/material';
  13. import { SubMesh } from '../Meshes/subMesh';
  14. import { PrePassEffectConfiguration } from "./prePassEffectConfiguration";
  15. import { RenderTargetTexture } from "../Materials/Textures/renderTargetTexture";
  16. import { GeometryBufferRenderer } from '../Rendering/geometryBufferRenderer';
  17. /**
  18. * Renders a pre pass of the scene
  19. * This means every mesh in the scene will be rendered to a render target texture
  20. * And then this texture will be composited to the rendering canvas with post processes
  21. * It is necessary for effects like subsurface scattering or deferred shading
  22. */
  23. export class PrePassRenderer {
  24. /** @hidden */
  25. public static _SceneComponentInitialization: (scene: Scene) => void = (_) => {
  26. throw _DevTools.WarnImport("PrePassRendererSceneComponent");
  27. }
  28. /**
  29. * To save performance, we can excluded skinned meshes from the prepass
  30. */
  31. public excludedSkinnedMesh: AbstractMesh[] = [];
  32. /**
  33. * Force material to be excluded from the prepass
  34. * Can be useful when `useGeometryBufferFallback` is set to `true`
  35. * and you don't want a material to show in the effect.
  36. */
  37. public excludedMaterials: Material[] = [];
  38. private _scene: Scene;
  39. private _engine: Engine;
  40. /**
  41. * Number of textures in the multi render target texture where the scene is directly rendered
  42. */
  43. public mrtCount: number = 0;
  44. private _mrtFormats: number[] = [];
  45. private _mrtLayout: number[] = [];
  46. private _textureIndices: number[] = [];
  47. private _multiRenderAttachments: number[];
  48. private _defaultAttachments: number[];
  49. private _clearAttachments: number[];
  50. /**
  51. * Returns the index of a texture in the multi render target texture array.
  52. * @param type Texture type
  53. * @return The index
  54. */
  55. public getIndex(type: number) : number {
  56. return this._textureIndices[type];
  57. }
  58. /**
  59. * How many samples are used for MSAA of the scene render target
  60. */
  61. public get samples() {
  62. return this.defaultRT.samples;
  63. }
  64. public set samples(n: number) {
  65. this.defaultRT.samples = n;
  66. }
  67. private static _textureFormats = [
  68. {
  69. type: Constants.PREPASS_IRRADIANCE_TEXTURE_TYPE,
  70. format: Constants.TEXTURETYPE_HALF_FLOAT,
  71. },
  72. {
  73. type: Constants.PREPASS_POSITION_TEXTURE_TYPE,
  74. format: Constants.TEXTURETYPE_HALF_FLOAT,
  75. },
  76. {
  77. type: Constants.PREPASS_VELOCITY_TEXTURE_TYPE,
  78. format: Constants.TEXTURETYPE_HALF_FLOAT,
  79. },
  80. {
  81. type: Constants.PREPASS_REFLECTIVITY_TEXTURE_TYPE,
  82. format: Constants.TEXTURETYPE_UNSIGNED_INT,
  83. },
  84. {
  85. type: Constants.PREPASS_COLOR_TEXTURE_TYPE,
  86. format: Constants.TEXTURETYPE_HALF_FLOAT,
  87. },
  88. {
  89. type: Constants.PREPASS_DEPTH_TEXTURE_TYPE,
  90. format: Constants.TEXTURETYPE_HALF_FLOAT,
  91. },
  92. {
  93. type: Constants.PREPASS_NORMAL_TEXTURE_TYPE,
  94. format: Constants.TEXTURETYPE_HALF_FLOAT,
  95. },
  96. {
  97. type: Constants.PREPASS_ALBEDO_TEXTURE_TYPE,
  98. format: Constants.TEXTURETYPE_UNSIGNED_INT,
  99. },
  100. ];
  101. private _isDirty: boolean = true;
  102. /**
  103. * The render target where the scene is directly rendered
  104. */
  105. public defaultRT: PrePassRenderTarget;
  106. /**
  107. * Configuration for prepass effects
  108. */
  109. private _effectConfigurations: PrePassEffectConfiguration[] = [];
  110. /**
  111. * Returns the prepass render target for the rendering pass.
  112. * If we are currently rendering a render target, it returns the PrePassRenderTarget
  113. * associated with that render target. Otherwise, it returns the scene default PrePassRenderTarget
  114. */
  115. public getRenderTarget(): PrePassRenderTarget {
  116. return this._currentTarget;
  117. }
  118. public _setRenderTarget(prePassRenderTarget: Nullable<PrePassRenderTarget>): void {
  119. if (prePassRenderTarget) {
  120. this._currentTarget = prePassRenderTarget;
  121. } else {
  122. this._currentTarget = this.defaultRT;
  123. }
  124. }
  125. /**
  126. * Returns true if the currently rendered prePassRenderTarget is the one
  127. * associated with the scene.
  128. */
  129. public get currentRTisSceneRT(): boolean {
  130. return this._currentTarget === this.defaultRT;
  131. }
  132. private _geometryBuffer: Nullable<GeometryBufferRenderer>;
  133. /**
  134. * Prevents the PrePassRenderer from using the GeometryBufferRenderer as a fallback
  135. */
  136. public doNotUseGeometryRendererFallback = true;
  137. private _refreshGeometryBufferRendererLink() {
  138. if (!this.doNotUseGeometryRendererFallback) {
  139. this._geometryBuffer = this._scene.enableGeometryBufferRenderer();
  140. if (!this._geometryBuffer) {
  141. // Not supported
  142. this.doNotUseGeometryRendererFallback = true;
  143. return;
  144. }
  145. this._geometryBuffer._linkPrePassRenderer(this);
  146. } else {
  147. if (this._geometryBuffer) {
  148. this._geometryBuffer._unlinkPrePassRenderer();
  149. }
  150. this._geometryBuffer = null;
  151. this._scene.disableGeometryBufferRenderer();
  152. }
  153. }
  154. private _currentTarget: PrePassRenderTarget;
  155. /**
  156. * All the render targets generated by prepass
  157. */
  158. public renderTargets: PrePassRenderTarget[] = [];
  159. private readonly _clearColor = new Color4(0, 0, 0, 0);
  160. private _enabled: boolean = false;
  161. private _needsCompositionForThisPass = false;
  162. private _postProcessesSourceForThisPass: Nullable<PostProcess>[];
  163. /**
  164. * Indicates if the prepass is enabled
  165. */
  166. public get enabled() {
  167. return this._enabled;
  168. }
  169. /**
  170. * Set to true to disable gamma transform in PrePass.
  171. * Can be useful in case you already proceed to gamma transform on a material level
  172. * and your post processes don't need to be in linear color space.
  173. */
  174. public disableGammaTransform = false;
  175. /**
  176. * Instanciates a prepass renderer
  177. * @param scene The scene
  178. */
  179. constructor(scene: Scene) {
  180. this._scene = scene;
  181. this._engine = scene.getEngine();
  182. PrePassRenderer._SceneComponentInitialization(this._scene);
  183. this.defaultRT = this._createRenderTarget("sceneprePassRT", null);
  184. this._setRenderTarget(null);
  185. }
  186. public _createRenderTarget(name: string, renderTargetTexture: Nullable<RenderTargetTexture>) : PrePassRenderTarget {
  187. const rt = new PrePassRenderTarget(name, renderTargetTexture, { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight() }, 0, this._scene,
  188. { generateMipMaps: false, generateStencilBuffer: true, defaultType: Constants.TEXTURETYPE_UNSIGNED_INT, types: [], drawOnlyOnFirstAttachmentByDefault: true });
  189. this.renderTargets.push(rt);
  190. return rt;
  191. }
  192. /**
  193. * Indicates if rendering a prepass is supported
  194. */
  195. public get isSupported() {
  196. return this._engine.webGLVersion > 1 || this._scene.getEngine().getCaps().drawBuffersExtension;
  197. }
  198. /**
  199. * Sets the proper output textures to draw in the engine.
  200. * @param effect The effect that is drawn. It can be or not be compatible with drawing to several output textures.
  201. * @param subMesh Submesh on which the effect is applied
  202. */
  203. public bindAttachmentsForEffect(effect: Effect, subMesh: SubMesh) {
  204. if (this.enabled && this._currentTarget.enabled) {
  205. if (effect._multiTarget) {
  206. this._engine.bindAttachments(this._multiRenderAttachments);
  207. } else {
  208. this._engine.bindAttachments(this._defaultAttachments);
  209. if (this._geometryBuffer && this.currentRTisSceneRT) {
  210. const material = subMesh.getMaterial();
  211. if (material && this.excludedMaterials.indexOf(material) === -1) {
  212. this._geometryBuffer.renderList!.push(subMesh.getRenderingMesh());
  213. }
  214. }
  215. }
  216. }
  217. }
  218. private _reinitializeAttachments() {
  219. const multiRenderLayout = [];
  220. const clearLayout = [false];
  221. const defaultLayout = [true];
  222. for (let i = 0; i < this.mrtCount; i++) {
  223. multiRenderLayout.push(true);
  224. if (i > 0) {
  225. clearLayout.push(true);
  226. defaultLayout.push(false);
  227. }
  228. }
  229. this._multiRenderAttachments = this._engine.buildTextureLayout(multiRenderLayout);
  230. this._clearAttachments = this._engine.buildTextureLayout(clearLayout);
  231. this._defaultAttachments = this._engine.buildTextureLayout(defaultLayout);
  232. }
  233. private _resetLayout() {
  234. for (let i = 0 ; i < PrePassRenderer._textureFormats.length; i++) {
  235. this._textureIndices[PrePassRenderer._textureFormats[i].type] = -1;
  236. }
  237. this._textureIndices[Constants.PREPASS_COLOR_TEXTURE_TYPE] = 0;
  238. this._mrtLayout = [Constants.PREPASS_COLOR_TEXTURE_TYPE];
  239. this._mrtFormats = [Constants.TEXTURETYPE_HALF_FLOAT];
  240. this.mrtCount = 1;
  241. }
  242. private _updateGeometryBufferLayout() {
  243. this._refreshGeometryBufferRendererLink();
  244. if (this._geometryBuffer) {
  245. this._geometryBuffer._resetLayout();
  246. const texturesActivated = [];
  247. for (let i = 0; i < this._mrtLayout.length; i++) {
  248. texturesActivated.push(false);
  249. }
  250. this._geometryBuffer._linkInternalTexture(this.defaultRT.getInternalTexture()!);
  251. const matches = [
  252. {
  253. prePassConstant: Constants.PREPASS_DEPTH_TEXTURE_TYPE,
  254. geometryBufferConstant: GeometryBufferRenderer.DEPTH_TEXTURE_TYPE,
  255. },
  256. {
  257. prePassConstant: Constants.PREPASS_NORMAL_TEXTURE_TYPE,
  258. geometryBufferConstant: GeometryBufferRenderer.NORMAL_TEXTURE_TYPE,
  259. },
  260. {
  261. prePassConstant: Constants.PREPASS_POSITION_TEXTURE_TYPE,
  262. geometryBufferConstant: GeometryBufferRenderer.POSITION_TEXTURE_TYPE,
  263. },
  264. {
  265. prePassConstant: Constants.PREPASS_REFLECTIVITY_TEXTURE_TYPE,
  266. geometryBufferConstant: GeometryBufferRenderer.REFLECTIVITY_TEXTURE_TYPE,
  267. },
  268. {
  269. prePassConstant: Constants.PREPASS_VELOCITY_TEXTURE_TYPE,
  270. geometryBufferConstant: GeometryBufferRenderer.VELOCITY_TEXTURE_TYPE,
  271. }
  272. ];
  273. // replace textures in the geometryBuffer RT
  274. for (let i = 0; i < matches.length; i++) {
  275. const index = this._mrtLayout.indexOf(matches[i].prePassConstant);
  276. if (index !== -1) {
  277. this._geometryBuffer._forceTextureType(matches[i].geometryBufferConstant, index);
  278. texturesActivated[index] = true;
  279. }
  280. }
  281. this._geometryBuffer._setAttachments(this._engine.buildTextureLayout(texturesActivated));
  282. }
  283. }
  284. /**
  285. * Restores attachments for single texture draw.
  286. */
  287. public restoreAttachments() {
  288. if (this.enabled && this._currentTarget.enabled && this._defaultAttachments) {
  289. this._engine.bindAttachments(this._defaultAttachments);
  290. }
  291. }
  292. /**
  293. * @hidden
  294. */
  295. public _beforeDraw(camera?: Camera, faceIndex?: number, layer?: number) {
  296. // const previousEnabled = this._enabled && this._currentTarget.enabled;
  297. if (this._isDirty) {
  298. this._update();
  299. }
  300. if (!this._enabled || !this._currentTarget.enabled) {
  301. return;
  302. }
  303. if (this._geometryBuffer) {
  304. this._geometryBuffer.renderList = [];
  305. }
  306. this._setupOutputForThisPass(this._currentTarget, camera);
  307. }
  308. private _prepareFrame(prePassRenderTarget: PrePassRenderTarget, faceIndex?: number, layer?: number) {
  309. if (prePassRenderTarget.renderTargetTexture) {
  310. prePassRenderTarget.renderTargetTexture._prepareFrame(this._scene, faceIndex, layer, prePassRenderTarget.renderTargetTexture.useCameraPostProcesses);
  311. } else if (this._postProcessesSourceForThisPass.length) {
  312. this._scene.postProcessManager._prepareFrame();
  313. } else {
  314. this._engine.restoreDefaultFramebuffer();
  315. }
  316. }
  317. private _renderPostProcesses(prePassRenderTarget: PrePassRenderTarget, faceIndex?: number) {
  318. const firstPP = this._postProcessesSourceForThisPass[0];
  319. let outputTexture = firstPP ? firstPP.inputTexture : (prePassRenderTarget.renderTargetTexture ? prePassRenderTarget.renderTargetTexture.getInternalTexture() : null);
  320. // Build post process chain for this prepass post draw
  321. let postProcessChain = this._currentTarget._beforeCompositionPostProcesses;
  322. if (this._needsCompositionForThisPass) {
  323. postProcessChain = postProcessChain.concat([this._currentTarget.imageProcessingPostProcess]);
  324. }
  325. // Activates and renders the chain
  326. if (postProcessChain.length) {
  327. this._scene.postProcessManager._prepareFrame(this._currentTarget.getInternalTexture()!, postProcessChain);
  328. this._scene.postProcessManager.directRender(postProcessChain, outputTexture, false, faceIndex);
  329. }
  330. }
  331. /**
  332. * @hidden
  333. */
  334. public _afterDraw(faceIndex?: number, layer?: number) {
  335. if (this._enabled && this._currentTarget.enabled) {
  336. this._prepareFrame(this._currentTarget, faceIndex, layer);
  337. this._renderPostProcesses(this._currentTarget, faceIndex);
  338. }
  339. }
  340. /**
  341. * Clears the current prepass render target (in the sense of settings pixels to the scene clear color value)
  342. * @hidden
  343. */
  344. public _clear() {
  345. if (this._enabled && this._currentTarget.enabled) {
  346. this._bindFrameBuffer(this._currentTarget);
  347. // Clearing other attachment with 0 on all other attachments
  348. this._engine.bindAttachments(this._clearAttachments);
  349. this._engine.clear(this._clearColor, true, false, false);
  350. // Regular clear color with the scene clear color of the 1st attachment
  351. this._engine.bindAttachments(this._defaultAttachments);
  352. }
  353. }
  354. private _bindFrameBuffer(prePassRenderTarget: PrePassRenderTarget) {
  355. if (this._enabled && this._currentTarget.enabled) {
  356. this._currentTarget._checkSize();
  357. var internalTexture = this._currentTarget.getInternalTexture();
  358. if (internalTexture) {
  359. this._engine.bindFramebuffer(internalTexture);
  360. }
  361. }
  362. }
  363. private _setState(enabled: boolean) {
  364. this._enabled = enabled;
  365. }
  366. private _setRenderTargetState(prePassRenderTarget: PrePassRenderTarget, enabled: boolean) {
  367. prePassRenderTarget.enabled = enabled;
  368. }
  369. /**
  370. * Adds an effect configuration to the prepass render target.
  371. * If an effect has already been added, it won't add it twice and will return the configuration
  372. * already present.
  373. * @param cfg the effect configuration
  374. * @return the effect configuration now used by the prepass
  375. */
  376. public addEffectConfiguration(cfg: PrePassEffectConfiguration) : PrePassEffectConfiguration {
  377. // Do not add twice
  378. for (let i = 0; i < this._effectConfigurations.length; i++) {
  379. if (this._effectConfigurations[i].name === cfg.name) {
  380. return this._effectConfigurations[i];
  381. }
  382. }
  383. this._effectConfigurations.push(cfg);
  384. return cfg;
  385. }
  386. private _enable() {
  387. const previousMrtCount = this.mrtCount;
  388. for (let i = 0; i < this._effectConfigurations.length; i++) {
  389. if (this._effectConfigurations[i].enabled) {
  390. this._enableTextures(this._effectConfigurations[i].texturesRequired);
  391. }
  392. }
  393. for (let i = 0; i < this.renderTargets.length; i++) {
  394. if (this.mrtCount !== previousMrtCount) {
  395. this.renderTargets[i].updateCount(this.mrtCount, { types: this._mrtFormats });
  396. }
  397. this.renderTargets[i]._resetPostProcessChain();
  398. for (let j = 0; j < this._effectConfigurations.length; j++) {
  399. if (this._effectConfigurations[j].enabled) {
  400. // TODO : subsurface scattering has 1 scene-wide effect configuration
  401. // solution : do not stock postProcess on effectConfiguration, but in the prepassRenderTarget (hashmap configuration => postProcess)
  402. // And call createPostProcess whenever the post process does not exist in the RT
  403. if (!this._effectConfigurations[j].postProcess && this._effectConfigurations[j].createPostProcess) {
  404. this._effectConfigurations[j].createPostProcess!();
  405. }
  406. if (this._effectConfigurations[j].postProcess) {
  407. this.renderTargets[i]._beforeCompositionPostProcesses.push(this._effectConfigurations[j].postProcess!);
  408. }
  409. }
  410. }
  411. }
  412. this._reinitializeAttachments();
  413. this._setState(true);
  414. this._updateGeometryBufferLayout();
  415. }
  416. private _disable() {
  417. this._setState(false);
  418. for (let i = 0; i < this.renderTargets.length; i++) {
  419. this._setRenderTargetState(this.renderTargets[i], false);
  420. }
  421. this._resetLayout();
  422. for (let i = 0; i < this._effectConfigurations.length; i++) {
  423. this._effectConfigurations[i].enabled = false;
  424. }
  425. }
  426. private _getPostProcessesSource(prePassRenderTarget: PrePassRenderTarget, camera?: Camera) : Nullable<PostProcess>[] {
  427. if (camera) {
  428. return camera._postProcesses;
  429. } else if (prePassRenderTarget.renderTargetTexture) {
  430. if (prePassRenderTarget.renderTargetTexture.useCameraPostProcesses) {
  431. const camera = prePassRenderTarget.renderTargetTexture.activeCamera ? prePassRenderTarget.renderTargetTexture.activeCamera : this._scene.activeCamera;
  432. return camera ? camera._postProcesses : [];
  433. } else if (prePassRenderTarget.renderTargetTexture.postProcesses) {
  434. return prePassRenderTarget.renderTargetTexture.postProcesses;
  435. } else {
  436. return [];
  437. }
  438. } else {
  439. return this._scene.activeCamera ? this._scene.activeCamera._postProcesses : [];
  440. }
  441. }
  442. private _setupOutputForThisPass(prePassRenderTarget: PrePassRenderTarget, camera?: Camera) {
  443. // Order is : draw ===> prePassRenderTarget._postProcesses ==> ipp ==> camera._postProcesses
  444. const secondaryCamera = camera && this._scene.activeCameras && !!this._scene.activeCameras.length && this._scene.activeCameras.indexOf(camera) !== 0;
  445. this._postProcessesSourceForThisPass = this._getPostProcessesSource(prePassRenderTarget, camera);
  446. this._postProcessesSourceForThisPass = (this._postProcessesSourceForThisPass.filter((pp) => { return pp != null; }));
  447. const cameraHasImageProcessing = this._hasImageProcessing(this._postProcessesSourceForThisPass);
  448. this._needsCompositionForThisPass = !cameraHasImageProcessing &&
  449. !this.disableGammaTransform &&
  450. this._needsImageProcessing() &&
  451. !secondaryCamera;
  452. const firstCameraPP = this._getFirstPostProcess(this._postProcessesSourceForThisPass);
  453. const firstPrePassPP = prePassRenderTarget._beforeCompositionPostProcesses && prePassRenderTarget._beforeCompositionPostProcesses[0];
  454. let firstPP = null;
  455. // Setting the scene-wide post process configuration
  456. this._scene.imageProcessingConfiguration.applyByPostProcess = this._needsCompositionForThisPass || cameraHasImageProcessing;
  457. // Create composition effect if needed
  458. if (this._needsCompositionForThisPass && !prePassRenderTarget.imageProcessingPostProcess) {
  459. prePassRenderTarget._createCompositionEffect();
  460. }
  461. // Setting the prePassRenderTarget as input texture of the first PP
  462. if (firstPrePassPP) {
  463. firstPP = firstPrePassPP;
  464. } else if (this._needsCompositionForThisPass) {
  465. firstPP = prePassRenderTarget.imageProcessingPostProcess;
  466. } else if (firstCameraPP) {
  467. firstPP = firstCameraPP;
  468. }
  469. this._bindFrameBuffer(prePassRenderTarget);
  470. this._linkInternalTexture(prePassRenderTarget, firstPP);
  471. }
  472. private _linkInternalTexture(prePassRenderTarget: PrePassRenderTarget, postProcess: Nullable<PostProcess>) {
  473. if (postProcess) {
  474. postProcess.autoClear = false;
  475. postProcess.inputTexture = prePassRenderTarget.getInternalTexture()!;
  476. }
  477. if (prePassRenderTarget._outputPostProcess !== postProcess) {
  478. if (prePassRenderTarget._outputPostProcess) {
  479. prePassRenderTarget._outputPostProcess.restoreDefaultInputTexture();
  480. }
  481. prePassRenderTarget._outputPostProcess = postProcess;
  482. }
  483. if (prePassRenderTarget._internalTextureDirty) {
  484. this._updateGeometryBufferLayout();
  485. prePassRenderTarget._internalTextureDirty = false;
  486. }
  487. }
  488. private _needsImageProcessing(): boolean {
  489. for (let i = 0; i < this._effectConfigurations.length; i++) {
  490. if (this._effectConfigurations[i].enabled && this._effectConfigurations[i].needsImageProcessing) {
  491. return true;
  492. }
  493. }
  494. return false;
  495. }
  496. private _hasImageProcessing(postProcesses: Nullable<PostProcess>[]): boolean {
  497. let isIPPAlreadyPresent = false;
  498. if (postProcesses) {
  499. for (let i = 0; i < postProcesses.length; i++) {
  500. if (postProcesses[i]?.getClassName() === "ImageProcessingPostProcess") {
  501. isIPPAlreadyPresent = true;
  502. }
  503. }
  504. }
  505. return isIPPAlreadyPresent;
  506. }
  507. /**
  508. * Internal, gets the first post proces.
  509. * @returns the first post process to be run on this camera.
  510. */
  511. private _getFirstPostProcess(postProcesses: Nullable<PostProcess>[]): Nullable<PostProcess> {
  512. for (var ppIndex = 0; ppIndex < postProcesses.length; ppIndex++) {
  513. if (postProcesses[ppIndex] !== null) {
  514. return postProcesses[ppIndex];
  515. }
  516. }
  517. return null;
  518. }
  519. /**
  520. * Marks the prepass renderer as dirty, triggering a check if the prepass is necessary for the next rendering.
  521. */
  522. public markAsDirty() {
  523. this._isDirty = true;
  524. }
  525. /**
  526. * Enables a texture on the MultiRenderTarget for prepass
  527. */
  528. private _enableTextures(types: number[]) {
  529. for (let i = 0; i < types.length; i++) {
  530. let type = types[i];
  531. if (this._textureIndices[type] === -1) {
  532. this._textureIndices[type] = this._mrtLayout.length;
  533. this._mrtLayout.push(type);
  534. this._mrtFormats.push(PrePassRenderer._textureFormats[type].format);
  535. this.mrtCount++;
  536. }
  537. }
  538. }
  539. private _update() {
  540. this._disable();
  541. let enablePrePass = false;
  542. this._scene.imageProcessingConfiguration.applyByPostProcess = false;
  543. for (let i = 0; i < this._scene.materials.length; i++) {
  544. if (this._scene.materials[i].setPrePassRenderer(this)) {
  545. enablePrePass = true;
  546. this._setRenderTargetState(this.defaultRT, true);
  547. // TODO : desactivate sss for render targets
  548. // for (let j = 0; j < this.renderTargets.length; j++) {
  549. // this._setRenderTargetState(this.renderTargets[j], true);
  550. // }
  551. }
  552. }
  553. let postProcesses;
  554. for (let i = 0; i < this.renderTargets.length; i++) {
  555. if (this.renderTargets[i].renderTargetTexture) {
  556. if (this.renderTargets[i])
  557. postProcesses = this._getPostProcessesSource(this.renderTargets[i]);
  558. } else {
  559. const camera = this._scene.activeCamera;
  560. if (!camera) {
  561. continue;
  562. }
  563. postProcesses = camera._postProcesses;
  564. }
  565. if (!postProcesses) {
  566. continue;
  567. }
  568. postProcesses = (<Nullable<PostProcess[]>>postProcesses.filter((pp) => { return pp != null; }));
  569. if (postProcesses) {
  570. for (let j = 0; j < postProcesses.length; j++) {
  571. if (postProcesses[j].setPrePassRenderer(this)) {
  572. this._setRenderTargetState(this.renderTargets[i], true);
  573. enablePrePass = true;
  574. }
  575. }
  576. if (this._hasImageProcessing(postProcesses)) {
  577. this._scene.imageProcessingConfiguration.applyByPostProcess = true;
  578. }
  579. }
  580. }
  581. this._markAllMaterialsAsPrePassDirty();
  582. this._isDirty = false;
  583. if (enablePrePass) {
  584. this._enable();
  585. }
  586. }
  587. private _markAllMaterialsAsPrePassDirty() {
  588. const materials = this._scene.materials;
  589. for (let i = 0; i < materials.length; i++) {
  590. materials[i].markAsDirty(Material.PrePassDirtyFlag);
  591. }
  592. }
  593. /**
  594. * Disposes the prepass renderer.
  595. */
  596. public dispose() {
  597. for (let i = this.renderTargets.length - 1; i >= 0; i--) {
  598. this.renderTargets[i].dispose();
  599. }
  600. for (let i = 0; i < this._effectConfigurations.length; i++) {
  601. if (this._effectConfigurations[i].dispose) {
  602. this._effectConfigurations[i].dispose!();
  603. }
  604. }
  605. }
  606. }