|
| 1 | + |
| 2 | +Title: WebGL Framebuffers |
| 3 | +Description: What are framebuffers in WebGL? |
| 4 | +TOC: Framebuffers |
| 5 | + |
| 6 | +This article is meant to try to give you a mental image |
| 7 | +of what a framebuffer is in WebGL. Framebuffers come up |
| 8 | +as they allow you to [render to a texture](webgl-render-to-texture.html). |
| 9 | + |
| 10 | +A Framebuffer is just a *collection of attachments*. That's it! It is |
| 11 | +used to allow rendering to textures and renderbuffers. |
| 12 | + |
| 13 | +You can think of a Framebuffer object like this |
| 14 | + |
| 15 | +```js |
| 16 | +class Framebuffer { |
| 17 | + constructor() { |
| 18 | + this.attachments = new Map(); // attachments by attachment point |
| 19 | + } |
| 20 | +} |
| 21 | +``` |
| 22 | + |
| 23 | +And the `WebGLRenderingContext` (the `gl` object) like this |
| 24 | + |
| 25 | +```js |
| 26 | +// pseudo code |
| 27 | +gl = { |
| 28 | + framebuffer: defaultFramebufferForCanvas, |
| 29 | +} |
| 30 | +``` |
| 31 | + |
| 32 | +There are 2 binding points. They are set like this |
| 33 | + |
| 34 | +```js |
| 35 | +gl.bindFramebuffer(target, framebuffer) { |
| 36 | + framebuffer = framebuffer || defaultFramebufferForCanvas; // if null use canvas |
| 37 | + switch (target) { |
| 38 | + case: gl.FRAMEBUFFER: |
| 39 | + this.framebufferBinding = framebuffer; |
| 40 | + break; |
| 41 | + default: |
| 42 | + ... error ... |
| 43 | + } |
| 44 | +} |
| 45 | +``` |
| 46 | + |
| 47 | +You can add attachments to a framebuffer via 2 functions, `framebufferTexture2D`, |
| 48 | +and `framebufferRenderbuffer`. |
| 49 | + |
| 50 | +We can imagine their implementation to be something like |
| 51 | + |
| 52 | +```js |
| 53 | +// pseudo code |
| 54 | +gl._getFramebufferByTarget(target) { |
| 55 | + switch (target) { |
| 56 | + case gl.FRAMEBUFFER: |
| 57 | + return this.framebufferBinding; |
| 58 | + } |
| 59 | +} |
| 60 | +gl.framebufferTexture2D(target, attachmentPoint, texTarget, texture, mipLevel) { |
| 61 | + const framebuffer = this._getFramebufferByTarget(target); |
| 62 | + framebuffer.attachments.set(attachmentPoint, { |
| 63 | + texture, texTarget, mipLevel, |
| 64 | + }); |
| 65 | +} |
| 66 | +gl.framebufferRenderbuffer(target, attachmentPoint, renderbufferTarget, renderbuffer) { |
| 67 | + const framebuffer = this._getFramebufferByTarget(target); |
| 68 | + framebuffer.attachments.set(attachmentPoint, { |
| 69 | + renderbufferTarget, renderbuffer |
| 70 | + }); |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +If you have and enable the [`WEBGL_draw_buffers`](https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_buffers/) |
| 75 | +extension then a `Framebuffer` conceptually expands to this |
| 76 | + |
| 77 | +```js |
| 78 | +class Framebuffer { |
| 79 | + constructor() { |
| 80 | + this.attachments = new Map(); |
| 81 | ++ this.drawBuffers = [gl.COLOR_ATTACHMENT0, gl.NONE, gl.NONE, gl.NONE, ...]; |
| 82 | + } |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +You can set the drawing buffer array with `gl.drawBuffers` which we can |
| 87 | +imagine is implemented like this |
| 88 | + |
| 89 | +```js |
| 90 | +// pseudo code |
| 91 | +ext.drawBuffersWebGL(drawBuffers) { |
| 92 | + const framebuffer = gl._getFramebufferByTarget(gl.FRAMEBUFFER); |
| 93 | + for (let i = 0; i > maxDrawBuffers; ++i) { |
| 94 | + framebuffer.drawBuffers[i] = i < drawBuffers.length |
| 95 | + ? drawBuffers[i] |
| 96 | + : gl.NONE |
| 97 | + } |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +The important part is a *framebuffer* is just a simple collection of attachments. |
| 102 | +The complications are the restrictions on what those attachments |
| 103 | +can be and the combinations that work. For example a floating point texture |
| 104 | +attachment can not be rendered to by default. Extensions can enable that like |
| 105 | +`WEBGL_color_buffer_float`. Similarly if there is |
| 106 | +more than one attachment they must all be the same dimensions. |
0 commit comments