Skip to content

Instantly share code, notes, and snippets.

@CodyJasonBennett
Last active May 9, 2022 18:07
Show Gist options
  • Save CodyJasonBennett/edbc4372d3c9a921ebc3e46cb5899ba8 to your computer and use it in GitHub Desktop.
Save CodyJasonBennett/edbc4372d3c9a921ebc3e46cb5899ba8 to your computer and use it in GitHub Desktop.
WebGL 2 FBO w/Multisampled MRT
/**
* 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