renderTargetTexture.js 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074
  1. import { Observable } from "../../Misc/observable.js";
  2. import { Matrix, Vector3 } from "../../Maths/math.vector.js";
  3. import { Texture } from "../../Materials/Textures/texture.js";
  4. import { PostProcessManager } from "../../PostProcesses/postProcessManager.js";
  5. import { RenderingManager } from "../../Rendering/renderingManager.js";
  6. import "../../Engines/Extensions/engine.renderTarget.js";
  7. import "../../Engines/Extensions/engine.renderTargetCube.js";
  8. import { _ObserveArray } from "../../Misc/arrayTools.js";
  9. import { DumpTools } from "../../Misc/dumpTools.js";
  10. import { FloorPOT, NearestPOT } from "../../Misc/tools.functions.js";
  11. /**
  12. * This Helps creating a texture that will be created from a camera in your scene.
  13. * It is basically a dynamic texture that could be used to create special effects for instance.
  14. * Actually, It is the base of lot of effects in the framework like post process, shadows, effect layers and rendering pipelines...
  15. */
  16. export class RenderTargetTexture extends Texture {
  17. /**
  18. * Use this list to define the list of mesh you want to render.
  19. */
  20. get renderList() {
  21. return this._renderList;
  22. }
  23. set renderList(value) {
  24. if (this._unObserveRenderList) {
  25. this._unObserveRenderList();
  26. this._unObserveRenderList = null;
  27. }
  28. if (value) {
  29. this._unObserveRenderList = _ObserveArray(value, this._renderListHasChanged);
  30. }
  31. this._renderList = value;
  32. }
  33. /**
  34. * Post-processes for this render target
  35. */
  36. get postProcesses() {
  37. return this._postProcesses;
  38. }
  39. get _prePassEnabled() {
  40. return !!this._prePassRenderTarget && this._prePassRenderTarget.enabled;
  41. }
  42. /**
  43. * Set a after unbind callback in the texture.
  44. * This has been kept for backward compatibility and use of onAfterUnbindObservable is recommended.
  45. */
  46. set onAfterUnbind(callback) {
  47. if (this._onAfterUnbindObserver) {
  48. this.onAfterUnbindObservable.remove(this._onAfterUnbindObserver);
  49. }
  50. this._onAfterUnbindObserver = this.onAfterUnbindObservable.add(callback);
  51. }
  52. /**
  53. * Set a before render callback in the texture.
  54. * This has been kept for backward compatibility and use of onBeforeRenderObservable is recommended.
  55. */
  56. set onBeforeRender(callback) {
  57. if (this._onBeforeRenderObserver) {
  58. this.onBeforeRenderObservable.remove(this._onBeforeRenderObserver);
  59. }
  60. this._onBeforeRenderObserver = this.onBeforeRenderObservable.add(callback);
  61. }
  62. /**
  63. * Set a after render callback in the texture.
  64. * This has been kept for backward compatibility and use of onAfterRenderObservable is recommended.
  65. */
  66. set onAfterRender(callback) {
  67. if (this._onAfterRenderObserver) {
  68. this.onAfterRenderObservable.remove(this._onAfterRenderObserver);
  69. }
  70. this._onAfterRenderObserver = this.onAfterRenderObservable.add(callback);
  71. }
  72. /**
  73. * Set a clear callback in the texture.
  74. * This has been kept for backward compatibility and use of onClearObservable is recommended.
  75. */
  76. set onClear(callback) {
  77. if (this._onClearObserver) {
  78. this.onClearObservable.remove(this._onClearObserver);
  79. }
  80. this._onClearObserver = this.onClearObservable.add(callback);
  81. }
  82. /**
  83. * Gets the render pass ids used by the render target texture. For a single render target the array length will be 1, for a cube texture it will be 6 and for
  84. * a 2D texture array it will return an array of ids the size of the 2D texture array
  85. */
  86. get renderPassIds() {
  87. return this._renderPassIds;
  88. }
  89. /**
  90. * Gets the current value of the refreshId counter
  91. */
  92. get currentRefreshId() {
  93. return this._currentRefreshId;
  94. }
  95. /**
  96. * Sets a specific material to be used to render a mesh/a list of meshes in this render target texture
  97. * @param mesh mesh or array of meshes
  98. * @param material material or array of materials to use for this render pass. If undefined is passed, no specific material will be used but the regular material instead (mesh.material). It's possible to provide an array of materials to use a different material for each rendering in the case of a cube texture (6 rendering) and a 2D texture array (as many rendering as the length of the array)
  99. */
  100. setMaterialForRendering(mesh, material) {
  101. let meshes;
  102. if (!Array.isArray(mesh)) {
  103. meshes = [mesh];
  104. }
  105. else {
  106. meshes = mesh;
  107. }
  108. for (let j = 0; j < meshes.length; ++j) {
  109. for (let i = 0; i < this._renderPassIds.length; ++i) {
  110. meshes[j].setMaterialForRenderPass(this._renderPassIds[i], material !== undefined ? (Array.isArray(material) ? material[i] : material) : undefined);
  111. }
  112. }
  113. }
  114. /**
  115. * Define if the texture has multiple draw buffers or if false a single draw buffer.
  116. */
  117. get isMulti() {
  118. return this._renderTarget?.isMulti ?? false;
  119. }
  120. /**
  121. * Gets render target creation options that were used.
  122. */
  123. get renderTargetOptions() {
  124. return this._renderTargetOptions;
  125. }
  126. /**
  127. * Gets the render target wrapper associated with this render target
  128. */
  129. get renderTarget() {
  130. return this._renderTarget;
  131. }
  132. _onRatioRescale() {
  133. if (this._sizeRatio) {
  134. this.resize(this._initialSizeParameter);
  135. }
  136. }
  137. /**
  138. * Gets or sets the size of the bounding box associated with the texture (when in cube mode)
  139. * When defined, the cubemap will switch to local mode
  140. * @see https://community.arm.com/graphics/b/blog/posts/reflections-based-on-local-cubemaps-in-unity
  141. * @example https://www.babylonjs-playground.com/#RNASML
  142. */
  143. set boundingBoxSize(value) {
  144. if (this._boundingBoxSize && this._boundingBoxSize.equals(value)) {
  145. return;
  146. }
  147. this._boundingBoxSize = value;
  148. const scene = this.getScene();
  149. if (scene) {
  150. scene.markAllMaterialsAsDirty(1);
  151. }
  152. }
  153. get boundingBoxSize() {
  154. return this._boundingBoxSize;
  155. }
  156. /**
  157. * In case the RTT has been created with a depth texture, get the associated
  158. * depth texture.
  159. * Otherwise, return null.
  160. */
  161. get depthStencilTexture() {
  162. return this._renderTarget?._depthStencilTexture ?? null;
  163. }
  164. /** @internal */
  165. constructor(name, size, scene, generateMipMaps = false, doNotChangeAspectRatio = true, type = 0, isCube = false, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, generateDepthBuffer = true, generateStencilBuffer = false, isMulti = false, format = 5, delayAllocation = false, samples, creationFlags, noColorAttachment = false, useSRGBBuffer = false) {
  166. let colorAttachment = undefined;
  167. let gammaSpace = true;
  168. if (typeof generateMipMaps === "object") {
  169. const options = generateMipMaps;
  170. generateMipMaps = !!options.generateMipMaps;
  171. doNotChangeAspectRatio = options.doNotChangeAspectRatio ?? true;
  172. type = options.type ?? 0;
  173. isCube = !!options.isCube;
  174. samplingMode = options.samplingMode ?? Texture.TRILINEAR_SAMPLINGMODE;
  175. generateDepthBuffer = options.generateDepthBuffer ?? true;
  176. generateStencilBuffer = !!options.generateStencilBuffer;
  177. isMulti = !!options.isMulti;
  178. format = options.format ?? 5;
  179. delayAllocation = !!options.delayAllocation;
  180. samples = options.samples;
  181. creationFlags = options.creationFlags;
  182. noColorAttachment = !!options.noColorAttachment;
  183. useSRGBBuffer = !!options.useSRGBBuffer;
  184. colorAttachment = options.colorAttachment;
  185. gammaSpace = options.gammaSpace ?? gammaSpace;
  186. }
  187. super(null, scene, !generateMipMaps, undefined, samplingMode, undefined, undefined, undefined, undefined, format);
  188. this._unObserveRenderList = null;
  189. this._renderListHasChanged = (_functionName, previousLength) => {
  190. const newLength = this._renderList ? this._renderList.length : 0;
  191. if ((previousLength === 0 && newLength > 0) || newLength === 0) {
  192. this.getScene()?.meshes.forEach((mesh) => {
  193. mesh._markSubMeshesAsLightDirty();
  194. });
  195. }
  196. };
  197. /**
  198. * Define if particles should be rendered in your texture.
  199. */
  200. this.renderParticles = true;
  201. /**
  202. * Define if sprites should be rendered in your texture.
  203. */
  204. this.renderSprites = false;
  205. /**
  206. * Force checking the layerMask property even if a custom list of meshes is provided (ie. if renderList is not undefined)
  207. */
  208. this.forceLayerMaskCheck = false;
  209. /**
  210. * Define if the camera viewport should be respected while rendering the texture or if the render should be done to the entire texture.
  211. */
  212. this.ignoreCameraViewport = false;
  213. /**
  214. * An event triggered when the texture is unbind.
  215. */
  216. this.onBeforeBindObservable = new Observable();
  217. /**
  218. * An event triggered when the texture is unbind.
  219. */
  220. this.onAfterUnbindObservable = new Observable();
  221. /**
  222. * An event triggered before rendering the texture
  223. */
  224. this.onBeforeRenderObservable = new Observable();
  225. /**
  226. * An event triggered after rendering the texture
  227. */
  228. this.onAfterRenderObservable = new Observable();
  229. /**
  230. * An event triggered after the texture clear
  231. */
  232. this.onClearObservable = new Observable();
  233. /**
  234. * An event triggered when the texture is resized.
  235. */
  236. this.onResizeObservable = new Observable();
  237. /** @internal */
  238. this._cleared = false;
  239. /**
  240. * Skip the initial clear of the rtt at the beginning of the frame render loop
  241. */
  242. this.skipInitialClear = false;
  243. this._currentRefreshId = -1;
  244. this._refreshRate = 1;
  245. this._samples = 1;
  246. this._canRescale = true;
  247. this._renderTarget = null;
  248. /**
  249. * Gets or sets the center of the bounding box associated with the texture (when in cube mode)
  250. * It must define where the camera used to render the texture is set
  251. */
  252. this.boundingBoxPosition = Vector3.Zero();
  253. scene = this.getScene();
  254. if (!scene) {
  255. return;
  256. }
  257. const engine = this.getScene().getEngine();
  258. this._gammaSpace = gammaSpace;
  259. this._coordinatesMode = Texture.PROJECTION_MODE;
  260. this.renderList = [];
  261. this.name = name;
  262. this.isRenderTarget = true;
  263. this._initialSizeParameter = size;
  264. this._renderPassIds = [];
  265. this._isCubeData = isCube;
  266. this._processSizeParameter(size);
  267. this.renderPassId = this._renderPassIds[0];
  268. this._resizeObserver = engine.onResizeObservable.add(() => { });
  269. this._generateMipMaps = generateMipMaps ? true : false;
  270. this._doNotChangeAspectRatio = doNotChangeAspectRatio;
  271. // Rendering groups
  272. this._renderingManager = new RenderingManager(scene);
  273. this._renderingManager._useSceneAutoClearSetup = true;
  274. if (isMulti) {
  275. return;
  276. }
  277. this._renderTargetOptions = {
  278. generateMipMaps: generateMipMaps,
  279. type: type,
  280. format: this._format ?? undefined,
  281. samplingMode: this.samplingMode,
  282. generateDepthBuffer: generateDepthBuffer,
  283. generateStencilBuffer: generateStencilBuffer,
  284. samples,
  285. creationFlags,
  286. noColorAttachment: noColorAttachment,
  287. useSRGBBuffer,
  288. colorAttachment: colorAttachment,
  289. label: this.name,
  290. };
  291. if (this.samplingMode === Texture.NEAREST_SAMPLINGMODE) {
  292. this.wrapU = Texture.CLAMP_ADDRESSMODE;
  293. this.wrapV = Texture.CLAMP_ADDRESSMODE;
  294. }
  295. if (!delayAllocation) {
  296. if (isCube) {
  297. this._renderTarget = scene.getEngine().createRenderTargetCubeTexture(this.getRenderSize(), this._renderTargetOptions);
  298. this.coordinatesMode = Texture.INVCUBIC_MODE;
  299. this._textureMatrix = Matrix.Identity();
  300. }
  301. else {
  302. this._renderTarget = scene.getEngine().createRenderTargetTexture(this._size, this._renderTargetOptions);
  303. }
  304. this._texture = this._renderTarget.texture;
  305. if (samples !== undefined) {
  306. this.samples = samples;
  307. }
  308. }
  309. }
  310. /**
  311. * Creates a depth stencil texture.
  312. * This is only available in WebGL 2 or with the depth texture extension available.
  313. * @param comparisonFunction Specifies the comparison function to set on the texture. If 0 or undefined, the texture is not in comparison mode (default: 0)
  314. * @param bilinearFiltering Specifies whether or not bilinear filtering is enable on the texture (default: true)
  315. * @param generateStencil Specifies whether or not a stencil should be allocated in the texture (default: false)
  316. * @param samples sample count of the depth/stencil texture (default: 1)
  317. * @param format format of the depth texture (default: 14)
  318. * @param label defines the label of the texture (for debugging purpose)
  319. */
  320. createDepthStencilTexture(comparisonFunction = 0, bilinearFiltering = true, generateStencil = false, samples = 1, format = 14, label) {
  321. this._renderTarget?.createDepthStencilTexture(comparisonFunction, bilinearFiltering, generateStencil, samples, format, label);
  322. }
  323. _releaseRenderPassId() {
  324. if (this._scene) {
  325. const engine = this._scene.getEngine();
  326. for (let i = 0; i < this._renderPassIds.length; ++i) {
  327. engine.releaseRenderPassId(this._renderPassIds[i]);
  328. }
  329. }
  330. this._renderPassIds = [];
  331. }
  332. _createRenderPassId() {
  333. this._releaseRenderPassId();
  334. const engine = this._scene.getEngine(); // scene can't be null in a RenderTargetTexture, see constructor
  335. const numPasses = this._isCubeData ? 6 : this.getRenderLayers() || 1;
  336. for (let i = 0; i < numPasses; ++i) {
  337. this._renderPassIds[i] = engine.createRenderPassId(`RenderTargetTexture - ${this.name}#${i}`);
  338. }
  339. }
  340. _processSizeParameter(size, createRenderPassIds = true) {
  341. if (size.ratio) {
  342. this._sizeRatio = size.ratio;
  343. const engine = this._getEngine();
  344. this._size = {
  345. width: this._bestReflectionRenderTargetDimension(engine.getRenderWidth(), this._sizeRatio),
  346. height: this._bestReflectionRenderTargetDimension(engine.getRenderHeight(), this._sizeRatio),
  347. };
  348. }
  349. else {
  350. this._size = size;
  351. }
  352. if (createRenderPassIds) {
  353. this._createRenderPassId();
  354. }
  355. }
  356. /**
  357. * Define the number of samples to use in case of MSAA.
  358. * It defaults to one meaning no MSAA has been enabled.
  359. */
  360. get samples() {
  361. return this._renderTarget?.samples ?? this._samples;
  362. }
  363. set samples(value) {
  364. if (this._renderTarget) {
  365. this._samples = this._renderTarget.setSamples(value);
  366. }
  367. }
  368. /**
  369. * Resets the refresh counter of the texture and start bak from scratch.
  370. * Could be useful to regenerate the texture if it is setup to render only once.
  371. */
  372. resetRefreshCounter() {
  373. this._currentRefreshId = -1;
  374. }
  375. /**
  376. * Define the refresh rate of the texture or the rendering frequency.
  377. * Use 0 to render just once, 1 to render on every frame, 2 to render every two frames and so on...
  378. */
  379. get refreshRate() {
  380. return this._refreshRate;
  381. }
  382. set refreshRate(value) {
  383. this._refreshRate = value;
  384. this.resetRefreshCounter();
  385. }
  386. /**
  387. * Adds a post process to the render target rendering passes.
  388. * @param postProcess define the post process to add
  389. */
  390. addPostProcess(postProcess) {
  391. if (!this._postProcessManager) {
  392. const scene = this.getScene();
  393. if (!scene) {
  394. return;
  395. }
  396. this._postProcessManager = new PostProcessManager(scene);
  397. this._postProcesses = new Array();
  398. }
  399. this._postProcesses.push(postProcess);
  400. this._postProcesses[0].autoClear = false;
  401. }
  402. /**
  403. * Clear all the post processes attached to the render target
  404. * @param dispose define if the cleared post processes should also be disposed (false by default)
  405. */
  406. clearPostProcesses(dispose = false) {
  407. if (!this._postProcesses) {
  408. return;
  409. }
  410. if (dispose) {
  411. for (const postProcess of this._postProcesses) {
  412. postProcess.dispose();
  413. }
  414. }
  415. this._postProcesses = [];
  416. }
  417. /**
  418. * Remove one of the post process from the list of attached post processes to the texture
  419. * @param postProcess define the post process to remove from the list
  420. */
  421. removePostProcess(postProcess) {
  422. if (!this._postProcesses) {
  423. return;
  424. }
  425. const index = this._postProcesses.indexOf(postProcess);
  426. if (index === -1) {
  427. return;
  428. }
  429. this._postProcesses.splice(index, 1);
  430. if (this._postProcesses.length > 0) {
  431. this._postProcesses[0].autoClear = false;
  432. }
  433. }
  434. /** @internal */
  435. _shouldRender() {
  436. if (this._currentRefreshId === -1) {
  437. // At least render once
  438. this._currentRefreshId = 1;
  439. return true;
  440. }
  441. if (this.refreshRate === this._currentRefreshId) {
  442. this._currentRefreshId = 1;
  443. return true;
  444. }
  445. this._currentRefreshId++;
  446. return false;
  447. }
  448. /**
  449. * Gets the actual render size of the texture.
  450. * @returns the width of the render size
  451. */
  452. getRenderSize() {
  453. return this.getRenderWidth();
  454. }
  455. /**
  456. * Gets the actual render width of the texture.
  457. * @returns the width of the render size
  458. */
  459. getRenderWidth() {
  460. if (this._size.width) {
  461. return this._size.width;
  462. }
  463. return this._size;
  464. }
  465. /**
  466. * Gets the actual render height of the texture.
  467. * @returns the height of the render size
  468. */
  469. getRenderHeight() {
  470. if (this._size.width) {
  471. return this._size.height;
  472. }
  473. return this._size;
  474. }
  475. /**
  476. * Gets the actual number of layers of the texture or, in the case of a 3D texture, return the depth.
  477. * @returns the number of layers
  478. */
  479. getRenderLayers() {
  480. const layers = this._size.layers;
  481. if (layers) {
  482. return layers;
  483. }
  484. const depth = this._size.depth;
  485. if (depth) {
  486. return depth;
  487. }
  488. return 0;
  489. }
  490. /**
  491. * Don't allow this render target texture to rescale. Mainly used to prevent rescaling by the scene optimizer.
  492. */
  493. disableRescaling() {
  494. this._canRescale = false;
  495. }
  496. /**
  497. * Get if the texture can be rescaled or not.
  498. */
  499. get canRescale() {
  500. return this._canRescale;
  501. }
  502. /**
  503. * Resize the texture using a ratio.
  504. * @param ratio the ratio to apply to the texture size in order to compute the new target size
  505. */
  506. scale(ratio) {
  507. const newSize = Math.max(1, this.getRenderSize() * ratio);
  508. this.resize(newSize);
  509. }
  510. /**
  511. * Get the texture reflection matrix used to rotate/transform the reflection.
  512. * @returns the reflection matrix
  513. */
  514. getReflectionTextureMatrix() {
  515. if (this.isCube) {
  516. return this._textureMatrix;
  517. }
  518. return super.getReflectionTextureMatrix();
  519. }
  520. /**
  521. * Resize the texture to a new desired size.
  522. * Be careful as it will recreate all the data in the new texture.
  523. * @param size Define the new size. It can be:
  524. * - a number for squared texture,
  525. * - an object containing { width: number, height: number }
  526. * - or an object containing a ratio { ratio: number }
  527. */
  528. resize(size) {
  529. const wasCube = this.isCube;
  530. this._renderTarget?.dispose();
  531. this._renderTarget = null;
  532. const scene = this.getScene();
  533. if (!scene) {
  534. return;
  535. }
  536. this._processSizeParameter(size, false);
  537. if (wasCube) {
  538. this._renderTarget = scene.getEngine().createRenderTargetCubeTexture(this.getRenderSize(), this._renderTargetOptions);
  539. }
  540. else {
  541. this._renderTarget = scene.getEngine().createRenderTargetTexture(this._size, this._renderTargetOptions);
  542. }
  543. this._texture = this._renderTarget.texture;
  544. if (this._renderTargetOptions.samples !== undefined) {
  545. this.samples = this._renderTargetOptions.samples;
  546. }
  547. if (this.onResizeObservable.hasObservers()) {
  548. this.onResizeObservable.notifyObservers(this);
  549. }
  550. }
  551. /**
  552. * Renders all the objects from the render list into the texture.
  553. * @param useCameraPostProcess Define if camera post processes should be used during the rendering
  554. * @param dumpForDebug Define if the rendering result should be dumped (copied) for debugging purpose
  555. */
  556. render(useCameraPostProcess = false, dumpForDebug = false) {
  557. this._render(useCameraPostProcess, dumpForDebug);
  558. }
  559. /**
  560. * This function will check if the render target texture can be rendered (textures are loaded, shaders are compiled)
  561. * @returns true if all required resources are ready
  562. */
  563. isReadyForRendering() {
  564. return this._render(false, false, true);
  565. }
  566. _render(useCameraPostProcess = false, dumpForDebug = false, checkReadiness = false) {
  567. const scene = this.getScene();
  568. if (!scene) {
  569. return checkReadiness;
  570. }
  571. const engine = scene.getEngine();
  572. if (this.useCameraPostProcesses !== undefined) {
  573. useCameraPostProcess = this.useCameraPostProcesses;
  574. }
  575. if (this._waitingRenderList) {
  576. if (!this.renderListPredicate) {
  577. this.renderList = [];
  578. for (let index = 0; index < this._waitingRenderList.length; index++) {
  579. const id = this._waitingRenderList[index];
  580. const mesh = scene.getMeshById(id);
  581. if (mesh) {
  582. this.renderList.push(mesh);
  583. }
  584. }
  585. }
  586. this._waitingRenderList = undefined;
  587. }
  588. // Is predicate defined?
  589. if (this.renderListPredicate) {
  590. if (this.renderList) {
  591. this.renderList.length = 0; // Clear previous renderList
  592. }
  593. else {
  594. this.renderList = [];
  595. }
  596. const scene = this.getScene();
  597. if (!scene) {
  598. return checkReadiness;
  599. }
  600. const sceneMeshes = scene.meshes;
  601. for (let index = 0; index < sceneMeshes.length; index++) {
  602. const mesh = sceneMeshes[index];
  603. if (this.renderListPredicate(mesh)) {
  604. this.renderList.push(mesh);
  605. }
  606. }
  607. }
  608. const currentRenderPassId = engine.currentRenderPassId;
  609. this.onBeforeBindObservable.notifyObservers(this);
  610. // Set custom projection.
  611. // Needs to be before binding to prevent changing the aspect ratio.
  612. const camera = this.activeCamera ?? scene.activeCamera;
  613. const sceneCamera = scene.activeCamera;
  614. if (camera) {
  615. if (camera !== scene.activeCamera) {
  616. scene.setTransformMatrix(camera.getViewMatrix(), camera.getProjectionMatrix(true));
  617. scene.activeCamera = camera;
  618. }
  619. engine.setViewport(camera.rigParent ? camera.rigParent.viewport : camera.viewport, this.getRenderWidth(), this.getRenderHeight());
  620. }
  621. this._defaultRenderListPrepared = false;
  622. let returnValue = checkReadiness;
  623. if (!checkReadiness) {
  624. if ((this.is2DArray || this.is3D) && !this.isMulti) {
  625. for (let layer = 0; layer < this.getRenderLayers(); layer++) {
  626. this._renderToTarget(0, useCameraPostProcess, dumpForDebug, layer, camera);
  627. scene.incrementRenderId();
  628. scene.resetCachedMaterial();
  629. }
  630. }
  631. else if (this.isCube && !this.isMulti) {
  632. for (let face = 0; face < 6; face++) {
  633. this._renderToTarget(face, useCameraPostProcess, dumpForDebug, undefined, camera);
  634. scene.incrementRenderId();
  635. scene.resetCachedMaterial();
  636. }
  637. }
  638. else {
  639. this._renderToTarget(0, useCameraPostProcess, dumpForDebug, undefined, camera);
  640. }
  641. }
  642. else {
  643. if (!scene.getViewMatrix()) {
  644. // We probably didn't execute scene.render() yet, so make sure we have a view/projection matrix setup for the scene
  645. scene.updateTransformMatrix();
  646. }
  647. const numLayers = this.is2DArray || this.is3D ? this.getRenderLayers() : this.isCube ? 6 : 1;
  648. for (let layer = 0; layer < numLayers && returnValue; layer++) {
  649. let currentRenderList = null;
  650. const defaultRenderList = this.renderList ? this.renderList : scene.getActiveMeshes().data;
  651. const defaultRenderListLength = this.renderList ? this.renderList.length : scene.getActiveMeshes().length;
  652. engine.currentRenderPassId = this._renderPassIds[layer];
  653. this.onBeforeRenderObservable.notifyObservers(layer);
  654. if (this.getCustomRenderList) {
  655. currentRenderList = this.getCustomRenderList(layer, defaultRenderList, defaultRenderListLength);
  656. }
  657. if (!currentRenderList) {
  658. currentRenderList = defaultRenderList;
  659. }
  660. if (!this._doNotChangeAspectRatio) {
  661. scene.updateTransformMatrix(true);
  662. }
  663. for (let i = 0; i < currentRenderList.length && returnValue; ++i) {
  664. const mesh = currentRenderList[i];
  665. if (!mesh.isEnabled() || mesh.isBlocked || !mesh.isVisible || !mesh.subMeshes) {
  666. continue;
  667. }
  668. if (this.customIsReadyFunction) {
  669. if (!this.customIsReadyFunction(mesh, this.refreshRate, checkReadiness)) {
  670. returnValue = false;
  671. continue;
  672. }
  673. }
  674. else if (!mesh.isReady(true)) {
  675. returnValue = false;
  676. continue;
  677. }
  678. }
  679. this.onAfterRenderObservable.notifyObservers(layer);
  680. if (this.is2DArray || this.is3D || this.isCube) {
  681. scene.incrementRenderId();
  682. scene.resetCachedMaterial();
  683. }
  684. }
  685. }
  686. this.onAfterUnbindObservable.notifyObservers(this);
  687. engine.currentRenderPassId = currentRenderPassId;
  688. if (sceneCamera) {
  689. scene.activeCamera = sceneCamera;
  690. if (this.activeCamera && this.activeCamera !== scene.activeCamera) {
  691. scene.setTransformMatrix(scene.activeCamera.getViewMatrix(), scene.activeCamera.getProjectionMatrix(true));
  692. }
  693. engine.setViewport(scene.activeCamera.viewport);
  694. }
  695. scene.resetCachedMaterial();
  696. return returnValue;
  697. }
  698. _bestReflectionRenderTargetDimension(renderDimension, scale) {
  699. const minimum = 128;
  700. const x = renderDimension * scale;
  701. const curved = NearestPOT(x + (minimum * minimum) / (minimum + x));
  702. // Ensure we don't exceed the render dimension (while staying POT)
  703. return Math.min(FloorPOT(renderDimension), curved);
  704. }
  705. _prepareRenderingManager(currentRenderList, currentRenderListLength, camera, checkLayerMask) {
  706. const scene = this.getScene();
  707. if (!scene) {
  708. return;
  709. }
  710. this._renderingManager.reset();
  711. const sceneRenderId = scene.getRenderId();
  712. for (let meshIndex = 0; meshIndex < currentRenderListLength; meshIndex++) {
  713. const mesh = currentRenderList[meshIndex];
  714. if (mesh && !mesh.isBlocked) {
  715. if (this.customIsReadyFunction) {
  716. if (!this.customIsReadyFunction(mesh, this.refreshRate, false)) {
  717. this.resetRefreshCounter();
  718. continue;
  719. }
  720. }
  721. else if (!mesh.isReady(this.refreshRate === 0)) {
  722. this.resetRefreshCounter();
  723. continue;
  724. }
  725. if (!mesh._internalAbstractMeshDataInfo._currentLODIsUpToDate && scene.activeCamera) {
  726. mesh._internalAbstractMeshDataInfo._currentLOD = scene.customLODSelector
  727. ? scene.customLODSelector(mesh, this.activeCamera || scene.activeCamera)
  728. : mesh.getLOD(this.activeCamera || scene.activeCamera);
  729. mesh._internalAbstractMeshDataInfo._currentLODIsUpToDate = true;
  730. }
  731. if (!mesh._internalAbstractMeshDataInfo._currentLOD) {
  732. continue;
  733. }
  734. let meshToRender = mesh._internalAbstractMeshDataInfo._currentLOD;
  735. meshToRender._preActivateForIntermediateRendering(sceneRenderId);
  736. let isMasked;
  737. if (checkLayerMask && camera) {
  738. isMasked = (mesh.layerMask & camera.layerMask) === 0;
  739. }
  740. else {
  741. isMasked = false;
  742. }
  743. if (mesh.isEnabled() && mesh.isVisible && mesh.subMeshes && !isMasked) {
  744. if (meshToRender !== mesh) {
  745. meshToRender._activate(sceneRenderId, true);
  746. }
  747. if (mesh._activate(sceneRenderId, true) && mesh.subMeshes.length) {
  748. if (!mesh.isAnInstance) {
  749. meshToRender._internalAbstractMeshDataInfo._onlyForInstancesIntermediate = false;
  750. }
  751. else {
  752. if (mesh._internalAbstractMeshDataInfo._actAsRegularMesh) {
  753. meshToRender = mesh;
  754. }
  755. }
  756. meshToRender._internalAbstractMeshDataInfo._isActiveIntermediate = true;
  757. for (let subIndex = 0; subIndex < meshToRender.subMeshes.length; subIndex++) {
  758. const subMesh = meshToRender.subMeshes[subIndex];
  759. this._renderingManager.dispatch(subMesh, meshToRender);
  760. }
  761. }
  762. }
  763. }
  764. }
  765. for (let particleIndex = 0; particleIndex < scene.particleSystems.length; particleIndex++) {
  766. const particleSystem = scene.particleSystems[particleIndex];
  767. const emitter = particleSystem.emitter;
  768. if (!particleSystem.isStarted() || !emitter || (emitter.position && !emitter.isEnabled())) {
  769. continue;
  770. }
  771. this._renderingManager.dispatchParticles(particleSystem);
  772. }
  773. }
  774. /**
  775. * @internal
  776. * @param faceIndex face index to bind to if this is a cubetexture
  777. * @param layer defines the index of the texture to bind in the array
  778. */
  779. _bindFrameBuffer(faceIndex = 0, layer = 0) {
  780. const scene = this.getScene();
  781. if (!scene) {
  782. return;
  783. }
  784. const engine = scene.getEngine();
  785. if (this._renderTarget) {
  786. engine.bindFramebuffer(this._renderTarget, this.isCube ? faceIndex : undefined, undefined, undefined, this.ignoreCameraViewport, 0, layer);
  787. }
  788. }
  789. _unbindFrameBuffer(engine, faceIndex) {
  790. if (!this._renderTarget) {
  791. return;
  792. }
  793. engine.unBindFramebuffer(this._renderTarget, this.isCube, () => {
  794. this.onAfterRenderObservable.notifyObservers(faceIndex);
  795. });
  796. }
  797. /**
  798. * @internal
  799. */
  800. _prepareFrame(scene, faceIndex, layer, useCameraPostProcess) {
  801. if (this._postProcessManager) {
  802. if (!this._prePassEnabled) {
  803. this._postProcessManager._prepareFrame(this._texture, this._postProcesses);
  804. }
  805. }
  806. else if (!useCameraPostProcess || !scene.postProcessManager._prepareFrame(this._texture)) {
  807. this._bindFrameBuffer(faceIndex, layer);
  808. }
  809. }
  810. _renderToTarget(faceIndex, useCameraPostProcess, dumpForDebug, layer = 0, camera = null) {
  811. const scene = this.getScene();
  812. if (!scene) {
  813. return;
  814. }
  815. const engine = scene.getEngine();
  816. engine._debugPushGroup?.(`render to face #${faceIndex} layer #${layer}`, 1);
  817. // Bind
  818. this._prepareFrame(scene, faceIndex, layer, useCameraPostProcess);
  819. if (this.is2DArray || this.is3D) {
  820. engine.currentRenderPassId = this._renderPassIds[layer];
  821. this.onBeforeRenderObservable.notifyObservers(layer);
  822. }
  823. else {
  824. engine.currentRenderPassId = this._renderPassIds[faceIndex];
  825. this.onBeforeRenderObservable.notifyObservers(faceIndex);
  826. }
  827. const fastPath = engine.snapshotRendering && engine.snapshotRenderingMode === 1;
  828. if (!fastPath) {
  829. // Get the list of meshes to render
  830. let currentRenderList = null;
  831. const defaultRenderList = this.renderList ? this.renderList : scene.getActiveMeshes().data;
  832. const defaultRenderListLength = this.renderList ? this.renderList.length : scene.getActiveMeshes().length;
  833. if (this.getCustomRenderList) {
  834. currentRenderList = this.getCustomRenderList(this.is2DArray || this.is3D ? layer : faceIndex, defaultRenderList, defaultRenderListLength);
  835. }
  836. if (!currentRenderList) {
  837. // No custom render list provided, we prepare the rendering for the default list, but check
  838. // first if we did not already performed the preparation before so as to avoid re-doing it several times
  839. if (!this._defaultRenderListPrepared) {
  840. this._prepareRenderingManager(defaultRenderList, defaultRenderListLength, camera, !this.renderList || this.forceLayerMaskCheck);
  841. this._defaultRenderListPrepared = true;
  842. }
  843. currentRenderList = defaultRenderList;
  844. }
  845. else {
  846. // Prepare the rendering for the custom render list provided
  847. this._prepareRenderingManager(currentRenderList, currentRenderList.length, camera, this.forceLayerMaskCheck);
  848. }
  849. // Before clear
  850. for (const step of scene._beforeRenderTargetClearStage) {
  851. step.action(this, faceIndex, layer);
  852. }
  853. // Clear
  854. if (this.onClearObservable.hasObservers()) {
  855. this.onClearObservable.notifyObservers(engine);
  856. }
  857. else if (!this.skipInitialClear) {
  858. engine.clear(this.clearColor || scene.clearColor, true, true, true);
  859. }
  860. if (!this._doNotChangeAspectRatio) {
  861. scene.updateTransformMatrix(true);
  862. }
  863. // Before Camera Draw
  864. for (const step of scene._beforeRenderTargetDrawStage) {
  865. step.action(this, faceIndex, layer);
  866. }
  867. // Render
  868. this._renderingManager.render(this.customRenderFunction, currentRenderList, this.renderParticles, this.renderSprites);
  869. // After Camera Draw
  870. for (const step of scene._afterRenderTargetDrawStage) {
  871. step.action(this, faceIndex, layer);
  872. }
  873. const saveGenerateMipMaps = this._texture?.generateMipMaps ?? false;
  874. if (this._texture) {
  875. this._texture.generateMipMaps = false; // if left true, the mipmaps will be generated (if this._texture.generateMipMaps = true) when the first post process binds its own RTT: by doing so it will unbind the current RTT,
  876. // which will trigger a mipmap generation. We don't want this because it's a wasted work, we will do an unbind of the current RTT at the end of the process (see unbindFrameBuffer) which will
  877. // trigger the generation of the final mipmaps
  878. }
  879. if (this._postProcessManager) {
  880. this._postProcessManager._finalizeFrame(false, this._renderTarget ?? undefined, faceIndex, this._postProcesses, this.ignoreCameraViewport);
  881. }
  882. else if (useCameraPostProcess) {
  883. scene.postProcessManager._finalizeFrame(false, this._renderTarget ?? undefined, faceIndex);
  884. }
  885. for (const step of scene._afterRenderTargetPostProcessStage) {
  886. step.action(this, faceIndex, layer);
  887. }
  888. if (this._texture) {
  889. this._texture.generateMipMaps = saveGenerateMipMaps;
  890. }
  891. if (!this._doNotChangeAspectRatio) {
  892. scene.updateTransformMatrix(true);
  893. }
  894. // Dump ?
  895. if (dumpForDebug) {
  896. DumpTools.DumpFramebuffer(this.getRenderWidth(), this.getRenderHeight(), engine);
  897. }
  898. }
  899. else {
  900. // Clear
  901. if (this.onClearObservable.hasObservers()) {
  902. this.onClearObservable.notifyObservers(engine);
  903. }
  904. else {
  905. if (!this.skipInitialClear) {
  906. engine.clear(this.clearColor || scene.clearColor, true, true, true);
  907. }
  908. }
  909. }
  910. // Unbind
  911. this._unbindFrameBuffer(engine, faceIndex);
  912. if (this._texture && this.isCube && faceIndex === 5) {
  913. engine.generateMipMapsForCubemap(this._texture, true);
  914. }
  915. engine._debugPopGroup?.(1);
  916. }
  917. /**
  918. * Overrides the default sort function applied in the rendering group to prepare the meshes.
  919. * This allowed control for front to back rendering or reversely depending of the special needs.
  920. *
  921. * @param renderingGroupId The rendering group id corresponding to its index
  922. * @param opaqueSortCompareFn The opaque queue comparison function use to sort.
  923. * @param alphaTestSortCompareFn The alpha test queue comparison function use to sort.
  924. * @param transparentSortCompareFn The transparent queue comparison function use to sort.
  925. */
  926. setRenderingOrder(renderingGroupId, opaqueSortCompareFn = null, alphaTestSortCompareFn = null, transparentSortCompareFn = null) {
  927. this._renderingManager.setRenderingOrder(renderingGroupId, opaqueSortCompareFn, alphaTestSortCompareFn, transparentSortCompareFn);
  928. }
  929. /**
  930. * Specifies whether or not the stencil and depth buffer are cleared between two rendering groups.
  931. *
  932. * @param renderingGroupId The rendering group id corresponding to its index
  933. * @param autoClearDepthStencil Automatically clears depth and stencil between groups if true.
  934. */
  935. setRenderingAutoClearDepthStencil(renderingGroupId, autoClearDepthStencil) {
  936. this._renderingManager.setRenderingAutoClearDepthStencil(renderingGroupId, autoClearDepthStencil);
  937. this._renderingManager._useSceneAutoClearSetup = false;
  938. }
  939. /**
  940. * Clones the texture.
  941. * @returns the cloned texture
  942. */
  943. clone() {
  944. const textureSize = this.getSize();
  945. const newTexture = new RenderTargetTexture(this.name, textureSize, this.getScene(), this._renderTargetOptions.generateMipMaps, this._doNotChangeAspectRatio, this._renderTargetOptions.type, this.isCube, this._renderTargetOptions.samplingMode, this._renderTargetOptions.generateDepthBuffer, this._renderTargetOptions.generateStencilBuffer, undefined, this._renderTargetOptions.format, undefined, this._renderTargetOptions.samples);
  946. // Base texture
  947. newTexture.hasAlpha = this.hasAlpha;
  948. newTexture.level = this.level;
  949. // RenderTarget Texture
  950. newTexture.coordinatesMode = this.coordinatesMode;
  951. if (this.renderList) {
  952. newTexture.renderList = this.renderList.slice(0);
  953. }
  954. return newTexture;
  955. }
  956. /**
  957. * Serialize the texture to a JSON representation we can easily use in the respective Parse function.
  958. * @returns The JSON representation of the texture
  959. */
  960. serialize() {
  961. if (!this.name) {
  962. return null;
  963. }
  964. const serializationObject = super.serialize();
  965. serializationObject.renderTargetSize = this.getRenderSize();
  966. serializationObject.renderList = [];
  967. if (this.renderList) {
  968. for (let index = 0; index < this.renderList.length; index++) {
  969. serializationObject.renderList.push(this.renderList[index].id);
  970. }
  971. }
  972. return serializationObject;
  973. }
  974. /**
  975. * This will remove the attached framebuffer objects. The texture will not be able to be used as render target anymore
  976. */
  977. disposeFramebufferObjects() {
  978. this._renderTarget?.dispose(true);
  979. }
  980. /**
  981. * Release and destroy the underlying lower level texture aka internalTexture.
  982. */
  983. releaseInternalTexture() {
  984. this._renderTarget?.releaseTextures();
  985. this._texture = null;
  986. }
  987. /**
  988. * Dispose the texture and release its associated resources.
  989. */
  990. dispose() {
  991. this.onResizeObservable.clear();
  992. this.onClearObservable.clear();
  993. this.onAfterRenderObservable.clear();
  994. this.onAfterUnbindObservable.clear();
  995. this.onBeforeBindObservable.clear();
  996. this.onBeforeRenderObservable.clear();
  997. if (this._postProcessManager) {
  998. this._postProcessManager.dispose();
  999. this._postProcessManager = null;
  1000. }
  1001. if (this._prePassRenderTarget) {
  1002. this._prePassRenderTarget.dispose();
  1003. }
  1004. this._releaseRenderPassId();
  1005. this.clearPostProcesses(true);
  1006. if (this._resizeObserver) {
  1007. this.getScene().getEngine().onResizeObservable.remove(this._resizeObserver);
  1008. this._resizeObserver = null;
  1009. }
  1010. this.renderList = null;
  1011. // Remove from custom render targets
  1012. const scene = this.getScene();
  1013. if (!scene) {
  1014. return;
  1015. }
  1016. let index = scene.customRenderTargets.indexOf(this);
  1017. if (index >= 0) {
  1018. scene.customRenderTargets.splice(index, 1);
  1019. }
  1020. for (const camera of scene.cameras) {
  1021. index = camera.customRenderTargets.indexOf(this);
  1022. if (index >= 0) {
  1023. camera.customRenderTargets.splice(index, 1);
  1024. }
  1025. }
  1026. this._renderTarget?.dispose();
  1027. this._renderTarget = null;
  1028. this._texture = null;
  1029. super.dispose();
  1030. }
  1031. /** @internal */
  1032. _rebuild() {
  1033. if (this.refreshRate === RenderTargetTexture.REFRESHRATE_RENDER_ONCE) {
  1034. this.refreshRate = RenderTargetTexture.REFRESHRATE_RENDER_ONCE;
  1035. }
  1036. if (this._postProcessManager) {
  1037. this._postProcessManager._rebuild();
  1038. }
  1039. }
  1040. /**
  1041. * Clear the info related to rendering groups preventing retention point in material dispose.
  1042. */
  1043. freeRenderingGroups() {
  1044. if (this._renderingManager) {
  1045. this._renderingManager.freeRenderingGroups();
  1046. }
  1047. }
  1048. /**
  1049. * Gets the number of views the corresponding to the texture (eg. a MultiviewRenderTarget will have > 1)
  1050. * @returns the view count
  1051. */
  1052. getViewCount() {
  1053. return 1;
  1054. }
  1055. }
  1056. /**
  1057. * The texture will only be rendered once which can be useful to improve performance if everything in your render is static for instance.
  1058. */
  1059. RenderTargetTexture.REFRESHRATE_RENDER_ONCE = 0;
  1060. /**
  1061. * The texture will only be rendered rendered every frame and is recommended for dynamic contents.
  1062. */
  1063. RenderTargetTexture.REFRESHRATE_RENDER_ONEVERYFRAME = 1;
  1064. /**
  1065. * The texture will be rendered every 2 frames which could be enough if your dynamic objects are not
  1066. * the central point of your effect and can save a lot of performances.
  1067. */
  1068. RenderTargetTexture.REFRESHRATE_RENDER_ONEVERYTWOFRAMES = 2;
  1069. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  1070. Texture._CreateRenderTargetTexture = (name, renderTargetSize, scene, generateMipMaps, creationFlags) => {
  1071. return new RenderTargetTexture(name, renderTargetSize, scene, generateMipMaps);
  1072. };
  1073. //# sourceMappingURL=renderTargetTexture.js.map