prePassRenderer.ts 27 KB

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