framebuffer2D.js

import { DataTexture2D } from "./dataTexture2D.js";
/**
 * Contains color and depth rendering target buffers for WebGL.
 * Essential for render-to-texture implementations.
 */
export class Framebuffer2D{
    /**
     * @param {Number} width width of the framebuffer in pixels
     * @param {Number} height height of the framebuffer in pixels
     * @param {Boolean} useColor wether to create buffer for color data (cannot be changed after creation)
     * @param {Boolean} useDepth wether to create buffer for depth data (cannot be changed after creation)
     */
    constructor( width, height, useColor, useDepth ){
        this.useColor = useColor || true;
        this.useDepth = useDepth || true;
        this.width = width;
        this.height = height;
        if(this.useColor){
            this.colorTexture = new DataTexture2D(null, this.width, this.height);
            this.colorTexture.wrapS = 'CLAMP_TO_EDGE';
            this.colorTexture.wrapT = 'CLAMP_TO_EDGE';
            this.colorTexture.magFilter = 'NEAREST';
        }
        this.depthBuffer = null;
        this.framebuffer = null;

        this.isInitialized = false;
        this.needsUpdate = true;
    }
    /**
     * @description initializes buffers. internal method, this should not be called manually!
     * @param {WebGLRenderingContext} gl gl context 
     */
    init(gl){
        if(this.useColor){
            this.colorTexture.init(gl);
            this.colorTexture.update(gl);
            this.framebuffer = gl.createFramebuffer();
            gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.colorTexture.texture, 0);
        }
        if(this.useDepth){
            this.depthBuffer = gl.createRenderbuffer();
            gl.bindRenderbuffer(gl.RENDERBUFFER, this.depthBuffer);
            gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);
            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthBuffer);
        }
        this.isInitialized = true;
    }
    /**
     * @description updates buffers. Automatically called by this.setActive() if needsUpdate == true
     * @param {WebGLRenderingContext} gl gl context
     */
    update(gl){
        if(this.useColor){
            this.colorTexture.width = this.width;
            this.colorTexture.height = this.height;
            this.colorTexture.update(gl);
        }
        if(this.useDepth){
            gl.bindRenderbuffer(gl.RENDERBUFFER, this.depthBuffer);
            gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);
        }
        this.needsUpdate = false;
    }
    /**
     * @description binds this framebuffer and sets up gl viewport. also initializes and/or updates buffers if necessary.
     * @param {WebGLRenderingContext} gl
     */
    setActive(gl){
        if(!this.isInitialized){this.init(gl);}
        if(this.needsUpdate){this.update(gl);}
        gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
        gl.viewport(0,0,this.width, this.height);
    }
}