Last active
May 9, 2022 18:07
-
-
Save CodyJasonBennett/edbc4372d3c9a921ebc3e46cb5899ba8 to your computer and use it in GitHub Desktop.
WebGL 2 FBO w/Multisampled MRT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Constructs a WebGL FBO with MRT and multi-sampling. | |
*/ | |
export class WebGLFBO { | |
readonly gl: WebGL2RenderingContext | |
readonly width: number | |
readonly height: number | |
readonly count: number | |
readonly samples: number | |
readonly frameBuffer: WebGLFramebuffer | |
readonly multisampleFrameBuffer: WebGLFramebuffer | |
readonly textures: WebGLTexture[] | |
readonly renderBuffers: WebGLRenderbuffer[] | |
constructor( | |
gl: WebGL2RenderingContext, | |
width: number, | |
height: number, | |
count = 1, | |
samples = 0, | |
textures: WebGLTexture[] = [], | |
) { | |
this.gl = gl | |
this.width = width | |
this.height = height | |
this.count = count | |
this.samples = samples | |
this.frameBuffer = this.gl.createFramebuffer()! | |
this.multisampleFrameBuffer = this.gl.createFramebuffer()! | |
this.textures = textures | |
this.renderBuffers = [] | |
this.bind() | |
const attachments: number[] = [] | |
for (let i = 0; i < this.count; i++) { | |
const attachment = this.gl.COLOR_ATTACHMENT0 + i | |
attachments.push(attachment) | |
if (this.samples) { | |
const renderBuffer = this.gl.createRenderbuffer()! | |
this.renderBuffers.push(renderBuffer) | |
this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, renderBuffer) | |
this.gl.renderbufferStorageMultisample( | |
this.gl.RENDERBUFFER, | |
this.samples, | |
this.gl.RGBA8, | |
this.width, | |
this.height, | |
) | |
this.gl.framebufferRenderbuffer(this.gl.FRAMEBUFFER, attachment, this.gl.RENDERBUFFER, renderBuffer) | |
} else if (this.textures.length) { | |
const texture = this.textures[i] | |
this.gl.framebufferTexture2D(this.gl.DRAW_FRAMEBUFFER, attachment, this.gl.TEXTURE_2D, texture, 0) | |
} | |
} | |
this.gl.drawBuffers(attachments) | |
this.unbind() | |
} | |
/** | |
* Binds the FBO. | |
*/ | |
bind(type = this.gl.FRAMEBUFFER, multisampled = this.samples) { | |
this.gl.bindFramebuffer(type, multisampled ? this.multisampleFrameBuffer : this.frameBuffer) | |
} | |
/** | |
* Unbinds the FBO. | |
*/ | |
unbind(type = this.gl.FRAMEBUFFER) { | |
this.gl.bindFramebuffer(type, null) | |
} | |
/** | |
* Downsamples the FBO and its attachments to be readable via `blitFramebuffer`. Supports MRT. | |
*/ | |
blit() { | |
// blitFramebuffer can only copy the first color attachment to another FBO | |
// so we unbind FBO attachments and copy renderBuffers to textures one by one | |
// (See issue #12): https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_framebuffer_blit.txt | |
// Remove FBO attachments | |
for (let i = 0; i < this.count; i++) { | |
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.multisampleFrameBuffer) | |
this.gl.framebufferRenderbuffer(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0 + i, this.gl.RENDERBUFFER, null) | |
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.frameBuffer) | |
this.gl.framebufferTexture2D(this.gl.DRAW_FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0 + i, this.gl.TEXTURE_2D, null, 0) | |
} | |
// Blit multi-sampled renderBuffers to textures | |
this.gl.bindFramebuffer(this.gl.READ_FRAMEBUFFER, this.multisampleFrameBuffer) | |
this.gl.bindFramebuffer(this.gl.DRAW_FRAMEBUFFER, this.frameBuffer) | |
for (let i = 0; i < this.count; i++) { | |
const renderBuffer = this.renderBuffers[i] | |
this.gl.framebufferRenderbuffer( | |
this.gl.READ_FRAMEBUFFER, | |
this.gl.COLOR_ATTACHMENT0, | |
this.gl.RENDERBUFFER, | |
renderBuffer, | |
) | |
const texture = this.textures[i] | |
this.gl.framebufferTexture2D( | |
this.gl.DRAW_FRAMEBUFFER, | |
this.gl.COLOR_ATTACHMENT0, | |
this.gl.TEXTURE_2D, | |
texture, | |
0, | |
) | |
this.gl.blitFramebuffer( | |
0, | |
0, | |
this.width, | |
this.height, | |
0, | |
0, | |
this.width, | |
this.height, | |
this.gl.COLOR_BUFFER_BIT, | |
this.gl.NEAREST, | |
) | |
} | |
this.gl.bindFramebuffer(this.gl.READ_FRAMEBUFFER, null) | |
this.gl.bindFramebuffer(this.gl.DRAW_FRAMEBUFFER, null) | |
// Reconstruct FBO attachments | |
for (let i = 0; i < this.count; i++) { | |
const renderBuffer = this.renderBuffers[i] | |
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.multisampleFrameBuffer) | |
this.gl.framebufferRenderbuffer( | |
this.gl.FRAMEBUFFER, | |
this.gl.COLOR_ATTACHMENT0 + i, | |
this.gl.RENDERBUFFER, | |
renderBuffer, | |
) | |
const texture = this.textures[i] | |
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.frameBuffer) | |
this.gl.framebufferTexture2D( | |
this.gl.DRAW_FRAMEBUFFER, | |
this.gl.COLOR_ATTACHMENT0 + i, | |
this.gl.TEXTURE_2D, | |
texture, | |
0, | |
) | |
} | |
} | |
/** | |
* Disposes of the FBO from GPU memory. | |
*/ | |
dispose() { | |
this.gl.deleteFramebuffer(this.frameBuffer) | |
this.gl.deleteFramebuffer(this.multisampleFrameBuffer) | |
for (const renderBuffer of this.renderBuffers) this.gl.deleteRenderbuffer(renderBuffer) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment