engine.multiRender.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. import { InternalTexture, InternalTextureSource } from '../../Materials/Textures/internalTexture';
  2. import { IMultiRenderTargetOptions } from '../../Materials/Textures/multiRenderTarget';
  3. import { Logger } from '../../Misc/logger';
  4. import { Nullable } from '../../types';
  5. import { Constants } from '../constants';
  6. import { ThinEngine } from '../thinEngine';
  7. declare module "../../Engines/thinEngine" {
  8. export interface ThinEngine {
  9. /**
  10. * Unbind a list of render target textures from the webGL context
  11. * This is used only when drawBuffer extension or webGL2 are active
  12. * @param textures defines the render target textures to unbind
  13. * @param disableGenerateMipMaps defines a boolean indicating that mipmaps must not be generated
  14. * @param onBeforeUnbind defines a function which will be called before the effective unbind
  15. */
  16. unBindMultiColorAttachmentFramebuffer(textures: InternalTexture[], disableGenerateMipMaps: boolean, onBeforeUnbind?: () => void): void;
  17. /**
  18. * Create a multi render target texture
  19. * @see http://doc.babylonjs.com/features/webgl2#multiple-render-target
  20. * @param size defines the size of the texture
  21. * @param options defines the creation options
  22. * @returns the cube texture as an InternalTexture
  23. */
  24. createMultipleRenderTarget(size: any, options: IMultiRenderTargetOptions): InternalTexture[];
  25. /**
  26. * Update the sample count for a given multiple render target texture
  27. * @see http://doc.babylonjs.com/features/webgl2#multisample-render-targets
  28. * @param textures defines the textures to update
  29. * @param samples defines the sample count to set
  30. * @returns the effective sample count (could be 0 if multisample render targets are not supported)
  31. */
  32. updateMultipleRenderTargetTextureSampleCount(textures: Nullable<InternalTexture[]>, samples: number): number;
  33. }
  34. }
  35. ThinEngine.prototype.unBindMultiColorAttachmentFramebuffer = function(textures: InternalTexture[], disableGenerateMipMaps: boolean = false, onBeforeUnbind?: () => void): void {
  36. this._currentRenderTarget = null;
  37. // If MSAA, we need to bitblt back to main texture
  38. var gl = this._gl;
  39. if (textures[0]._MSAAFramebuffer) {
  40. gl.bindFramebuffer(gl.READ_FRAMEBUFFER, textures[0]._MSAAFramebuffer);
  41. gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, textures[0]._framebuffer);
  42. var attachments = textures[0]._attachments;
  43. if (!attachments) {
  44. attachments = new Array(textures.length);
  45. textures[0]._attachments = attachments;
  46. }
  47. for (var i = 0; i < textures.length; i++) {
  48. var texture = textures[i];
  49. for (var j = 0; j < attachments.length; j++) {
  50. attachments[j] = gl.NONE;
  51. }
  52. attachments[i] = (<any>gl)[this.webGLVersion > 1 ? "COLOR_ATTACHMENT" + i : "COLOR_ATTACHMENT" + i + "_WEBGL"];
  53. gl.readBuffer(attachments[i]);
  54. gl.drawBuffers(attachments);
  55. gl.blitFramebuffer(0, 0, texture.width, texture.height,
  56. 0, 0, texture.width, texture.height,
  57. gl.COLOR_BUFFER_BIT, gl.NEAREST);
  58. }
  59. for (var i = 0; i < attachments.length; i++) {
  60. attachments[i] = (<any>gl)[this.webGLVersion > 1 ? "COLOR_ATTACHMENT" + i : "COLOR_ATTACHMENT" + i + "_WEBGL"];
  61. }
  62. gl.drawBuffers(attachments);
  63. }
  64. for (var i = 0; i < textures.length; i++) {
  65. var texture = textures[i];
  66. if (texture.generateMipMaps && !disableGenerateMipMaps && !texture.isCube) {
  67. this._bindTextureDirectly(gl.TEXTURE_2D, texture);
  68. gl.generateMipmap(gl.TEXTURE_2D);
  69. this._bindTextureDirectly(gl.TEXTURE_2D, null);
  70. }
  71. }
  72. if (onBeforeUnbind) {
  73. if (textures[0]._MSAAFramebuffer) {
  74. // Bind the correct framebuffer
  75. this._bindUnboundFramebuffer(textures[0]._framebuffer);
  76. }
  77. onBeforeUnbind();
  78. }
  79. this._bindUnboundFramebuffer(null);
  80. };
  81. ThinEngine.prototype.createMultipleRenderTarget = function(size: any, options: IMultiRenderTargetOptions): InternalTexture[] {
  82. var generateMipMaps = false;
  83. var generateDepthBuffer = true;
  84. var generateStencilBuffer = false;
  85. var generateDepthTexture = false;
  86. var textureCount = 1;
  87. var defaultType = Constants.TEXTURETYPE_UNSIGNED_INT;
  88. var defaultSamplingMode = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE;
  89. var types = new Array<number>();
  90. var samplingModes = new Array<number>();
  91. if (options !== undefined) {
  92. generateMipMaps = options.generateMipMaps === undefined ? false : options.generateMipMaps;
  93. generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
  94. generateStencilBuffer = options.generateStencilBuffer === undefined ? false : options.generateStencilBuffer;
  95. generateDepthTexture = options.generateDepthTexture === undefined ? false : options.generateDepthTexture;
  96. textureCount = options.textureCount || 1;
  97. if (options.types) {
  98. types = options.types;
  99. }
  100. if (options.samplingModes) {
  101. samplingModes = options.samplingModes;
  102. }
  103. }
  104. var gl = this._gl;
  105. // Create the framebuffer
  106. var framebuffer = gl.createFramebuffer();
  107. this._bindUnboundFramebuffer(framebuffer);
  108. var width = size.width || size;
  109. var height = size.height || size;
  110. var textures = [];
  111. var attachments = [];
  112. var depthStencilBuffer = this._setupFramebufferDepthAttachments(generateStencilBuffer, generateDepthBuffer, width, height);
  113. for (var i = 0; i < textureCount; i++) {
  114. var samplingMode = samplingModes[i] || defaultSamplingMode;
  115. var type = types[i] || defaultType;
  116. if (type === Constants.TEXTURETYPE_FLOAT && !this._caps.textureFloatLinearFiltering) {
  117. // if floating point linear (gl.FLOAT) then force to NEAREST_SAMPLINGMODE
  118. samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
  119. }
  120. else if (type === Constants.TEXTURETYPE_HALF_FLOAT && !this._caps.textureHalfFloatLinearFiltering) {
  121. // if floating point linear (HALF_FLOAT) then force to NEAREST_SAMPLINGMODE
  122. samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
  123. }
  124. var filters = this._getSamplingParameters(samplingMode, generateMipMaps);
  125. if (type === Constants.TEXTURETYPE_FLOAT && !this._caps.textureFloat) {
  126. type = Constants.TEXTURETYPE_UNSIGNED_INT;
  127. Logger.Warn("Float textures are not supported. Render target forced to TEXTURETYPE_UNSIGNED_BYTE type");
  128. }
  129. var texture = new InternalTexture(this, InternalTextureSource.MultiRenderTarget);
  130. var attachment = (<any>gl)[this.webGLVersion > 1 ? "COLOR_ATTACHMENT" + i : "COLOR_ATTACHMENT" + i + "_WEBGL"];
  131. textures.push(texture);
  132. attachments.push(attachment);
  133. gl.activeTexture((<any>gl)["TEXTURE" + i]);
  134. gl.bindTexture(gl.TEXTURE_2D, texture._webGLTexture);
  135. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filters.mag);
  136. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filters.min);
  137. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  138. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  139. gl.texImage2D(gl.TEXTURE_2D, 0, this._getRGBABufferInternalSizedFormat(type), width, height, 0, gl.RGBA, this._getWebGLTextureType(type), null);
  140. gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, attachment, gl.TEXTURE_2D, texture._webGLTexture, 0);
  141. if (generateMipMaps) {
  142. this._gl.generateMipmap(this._gl.TEXTURE_2D);
  143. }
  144. // Unbind
  145. this._bindTextureDirectly(gl.TEXTURE_2D, null);
  146. texture._framebuffer = framebuffer;
  147. texture._depthStencilBuffer = depthStencilBuffer;
  148. texture.baseWidth = width;
  149. texture.baseHeight = height;
  150. texture.width = width;
  151. texture.height = height;
  152. texture.isReady = true;
  153. texture.samples = 1;
  154. texture.generateMipMaps = generateMipMaps;
  155. texture.samplingMode = samplingMode;
  156. texture.type = type;
  157. texture._generateDepthBuffer = generateDepthBuffer;
  158. texture._generateStencilBuffer = generateStencilBuffer;
  159. texture._attachments = attachments;
  160. this._internalTexturesCache.push(texture);
  161. }
  162. if (generateDepthTexture && this._caps.depthTextureExtension) {
  163. // Depth texture
  164. var depthTexture = new InternalTexture(this, InternalTextureSource.MultiRenderTarget);
  165. gl.activeTexture(gl.TEXTURE0);
  166. gl.bindTexture(gl.TEXTURE_2D, depthTexture._webGLTexture);
  167. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  168. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  169. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  170. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  171. gl.texImage2D(
  172. gl.TEXTURE_2D,
  173. 0,
  174. this.webGLVersion < 2 ? gl.DEPTH_COMPONENT : gl.DEPTH_COMPONENT16,
  175. width,
  176. height,
  177. 0,
  178. gl.DEPTH_COMPONENT,
  179. gl.UNSIGNED_SHORT,
  180. null
  181. );
  182. gl.framebufferTexture2D(
  183. gl.FRAMEBUFFER,
  184. gl.DEPTH_ATTACHMENT,
  185. gl.TEXTURE_2D,
  186. depthTexture._webGLTexture,
  187. 0
  188. );
  189. depthTexture._framebuffer = framebuffer;
  190. depthTexture.baseWidth = width;
  191. depthTexture.baseHeight = height;
  192. depthTexture.width = width;
  193. depthTexture.height = height;
  194. depthTexture.isReady = true;
  195. depthTexture.samples = 1;
  196. depthTexture.generateMipMaps = generateMipMaps;
  197. depthTexture.samplingMode = gl.NEAREST;
  198. depthTexture._generateDepthBuffer = generateDepthBuffer;
  199. depthTexture._generateStencilBuffer = generateStencilBuffer;
  200. textures.push(depthTexture);
  201. this._internalTexturesCache.push(depthTexture);
  202. }
  203. gl.drawBuffers(attachments);
  204. gl.bindRenderbuffer(gl.RENDERBUFFER, null);
  205. this._bindUnboundFramebuffer(null);
  206. this.resetTextureCache();
  207. return textures;
  208. };
  209. ThinEngine.prototype.updateMultipleRenderTargetTextureSampleCount = function(textures: Nullable<InternalTexture[]>, samples: number): number {
  210. if (this.webGLVersion < 2 || !textures || textures.length == 0) {
  211. return 1;
  212. }
  213. if (textures[0].samples === samples) {
  214. return samples;
  215. }
  216. var gl = this._gl;
  217. samples = Math.min(samples, this.getCaps().maxMSAASamples);
  218. // Dispose previous render buffers
  219. if (textures[0]._depthStencilBuffer) {
  220. gl.deleteRenderbuffer(textures[0]._depthStencilBuffer);
  221. textures[0]._depthStencilBuffer = null;
  222. }
  223. if (textures[0]._MSAAFramebuffer) {
  224. gl.deleteFramebuffer(textures[0]._MSAAFramebuffer);
  225. textures[0]._MSAAFramebuffer = null;
  226. }
  227. for (var i = 0; i < textures.length; i++) {
  228. if (textures[i]._MSAARenderBuffer) {
  229. gl.deleteRenderbuffer(textures[i]._MSAARenderBuffer);
  230. textures[i]._MSAARenderBuffer = null;
  231. }
  232. }
  233. if (samples > 1) {
  234. let framebuffer = gl.createFramebuffer();
  235. if (!framebuffer) {
  236. throw new Error("Unable to create multi sampled framebuffer");
  237. }
  238. this._bindUnboundFramebuffer(framebuffer);
  239. let depthStencilBuffer = this._setupFramebufferDepthAttachments(textures[0]._generateStencilBuffer, textures[0]._generateDepthBuffer, textures[0].width, textures[0].height, samples);
  240. var attachments = [];
  241. for (var i = 0; i < textures.length; i++) {
  242. var texture = textures[i];
  243. var attachment = (<any>gl)[this.webGLVersion > 1 ? "COLOR_ATTACHMENT" + i : "COLOR_ATTACHMENT" + i + "_WEBGL"];
  244. var colorRenderbuffer = gl.createRenderbuffer();
  245. if (!colorRenderbuffer) {
  246. throw new Error("Unable to create multi sampled framebuffer");
  247. }
  248. gl.bindRenderbuffer(gl.RENDERBUFFER, colorRenderbuffer);
  249. gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, this._getRGBAMultiSampleBufferFormat(texture.type), texture.width, texture.height);
  250. gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, colorRenderbuffer);
  251. texture._MSAAFramebuffer = framebuffer;
  252. texture._MSAARenderBuffer = colorRenderbuffer;
  253. texture.samples = samples;
  254. texture._depthStencilBuffer = depthStencilBuffer;
  255. gl.bindRenderbuffer(gl.RENDERBUFFER, null);
  256. attachments.push(attachment);
  257. }
  258. gl.drawBuffers(attachments);
  259. } else {
  260. this._bindUnboundFramebuffer(textures[0]._framebuffer);
  261. }
  262. this._bindUnboundFramebuffer(null);
  263. return samples;
  264. };