multiRenderTarget.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. import { Texture } from "../../Materials/Textures/texture.js";
  2. import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture.js";
  3. import "../../Engines/Extensions/engine.multiRender.js";
  4. /**
  5. * A multi render target, like a render target provides the ability to render to a texture.
  6. * Unlike the render target, it can render to several draw buffers (render textures) in one draw.
  7. * This is specially interesting in deferred rendering or for any effects requiring more than
  8. * just one color from a single pass.
  9. */
  10. export class MultiRenderTarget extends RenderTargetTexture {
  11. /**
  12. * Get if draw buffers (render textures) are currently supported by the used hardware and browser.
  13. */
  14. get isSupported() {
  15. return this._engine?.getCaps().drawBuffersExtension ?? false;
  16. }
  17. /**
  18. * Get the list of textures generated by the multi render target.
  19. */
  20. get textures() {
  21. return this._textures;
  22. }
  23. /**
  24. * Gets the number of textures in this MRT. This number can be different from `_textures.length` in case a depth texture is generated.
  25. */
  26. get count() {
  27. return this._count;
  28. }
  29. /**
  30. * Get the depth texture generated by the multi render target if options.generateDepthTexture has been set
  31. */
  32. get depthTexture() {
  33. return this._textures[this._textures.length - 1];
  34. }
  35. /**
  36. * Set the wrapping mode on U of all the textures we are rendering to.
  37. * Can be any of the Texture. (CLAMP_ADDRESSMODE, MIRROR_ADDRESSMODE or WRAP_ADDRESSMODE)
  38. */
  39. set wrapU(wrap) {
  40. if (this._textures) {
  41. for (let i = 0; i < this._textures.length; i++) {
  42. this._textures[i].wrapU = wrap;
  43. }
  44. }
  45. }
  46. /**
  47. * Set the wrapping mode on V of all the textures we are rendering to.
  48. * Can be any of the Texture. (CLAMP_ADDRESSMODE, MIRROR_ADDRESSMODE or WRAP_ADDRESSMODE)
  49. */
  50. set wrapV(wrap) {
  51. if (this._textures) {
  52. for (let i = 0; i < this._textures.length; i++) {
  53. this._textures[i].wrapV = wrap;
  54. }
  55. }
  56. }
  57. /**
  58. * Instantiate a new multi render target texture.
  59. * A multi render target, like a render target provides the ability to render to a texture.
  60. * Unlike the render target, it can render to several draw buffers (render textures) in one draw.
  61. * This is specially interesting in deferred rendering or for any effects requiring more than
  62. * just one color from a single pass.
  63. * @param name Define the name of the texture
  64. * @param size Define the size of the buffers to render to
  65. * @param count Define the number of target we are rendering into
  66. * @param scene Define the scene the texture belongs to
  67. * @param options Define the options used to create the multi render target
  68. * @param textureNames Define the names to set to the textures (if count \> 0 - optional)
  69. */
  70. constructor(name, size, count, scene, options, textureNames) {
  71. const generateMipMaps = options && options.generateMipMaps ? options.generateMipMaps : false;
  72. const generateDepthTexture = options && options.generateDepthTexture ? options.generateDepthTexture : false;
  73. const depthTextureFormat = options && options.depthTextureFormat ? options.depthTextureFormat : 15;
  74. const doNotChangeAspectRatio = !options || options.doNotChangeAspectRatio === undefined ? true : options.doNotChangeAspectRatio;
  75. const drawOnlyOnFirstAttachmentByDefault = options && options.drawOnlyOnFirstAttachmentByDefault ? options.drawOnlyOnFirstAttachmentByDefault : false;
  76. super(name, size, scene, generateMipMaps, doNotChangeAspectRatio, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true);
  77. if (!this.isSupported) {
  78. this.dispose();
  79. return;
  80. }
  81. this._textureNames = textureNames;
  82. const types = [];
  83. const samplingModes = [];
  84. const useSRGBBuffers = [];
  85. const formats = [];
  86. const targetTypes = [];
  87. const faceIndex = [];
  88. const layerIndex = [];
  89. const layerCounts = [];
  90. this._initTypes(count, types, samplingModes, useSRGBBuffers, formats, targetTypes, faceIndex, layerIndex, layerCounts, options);
  91. const generateDepthBuffer = !options || options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
  92. const generateStencilBuffer = !options || options.generateStencilBuffer === undefined ? false : options.generateStencilBuffer;
  93. this._multiRenderTargetOptions = {
  94. samplingModes: samplingModes,
  95. generateMipMaps: generateMipMaps,
  96. generateDepthBuffer: generateDepthBuffer,
  97. generateStencilBuffer: generateStencilBuffer,
  98. generateDepthTexture: generateDepthTexture,
  99. depthTextureFormat: depthTextureFormat,
  100. types: types,
  101. textureCount: count,
  102. useSRGBBuffers: useSRGBBuffers,
  103. formats: formats,
  104. targetTypes: targetTypes,
  105. faceIndex: faceIndex,
  106. layerIndex: layerIndex,
  107. layerCounts: layerCounts,
  108. labels: textureNames,
  109. label: name,
  110. };
  111. this._count = count;
  112. this._drawOnlyOnFirstAttachmentByDefault = drawOnlyOnFirstAttachmentByDefault;
  113. if (count > 0) {
  114. this._createInternalTextures();
  115. this._createTextures(textureNames);
  116. }
  117. }
  118. _initTypes(count, types, samplingModes, useSRGBBuffers, formats, targets, faceIndex, layerIndex, layerCounts, options) {
  119. for (let i = 0; i < count; i++) {
  120. if (options && options.types && options.types[i] !== undefined) {
  121. types.push(options.types[i]);
  122. }
  123. else {
  124. types.push(options && options.defaultType ? options.defaultType : 0);
  125. }
  126. if (options && options.samplingModes && options.samplingModes[i] !== undefined) {
  127. samplingModes.push(options.samplingModes[i]);
  128. }
  129. else {
  130. samplingModes.push(Texture.BILINEAR_SAMPLINGMODE);
  131. }
  132. if (options && options.useSRGBBuffers && options.useSRGBBuffers[i] !== undefined) {
  133. useSRGBBuffers.push(options.useSRGBBuffers[i]);
  134. }
  135. else {
  136. useSRGBBuffers.push(false);
  137. }
  138. if (options && options.formats && options.formats[i] !== undefined) {
  139. formats.push(options.formats[i]);
  140. }
  141. else {
  142. formats.push(5);
  143. }
  144. if (options && options.targetTypes && options.targetTypes[i] !== undefined) {
  145. targets.push(options.targetTypes[i]);
  146. }
  147. else {
  148. targets.push(3553);
  149. }
  150. if (options && options.faceIndex && options.faceIndex[i] !== undefined) {
  151. faceIndex.push(options.faceIndex[i]);
  152. }
  153. else {
  154. faceIndex.push(0);
  155. }
  156. if (options && options.layerIndex && options.layerIndex[i] !== undefined) {
  157. layerIndex.push(options.layerIndex[i]);
  158. }
  159. else {
  160. layerIndex.push(0);
  161. }
  162. if (options && options.layerCounts && options.layerCounts[i] !== undefined) {
  163. layerCounts.push(options.layerCounts[i]);
  164. }
  165. else {
  166. layerCounts.push(1);
  167. }
  168. }
  169. }
  170. _createInternaTextureIndexMapping() {
  171. const mapMainInternalTexture2Index = {};
  172. const mapInternalTexture2MainIndex = [];
  173. if (!this._renderTarget) {
  174. return mapInternalTexture2MainIndex;
  175. }
  176. const internalTextures = this._renderTarget.textures;
  177. for (let i = 0; i < internalTextures.length; i++) {
  178. const texture = internalTextures[i];
  179. if (!texture) {
  180. continue;
  181. }
  182. const mainIndex = mapMainInternalTexture2Index[texture.uniqueId];
  183. if (mainIndex !== undefined) {
  184. mapInternalTexture2MainIndex[i] = mainIndex;
  185. }
  186. else {
  187. mapMainInternalTexture2Index[texture.uniqueId] = i;
  188. }
  189. }
  190. return mapInternalTexture2MainIndex;
  191. }
  192. /**
  193. * @internal
  194. */
  195. _rebuild(fromContextLost = false, forceFullRebuild = false, textureNames) {
  196. if (this._count < 1 || fromContextLost) {
  197. return;
  198. }
  199. const mapInternalTexture2MainIndex = this._createInternaTextureIndexMapping();
  200. this.releaseInternalTextures();
  201. this._createInternalTextures();
  202. if (forceFullRebuild) {
  203. this._releaseTextures();
  204. this._createTextures(textureNames);
  205. }
  206. const internalTextures = this._renderTarget.textures;
  207. for (let i = 0; i < internalTextures.length; i++) {
  208. const texture = this._textures[i];
  209. if (mapInternalTexture2MainIndex[i] !== undefined) {
  210. this._renderTarget.setTexture(internalTextures[mapInternalTexture2MainIndex[i]], i);
  211. }
  212. texture._texture = internalTextures[i];
  213. if (texture._texture) {
  214. texture._noMipmap = !texture._texture.useMipMaps;
  215. texture._useSRGBBuffer = texture._texture._useSRGBBuffer;
  216. }
  217. }
  218. if (this.samples !== 1) {
  219. this._renderTarget.setSamples(this.samples, !this._drawOnlyOnFirstAttachmentByDefault, true);
  220. }
  221. }
  222. _createInternalTextures() {
  223. this._renderTarget = this._getEngine().createMultipleRenderTarget(this._size, this._multiRenderTargetOptions, !this._drawOnlyOnFirstAttachmentByDefault);
  224. this._texture = this._renderTarget.texture;
  225. }
  226. _releaseTextures() {
  227. if (this._textures) {
  228. for (let i = 0; i < this._textures.length; i++) {
  229. this._textures[i]._texture = null; // internal textures are released by a call to releaseInternalTextures()
  230. this._textures[i].dispose();
  231. }
  232. }
  233. }
  234. _createTextures(textureNames) {
  235. const internalTextures = this._renderTarget.textures;
  236. this._textures = [];
  237. for (let i = 0; i < internalTextures.length; i++) {
  238. const texture = new Texture(null, this.getScene());
  239. if (textureNames?.[i]) {
  240. texture.name = textureNames[i];
  241. }
  242. texture._texture = internalTextures[i];
  243. if (texture._texture) {
  244. texture._noMipmap = !texture._texture.useMipMaps;
  245. texture._useSRGBBuffer = texture._texture._useSRGBBuffer;
  246. }
  247. this._textures.push(texture);
  248. }
  249. }
  250. /**
  251. * Replaces an internal texture within the MRT. Useful to share textures between MultiRenderTarget.
  252. * @param texture The new texture to set in the MRT
  253. * @param index The index of the texture to replace
  254. * @param disposePrevious Set to true if the previous internal texture should be disposed
  255. */
  256. setInternalTexture(texture, index, disposePrevious = true) {
  257. if (!this.renderTarget) {
  258. return;
  259. }
  260. if (index === 0) {
  261. this._texture = texture;
  262. }
  263. this.renderTarget.setTexture(texture, index, disposePrevious);
  264. if (!this.textures[index]) {
  265. this.textures[index] = new Texture(null, this.getScene());
  266. this.textures[index].name = this._textureNames?.[index] ?? this.textures[index].name;
  267. }
  268. this.textures[index]._texture = texture;
  269. this.textures[index]._noMipmap = !texture.useMipMaps;
  270. this.textures[index]._useSRGBBuffer = texture._useSRGBBuffer;
  271. this._count = this.renderTarget.textures ? this.renderTarget.textures.length : 0;
  272. if (this._multiRenderTargetOptions.types) {
  273. this._multiRenderTargetOptions.types[index] = texture.type;
  274. }
  275. if (this._multiRenderTargetOptions.samplingModes) {
  276. this._multiRenderTargetOptions.samplingModes[index] = texture.samplingMode;
  277. }
  278. if (this._multiRenderTargetOptions.useSRGBBuffers) {
  279. this._multiRenderTargetOptions.useSRGBBuffers[index] = texture._useSRGBBuffer;
  280. }
  281. if (this._multiRenderTargetOptions.targetTypes && this._multiRenderTargetOptions.targetTypes[index] !== -1) {
  282. let target = 0;
  283. if (texture.is2DArray) {
  284. target = 35866;
  285. }
  286. else if (texture.isCube) {
  287. target = 34067;
  288. } /*else if (texture.isCubeArray) {
  289. target = 3735928559;
  290. }*/
  291. else if (texture.is3D) {
  292. target = 32879;
  293. }
  294. else {
  295. target = 3553;
  296. }
  297. this._multiRenderTargetOptions.targetTypes[index] = target;
  298. }
  299. }
  300. /**
  301. * Changes an attached texture's face index or layer.
  302. * @param index The index of the texture to modify the attachment of
  303. * @param layerIndex The layer index of the texture to be attached to the framebuffer
  304. * @param faceIndex The face index of the texture to be attached to the framebuffer
  305. */
  306. setLayerAndFaceIndex(index, layerIndex = -1, faceIndex = -1) {
  307. if (!this.textures[index] || !this.renderTarget) {
  308. return;
  309. }
  310. if (this._multiRenderTargetOptions.layerIndex) {
  311. this._multiRenderTargetOptions.layerIndex[index] = layerIndex;
  312. }
  313. if (this._multiRenderTargetOptions.faceIndex) {
  314. this._multiRenderTargetOptions.faceIndex[index] = faceIndex;
  315. }
  316. this.renderTarget.setLayerAndFaceIndex(index, layerIndex, faceIndex);
  317. }
  318. /**
  319. * Changes every attached texture's face index or layer.
  320. * @param layerIndices The layer indices of the texture to be attached to the framebuffer
  321. * @param faceIndices The face indices of the texture to be attached to the framebuffer
  322. */
  323. setLayerAndFaceIndices(layerIndices, faceIndices) {
  324. if (!this.renderTarget) {
  325. return;
  326. }
  327. this._multiRenderTargetOptions.layerIndex = layerIndices;
  328. this._multiRenderTargetOptions.faceIndex = faceIndices;
  329. this.renderTarget.setLayerAndFaceIndices(layerIndices, faceIndices);
  330. }
  331. /**
  332. * Define the number of samples used if MSAA is enabled.
  333. */
  334. get samples() {
  335. return this._samples;
  336. }
  337. set samples(value) {
  338. if (this._renderTarget) {
  339. this._samples = this._renderTarget.setSamples(value);
  340. }
  341. else {
  342. // In case samples are set with 0 textures created, we must save the desired samples value
  343. this._samples = value;
  344. }
  345. }
  346. /**
  347. * Resize all the textures in the multi render target.
  348. * Be careful as it will recreate all the data in the new texture.
  349. * @param size Define the new size
  350. */
  351. resize(size) {
  352. this._processSizeParameter(size, false);
  353. this._rebuild(false, undefined, this._textureNames);
  354. }
  355. /**
  356. * Changes the number of render targets in this MRT
  357. * Be careful as it will recreate all the data in the new texture.
  358. * @param count new texture count
  359. * @param options Specifies texture types and sampling modes for new textures
  360. * @param textureNames Specifies the names of the textures (optional)
  361. */
  362. updateCount(count, options, textureNames) {
  363. this._multiRenderTargetOptions.textureCount = count;
  364. this._count = count;
  365. const types = [];
  366. const samplingModes = [];
  367. const useSRGBBuffers = [];
  368. const formats = [];
  369. const targetTypes = [];
  370. const faceIndex = [];
  371. const layerIndex = [];
  372. const layerCounts = [];
  373. this._textureNames = textureNames;
  374. this._initTypes(count, types, samplingModes, useSRGBBuffers, formats, targetTypes, faceIndex, layerIndex, layerCounts, options);
  375. this._multiRenderTargetOptions.types = types;
  376. this._multiRenderTargetOptions.samplingModes = samplingModes;
  377. this._multiRenderTargetOptions.useSRGBBuffers = useSRGBBuffers;
  378. this._multiRenderTargetOptions.formats = formats;
  379. this._multiRenderTargetOptions.targetTypes = targetTypes;
  380. this._multiRenderTargetOptions.faceIndex = faceIndex;
  381. this._multiRenderTargetOptions.layerIndex = layerIndex;
  382. this._multiRenderTargetOptions.layerCounts = layerCounts;
  383. this._multiRenderTargetOptions.labels = textureNames;
  384. this._rebuild(false, true, textureNames);
  385. }
  386. _unbindFrameBuffer(engine, faceIndex) {
  387. if (this._renderTarget) {
  388. engine.unBindMultiColorAttachmentFramebuffer(this._renderTarget, this.isCube, () => {
  389. this.onAfterRenderObservable.notifyObservers(faceIndex);
  390. });
  391. }
  392. }
  393. /**
  394. * Dispose the render targets and their associated resources
  395. * @param doNotDisposeInternalTextures if set to true, internal textures won't be disposed (default: false).
  396. */
  397. dispose(doNotDisposeInternalTextures = false) {
  398. this._releaseTextures();
  399. if (!doNotDisposeInternalTextures) {
  400. this.releaseInternalTextures();
  401. }
  402. else {
  403. // Prevent internal texture dispose in super.dispose
  404. this._texture = null;
  405. }
  406. super.dispose();
  407. }
  408. /**
  409. * Release all the underlying texture used as draw buffers (render textures).
  410. */
  411. releaseInternalTextures() {
  412. const internalTextures = this._renderTarget?.textures;
  413. if (!internalTextures) {
  414. return;
  415. }
  416. for (let i = internalTextures.length - 1; i >= 0; i--) {
  417. this._textures[i]._texture = null;
  418. }
  419. this._renderTarget?.dispose();
  420. this._renderTarget = null;
  421. }
  422. }
  423. //# sourceMappingURL=multiRenderTarget.js.map