123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- import Check from "../Core/Check.js";
- import defaultValue from "../Core/defaultValue.js";
- import defined from "../Core/defined.js";
- import destroyObject from "../Core/destroyObject.js";
- import DeveloperError from "../Core/DeveloperError.js";
- import PixelFormat from "../Core/PixelFormat.js";
- import ContextLimits from "./ContextLimits.js";
- import PixelDatatype from "./PixelDatatype.js";
- function attachTexture(framebuffer, attachment, texture) {
- const gl = framebuffer._gl;
- gl.framebufferTexture2D(
- gl.FRAMEBUFFER,
- attachment,
- texture._target,
- texture._texture,
- 0
- );
- }
- function attachRenderbuffer(framebuffer, attachment, renderbuffer) {
- const gl = framebuffer._gl;
- gl.framebufferRenderbuffer(
- gl.FRAMEBUFFER,
- attachment,
- gl.RENDERBUFFER,
- renderbuffer._getRenderbuffer()
- );
- }
- /**
- * Creates a framebuffer with optional initial color, depth, and stencil attachments.
- * Framebuffers are used for render-to-texture effects; they allow us to render to
- * textures in one pass, and read from it in a later pass.
- *
- * @param {Object} options The initial framebuffer attachments as shown in the example below. <code>context</code> is required. The possible properties are <code>colorTextures</code>, <code>colorRenderbuffers</code>, <code>depthTexture</code>, <code>depthRenderbuffer</code>, <code>stencilRenderbuffer</code>, <code>depthStencilTexture</code>, <code>depthStencilRenderbuffer</code>, and <code>destroyAttachments</code>.
- *
- * @exception {DeveloperError} Cannot have both color texture and color renderbuffer attachments.
- * @exception {DeveloperError} Cannot have both a depth texture and depth renderbuffer attachment.
- * @exception {DeveloperError} Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment.
- * @exception {DeveloperError} Cannot have both a depth and depth-stencil renderbuffer.
- * @exception {DeveloperError} Cannot have both a stencil and depth-stencil renderbuffer.
- * @exception {DeveloperError} Cannot have both a depth and stencil renderbuffer.
- * @exception {DeveloperError} The color-texture pixel-format must be a color format.
- * @exception {DeveloperError} The depth-texture pixel-format must be DEPTH_COMPONENT.
- * @exception {DeveloperError} The depth-stencil-texture pixel-format must be DEPTH_STENCIL.
- * @exception {DeveloperError} The number of color attachments exceeds the number supported.
- * @exception {DeveloperError} The color-texture pixel datatype is HALF_FLOAT and the WebGL implementation does not support the EXT_color_buffer_half_float extension.
- * @exception {DeveloperError} The color-texture pixel datatype is FLOAT and the WebGL implementation does not support the EXT_color_buffer_float or WEBGL_color_buffer_float extensions.
- *
- * @example
- * // Create a framebuffer with color and depth texture attachments.
- * const width = context.canvas.clientWidth;
- * const height = context.canvas.clientHeight;
- * const framebuffer = new Framebuffer({
- * context : context,
- * colorTextures : [new Texture({
- * context : context,
- * width : width,
- * height : height,
- * pixelFormat : PixelFormat.RGBA
- * })],
- * depthTexture : new Texture({
- * context : context,
- * width : width,
- * height : height,
- * pixelFormat : PixelFormat.DEPTH_COMPONENT,
- * pixelDatatype : PixelDatatype.UNSIGNED_SHORT
- * })
- * });
- *
- * @private
- * @constructor
- */
- function Framebuffer(options) {
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
- const context = options.context;
- //>>includeStart('debug', pragmas.debug);
- Check.defined("options.context", context);
- //>>includeEnd('debug');
- const gl = context._gl;
- const maximumColorAttachments = ContextLimits.maximumColorAttachments;
- this._gl = gl;
- this._framebuffer = gl.createFramebuffer();
- this._colorTextures = [];
- this._colorRenderbuffers = [];
- this._activeColorAttachments = [];
- this._depthTexture = undefined;
- this._depthRenderbuffer = undefined;
- this._stencilRenderbuffer = undefined;
- this._depthStencilTexture = undefined;
- this._depthStencilRenderbuffer = undefined;
- /**
- * When true, the framebuffer owns its attachments so they will be destroyed when
- * {@link Framebuffer#destroy} is called or when a new attachment is assigned
- * to an attachment point.
- *
- * @type {Boolean}
- * @default true
- *
- * @see Framebuffer#destroy
- */
- this.destroyAttachments = defaultValue(options.destroyAttachments, true);
- // Throw if a texture and renderbuffer are attached to the same point. This won't
- // cause a WebGL error (because only one will be attached), but is likely a developer error.
- //>>includeStart('debug', pragmas.debug);
- if (defined(options.colorTextures) && defined(options.colorRenderbuffers)) {
- throw new DeveloperError(
- "Cannot have both color texture and color renderbuffer attachments."
- );
- }
- if (defined(options.depthTexture) && defined(options.depthRenderbuffer)) {
- throw new DeveloperError(
- "Cannot have both a depth texture and depth renderbuffer attachment."
- );
- }
- if (
- defined(options.depthStencilTexture) &&
- defined(options.depthStencilRenderbuffer)
- ) {
- throw new DeveloperError(
- "Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment."
- );
- }
- //>>includeEnd('debug');
- // Avoid errors defined in Section 6.5 of the WebGL spec
- const depthAttachment =
- defined(options.depthTexture) || defined(options.depthRenderbuffer);
- const depthStencilAttachment =
- defined(options.depthStencilTexture) ||
- defined(options.depthStencilRenderbuffer);
- //>>includeStart('debug', pragmas.debug);
- if (depthAttachment && depthStencilAttachment) {
- throw new DeveloperError(
- "Cannot have both a depth and depth-stencil attachment."
- );
- }
- if (defined(options.stencilRenderbuffer) && depthStencilAttachment) {
- throw new DeveloperError(
- "Cannot have both a stencil and depth-stencil attachment."
- );
- }
- if (depthAttachment && defined(options.stencilRenderbuffer)) {
- throw new DeveloperError(
- "Cannot have both a depth and stencil attachment."
- );
- }
- //>>includeEnd('debug');
- ///////////////////////////////////////////////////////////////////
- this._bind();
- let texture;
- let renderbuffer;
- let i;
- let length;
- let attachmentEnum;
- if (defined(options.colorTextures)) {
- const textures = options.colorTextures;
- length = this._colorTextures.length = this._activeColorAttachments.length =
- textures.length;
- //>>includeStart('debug', pragmas.debug);
- if (length > maximumColorAttachments) {
- throw new DeveloperError(
- "The number of color attachments exceeds the number supported."
- );
- }
- //>>includeEnd('debug');
- for (i = 0; i < length; ++i) {
- texture = textures[i];
- //>>includeStart('debug', pragmas.debug);
- if (!PixelFormat.isColorFormat(texture.pixelFormat)) {
- throw new DeveloperError(
- "The color-texture pixel-format must be a color format."
- );
- }
- if (
- texture.pixelDatatype === PixelDatatype.FLOAT &&
- !context.colorBufferFloat
- ) {
- throw new DeveloperError(
- "The color texture pixel datatype is FLOAT and the WebGL implementation does not support the EXT_color_buffer_float or WEBGL_color_buffer_float extensions. See Context.colorBufferFloat."
- );
- }
- if (
- texture.pixelDatatype === PixelDatatype.HALF_FLOAT &&
- !context.colorBufferHalfFloat
- ) {
- throw new DeveloperError(
- "The color texture pixel datatype is HALF_FLOAT and the WebGL implementation does not support the EXT_color_buffer_half_float extension. See Context.colorBufferHalfFloat."
- );
- }
- //>>includeEnd('debug');
- attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i;
- attachTexture(this, attachmentEnum, texture);
- this._activeColorAttachments[i] = attachmentEnum;
- this._colorTextures[i] = texture;
- }
- }
- if (defined(options.colorRenderbuffers)) {
- const renderbuffers = options.colorRenderbuffers;
- length = this._colorRenderbuffers.length = this._activeColorAttachments.length =
- renderbuffers.length;
- //>>includeStart('debug', pragmas.debug);
- if (length > maximumColorAttachments) {
- throw new DeveloperError(
- "The number of color attachments exceeds the number supported."
- );
- }
- //>>includeEnd('debug');
- for (i = 0; i < length; ++i) {
- renderbuffer = renderbuffers[i];
- attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i;
- attachRenderbuffer(this, attachmentEnum, renderbuffer);
- this._activeColorAttachments[i] = attachmentEnum;
- this._colorRenderbuffers[i] = renderbuffer;
- }
- }
- if (defined(options.depthTexture)) {
- texture = options.depthTexture;
- //>>includeStart('debug', pragmas.debug);
- if (texture.pixelFormat !== PixelFormat.DEPTH_COMPONENT) {
- throw new DeveloperError(
- "The depth-texture pixel-format must be DEPTH_COMPONENT."
- );
- }
- //>>includeEnd('debug');
- attachTexture(this, this._gl.DEPTH_ATTACHMENT, texture);
- this._depthTexture = texture;
- }
- if (defined(options.depthRenderbuffer)) {
- renderbuffer = options.depthRenderbuffer;
- attachRenderbuffer(this, this._gl.DEPTH_ATTACHMENT, renderbuffer);
- this._depthRenderbuffer = renderbuffer;
- }
- if (defined(options.stencilRenderbuffer)) {
- renderbuffer = options.stencilRenderbuffer;
- attachRenderbuffer(this, this._gl.STENCIL_ATTACHMENT, renderbuffer);
- this._stencilRenderbuffer = renderbuffer;
- }
- if (defined(options.depthStencilTexture)) {
- texture = options.depthStencilTexture;
- //>>includeStart('debug', pragmas.debug);
- if (texture.pixelFormat !== PixelFormat.DEPTH_STENCIL) {
- throw new DeveloperError(
- "The depth-stencil pixel-format must be DEPTH_STENCIL."
- );
- }
- //>>includeEnd('debug');
- attachTexture(this, this._gl.DEPTH_STENCIL_ATTACHMENT, texture);
- this._depthStencilTexture = texture;
- }
- if (defined(options.depthStencilRenderbuffer)) {
- renderbuffer = options.depthStencilRenderbuffer;
- attachRenderbuffer(this, this._gl.DEPTH_STENCIL_ATTACHMENT, renderbuffer);
- this._depthStencilRenderbuffer = renderbuffer;
- }
- this._unBind();
- }
- Object.defineProperties(Framebuffer.prototype, {
- /**
- * The status of the framebuffer. If the status is not WebGLConstants.FRAMEBUFFER_COMPLETE,
- * a {@link DeveloperError} will be thrown when attempting to render to the framebuffer.
- * @memberof Framebuffer.prototype
- * @type {Number}
- */
- status: {
- get: function () {
- this._bind();
- const status = this._gl.checkFramebufferStatus(this._gl.FRAMEBUFFER);
- this._unBind();
- return status;
- },
- },
- numberOfColorAttachments: {
- get: function () {
- return this._activeColorAttachments.length;
- },
- },
- depthTexture: {
- get: function () {
- return this._depthTexture;
- },
- },
- depthRenderbuffer: {
- get: function () {
- return this._depthRenderbuffer;
- },
- },
- stencilRenderbuffer: {
- get: function () {
- return this._stencilRenderbuffer;
- },
- },
- depthStencilTexture: {
- get: function () {
- return this._depthStencilTexture;
- },
- },
- depthStencilRenderbuffer: {
- get: function () {
- return this._depthStencilRenderbuffer;
- },
- },
- /**
- * True if the framebuffer has a depth attachment. Depth attachments include
- * depth and depth-stencil textures, and depth and depth-stencil renderbuffers. When
- * rendering to a framebuffer, a depth attachment is required for the depth test to have effect.
- * @memberof Framebuffer.prototype
- * @type {Boolean}
- */
- hasDepthAttachment: {
- get: function () {
- return !!(
- this.depthTexture ||
- this.depthRenderbuffer ||
- this.depthStencilTexture ||
- this.depthStencilRenderbuffer
- );
- },
- },
- });
- Framebuffer.prototype._bind = function () {
- const gl = this._gl;
- gl.bindFramebuffer(gl.FRAMEBUFFER, this._framebuffer);
- };
- Framebuffer.prototype._unBind = function () {
- const gl = this._gl;
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- };
- Framebuffer.prototype.bindDraw = function () {
- const gl = this._gl;
- gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this._framebuffer);
- };
- Framebuffer.prototype.bindRead = function () {
- const gl = this._gl;
- gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this._framebuffer);
- };
- Framebuffer.prototype._getActiveColorAttachments = function () {
- return this._activeColorAttachments;
- };
- Framebuffer.prototype.getColorTexture = function (index) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(index) || index < 0 || index >= this._colorTextures.length) {
- throw new DeveloperError(
- "index is required, must be greater than or equal to zero and must be less than the number of color attachments."
- );
- }
- //>>includeEnd('debug');
- return this._colorTextures[index];
- };
- Framebuffer.prototype.getColorRenderbuffer = function (index) {
- //>>includeStart('debug', pragmas.debug);
- if (
- !defined(index) ||
- index < 0 ||
- index >= this._colorRenderbuffers.length
- ) {
- throw new DeveloperError(
- "index is required, must be greater than or equal to zero and must be less than the number of color attachments."
- );
- }
- //>>includeEnd('debug');
- return this._colorRenderbuffers[index];
- };
- Framebuffer.prototype.isDestroyed = function () {
- return false;
- };
- Framebuffer.prototype.destroy = function () {
- if (this.destroyAttachments) {
- // If the color texture is a cube map face, it is owned by the cube map, and will not be destroyed.
- let i = 0;
- const textures = this._colorTextures;
- let length = textures.length;
- for (; i < length; ++i) {
- const texture = textures[i];
- if (defined(texture)) {
- texture.destroy();
- }
- }
- const renderbuffers = this._colorRenderbuffers;
- length = renderbuffers.length;
- for (i = 0; i < length; ++i) {
- const renderbuffer = renderbuffers[i];
- if (defined(renderbuffer)) {
- renderbuffer.destroy();
- }
- }
- this._depthTexture = this._depthTexture && this._depthTexture.destroy();
- this._depthRenderbuffer =
- this._depthRenderbuffer && this._depthRenderbuffer.destroy();
- this._stencilRenderbuffer =
- this._stencilRenderbuffer && this._stencilRenderbuffer.destroy();
- this._depthStencilTexture =
- this._depthStencilTexture && this._depthStencilTexture.destroy();
- this._depthStencilRenderbuffer =
- this._depthStencilRenderbuffer &&
- this._depthStencilRenderbuffer.destroy();
- }
- this._gl.deleteFramebuffer(this._framebuffer);
- return destroyObject(this);
- };
- export default Framebuffer;
|