Framebuffer.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /*global define*/
  2. define([
  3. '../Core/defaultValue',
  4. '../Core/defined',
  5. '../Core/defineProperties',
  6. '../Core/destroyObject',
  7. '../Core/DeveloperError',
  8. '../Core/PixelFormat'
  9. ], function(
  10. defaultValue,
  11. defined,
  12. defineProperties,
  13. destroyObject,
  14. DeveloperError,
  15. PixelFormat) {
  16. "use strict";
  17. function attachTexture(framebuffer, attachment, texture) {
  18. var gl = framebuffer._gl;
  19. gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, texture._target, texture._texture, 0);
  20. }
  21. function attachRenderbuffer(framebuffer, attachment, renderbuffer) {
  22. var gl = framebuffer._gl;
  23. gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, renderbuffer._getRenderbuffer());
  24. }
  25. /**
  26. * @private
  27. */
  28. var Framebuffer = function(gl, maximumColorAttachments, options) {
  29. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  30. this._gl = gl;
  31. this._framebuffer = gl.createFramebuffer();
  32. this._colorTextures = [];
  33. this._colorRenderbuffers = [];
  34. this._activeColorAttachments = [];
  35. this._depthTexture = undefined;
  36. this._depthRenderbuffer = undefined;
  37. this._stencilRenderbuffer = undefined;
  38. this._depthStencilTexture = undefined;
  39. this._depthStencilRenderbuffer = undefined;
  40. /**
  41. * When true, the framebuffer owns its attachments so they will be destroyed when
  42. * {@link Framebuffer#destroy} is called or when a new attachment is assigned
  43. * to an attachment point.
  44. *
  45. * @type {Boolean}
  46. * @default true
  47. *
  48. * @see Framebuffer#destroy
  49. */
  50. this.destroyAttachments = defaultValue(options.destroyAttachments, true);
  51. // Throw if a texture and renderbuffer are attached to the same point. This won't
  52. // cause a WebGL error (because only one will be attached), but is likely a developer error.
  53. //>>includeStart('debug', pragmas.debug);
  54. if (defined(options.colorTextures) && defined(options.colorRenderbuffers)) {
  55. throw new DeveloperError('Cannot have both color texture and color renderbuffer attachments.');
  56. }
  57. if (defined(options.depthTexture) && defined(options.depthRenderbuffer)) {
  58. throw new DeveloperError('Cannot have both a depth texture and depth renderbuffer attachment.');
  59. }
  60. if (defined(options.depthStencilTexture) && defined(options.depthStencilRenderbuffer)) {
  61. throw new DeveloperError('Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment.');
  62. }
  63. //>>includeEnd('debug');
  64. // Avoid errors defined in Section 6.5 of the WebGL spec
  65. var depthAttachment = (defined(options.depthTexture) || defined(options.depthRenderbuffer));
  66. var depthStencilAttachment = (defined(options.depthStencilTexture) || defined(options.depthStencilRenderbuffer));
  67. //>>includeStart('debug', pragmas.debug);
  68. if (depthAttachment && depthStencilAttachment) {
  69. throw new DeveloperError('Cannot have both a depth and depth-stencil attachment.');
  70. }
  71. if (defined(options.stencilRenderbuffer) && depthStencilAttachment) {
  72. throw new DeveloperError('Cannot have both a stencil and depth-stencil attachment.');
  73. }
  74. if (depthAttachment && defined(options.stencilRenderbuffer)) {
  75. throw new DeveloperError('Cannot have both a depth and stencil attachment.');
  76. }
  77. //>>includeEnd('debug');
  78. ///////////////////////////////////////////////////////////////////
  79. this._bind();
  80. var texture;
  81. var renderbuffer;
  82. var i;
  83. var length;
  84. var attachmentEnum;
  85. if (defined(options.colorTextures)) {
  86. var textures = options.colorTextures;
  87. length = this._colorTextures.length = this._activeColorAttachments.length = textures.length;
  88. //>>includeStart('debug', pragmas.debug);
  89. if (length > maximumColorAttachments) {
  90. throw new DeveloperError('The number of color attachments exceeds the number supported.');
  91. }
  92. //>>includeEnd('debug');
  93. for (i = 0; i < length; ++i) {
  94. texture = textures[i];
  95. //>>includeStart('debug', pragmas.debug);
  96. if (!PixelFormat.isColorFormat(texture.pixelFormat)) {
  97. throw new DeveloperError('The color-texture pixel-format must be a color format.');
  98. }
  99. //>>includeEnd('debug');
  100. attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i;
  101. attachTexture(this, attachmentEnum, texture);
  102. this._activeColorAttachments[i] = attachmentEnum;
  103. this._colorTextures[i] = texture;
  104. }
  105. }
  106. if (defined(options.colorRenderbuffers)) {
  107. var renderbuffers = options.colorRenderbuffers;
  108. length = this._colorRenderbuffers.length = this._activeColorAttachments.length = renderbuffers.length;
  109. //>>includeStart('debug', pragmas.debug);
  110. if (length > maximumColorAttachments) {
  111. throw new DeveloperError('The number of color attachments exceeds the number supported.');
  112. }
  113. //>>includeEnd('debug');
  114. for (i = 0; i < length; ++i) {
  115. renderbuffer = renderbuffers[i];
  116. attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i;
  117. attachRenderbuffer(this, attachmentEnum, renderbuffer);
  118. this._activeColorAttachments[i] = attachmentEnum;
  119. this._colorRenderbuffers[i] = renderbuffer;
  120. }
  121. }
  122. if (defined(options.depthTexture)) {
  123. texture = options.depthTexture;
  124. //>>includeStart('debug', pragmas.debug);
  125. if (texture.pixelFormat !== PixelFormat.DEPTH_COMPONENT) {
  126. throw new DeveloperError('The depth-texture pixel-format must be DEPTH_COMPONENT.');
  127. }
  128. //>>includeEnd('debug');
  129. attachTexture(this, this._gl.DEPTH_ATTACHMENT, texture);
  130. this._depthTexture = texture;
  131. }
  132. if (defined(options.depthRenderbuffer)) {
  133. renderbuffer = options.depthRenderbuffer;
  134. attachRenderbuffer(this, this._gl.DEPTH_ATTACHMENT, renderbuffer);
  135. this._depthRenderbuffer = renderbuffer;
  136. }
  137. if (defined(options.stencilRenderbuffer)) {
  138. renderbuffer = options.stencilRenderbuffer;
  139. attachRenderbuffer(this, this._gl.STENCIL_ATTACHMENT, renderbuffer);
  140. this._stencilRenderbuffer = renderbuffer;
  141. }
  142. if (defined(options.depthStencilTexture)) {
  143. texture = options.depthStencilTexture;
  144. //>>includeStart('debug', pragmas.debug);
  145. if (texture.pixelFormat !== PixelFormat.DEPTH_STENCIL) {
  146. throw new DeveloperError('The depth-stencil pixel-format must be DEPTH_STENCIL.');
  147. }
  148. //>>includeEnd('debug');
  149. attachTexture(this, this._gl.DEPTH_STENCIL_ATTACHMENT, texture);
  150. this._depthStencilTexture = texture;
  151. }
  152. if (defined(options.depthStencilRenderbuffer)) {
  153. renderbuffer = options.depthStencilRenderbuffer;
  154. attachRenderbuffer(this, this._gl.DEPTH_STENCIL_ATTACHMENT, renderbuffer);
  155. this._depthStencilRenderbuffer = renderbuffer;
  156. }
  157. this._unBind();
  158. };
  159. defineProperties(Framebuffer.prototype, {
  160. /**
  161. * The status of the framebuffer. If the status is not WebGLRenderingContext.COMPLETE,
  162. * a {@link DeveloperError} will be thrown when attempting to render to the framebuffer.
  163. * @memberof Framebuffer.prototype
  164. * @type {Number}
  165. */
  166. status : {
  167. get : function() {
  168. this._bind();
  169. var status = this._gl.checkFramebufferStatus(this._gl.FRAMEBUFFER);
  170. this._unBind();
  171. return status;
  172. }
  173. },
  174. numberOfColorAttachments : {
  175. get : function() {
  176. return this._activeColorAttachments.length;
  177. }
  178. },
  179. depthTexture: {
  180. get : function() {
  181. return this._depthTexture;
  182. }
  183. },
  184. depthRenderbuffer: {
  185. get : function() {
  186. return this._depthRenderbuffer;
  187. }
  188. },
  189. stencilRenderbuffer : {
  190. get : function() {
  191. return this._stencilRenderbuffer;
  192. }
  193. },
  194. depthStencilTexture : {
  195. get : function() {
  196. return this._depthStencilTexture;
  197. }
  198. },
  199. depthStencilRenderbuffer : {
  200. get : function() {
  201. return this._depthStencilRenderbuffer;
  202. }
  203. },
  204. /**
  205. * True if the framebuffer has a depth attachment. Depth attachments include
  206. * depth and depth-stencil textures, and depth and depth-stencil renderbuffers. When
  207. * rendering to a framebuffer, a depth attachment is required for the depth test to have effect.
  208. * @memberof Framebuffer.prototype
  209. * @type {Boolean}
  210. */
  211. hasDepthAttachment : {
  212. get : function() {
  213. return !!(this.depthTexture || this.depthRenderbuffer || this.depthStencilTexture || this.depthStencilRenderbuffer);
  214. }
  215. }
  216. });
  217. Framebuffer.prototype._bind = function() {
  218. var gl = this._gl;
  219. gl.bindFramebuffer(gl.FRAMEBUFFER, this._framebuffer);
  220. };
  221. Framebuffer.prototype._unBind = function() {
  222. var gl = this._gl;
  223. gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  224. };
  225. Framebuffer.prototype._getActiveColorAttachments = function() {
  226. return this._activeColorAttachments;
  227. };
  228. Framebuffer.prototype.getColorTexture = function(index) {
  229. //>>includeStart('debug', pragmas.debug);
  230. if (!defined(index) || index < 0 || index >= this._colorTextures.length) {
  231. throw new DeveloperError('index is required, must be greater than or equal to zero and must be less than the number of color attachments.');
  232. }
  233. //>>includeEnd('debug');
  234. return this._colorTextures[index];
  235. };
  236. Framebuffer.prototype.getColorRenderbuffer = function(index) {
  237. //>>includeStart('debug', pragmas.debug);
  238. if (!defined(index) || index < 0 || index >= this._colorRenderbuffers.length) {
  239. throw new DeveloperError('index is required, must be greater than or equal to zero and must be less than the number of color attachments.');
  240. }
  241. //>>includeEnd('debug');
  242. return this._colorRenderbuffers[index];
  243. };
  244. Framebuffer.prototype.isDestroyed = function() {
  245. return false;
  246. };
  247. Framebuffer.prototype.destroy = function() {
  248. if (this.destroyAttachments) {
  249. // If the color texture is a cube map face, it is owned by the cube map, and will not be destroyed.
  250. var i = 0;
  251. var textures = this._colorTextures;
  252. var length = textures.length;
  253. for (; i < length; ++i) {
  254. var texture = textures[i];
  255. if (defined(texture)) {
  256. texture.destroy();
  257. }
  258. }
  259. var renderbuffers = this._colorRenderbuffers;
  260. length = renderbuffers.length;
  261. for (i = 0; i < length; ++i) {
  262. var renderbuffer = renderbuffers[i];
  263. if (defined(renderbuffer)) {
  264. renderbuffer.destroy();
  265. }
  266. }
  267. this._depthTexture = this._depthTexture && this._depthTexture.destroy();
  268. this._depthRenderbuffer = this._depthRenderbuffer && this._depthRenderbuffer.destroy();
  269. this._stencilRenderbuffer = this._stencilRenderbuffer && this._stencilRenderbuffer.destroy();
  270. this._depthStencilTexture = this._depthStencilTexture && this._depthStencilTexture.destroy();
  271. this._depthStencilRenderbuffer = this._depthStencilRenderbuffer && this._depthStencilRenderbuffer.destroy();
  272. }
  273. this._gl.deleteFramebuffer(this._framebuffer);
  274. return destroyObject(this);
  275. };
  276. return Framebuffer;
  277. });