renderer/WOITPass.tstypescript
import { Framebuffer } from "../webgl/Framebuffer";
import { weightedOITResolve } from "../shaders/weightedOITResolve";
import type { ITransparencyPass } from "./ITransparencyPass";
import type { Renderer } from "./Renderer";

export class WOITPass implements ITransparencyPass {
    protected _renderer: Renderer;
    protected _framebuffer: Framebuffer | null = null;

    constructor(renderer: Renderer) {
        this._renderer = renderer;
    }

    public init() {
        let h = this._renderer.handler;
        let initialWidth = h.canvas!.width;
        let initialHeight = h.canvas!.height;

        if (this._renderer.forwardFramebuffer) {
            initialWidth = this._renderer.forwardFramebuffer.width;
            initialHeight = this._renderer.forwardFramebuffer.height;
        }

        h.addProgram(weightedOITResolve());

        this._framebuffer = new Framebuffer(h, {
            width: initialWidth,
            height: initialHeight,
            targets: [
                {
                    internalFormat: "RGBA16F",
                    attachment: "COLOR_ATTACHMENT",
                    filter: "NEAREST"
                },
                {
                    internalFormat: "R16F",
                    attachment: "COLOR_ATTACHMENT",
                    filter: "NEAREST"
                }
            ],
            depthComponent: this._renderer.depthComponent,
            sharedDepthFramebuffer: this._renderer.getMSAA() == 0 ? this._renderer.forwardFramebuffer : null,
            useDepth: true
        });

        this._framebuffer.init();
    }

    public beginPass() {
        let gl = this._renderer.handler.gl!;

        if (!this._framebuffer!.sharedDepthFramebuffer) {
            this._framebuffer!.blitDepthFrom(this._renderer.forwardFramebuffer!);
        }

        this._framebuffer!.activate();
        gl.clearColor(0, 0, 0, 1);
        gl.clear(gl.COLOR_BUFFER_BIT);
    }

    public endPass() {
        this._framebuffer!.deactivate();
    }

    public resolve() {
        let r = this._renderer;
        let h = r.handler,
            gl = h.gl!,
            sh = h.programs.weightedOITResolve,
            p = sh;

        gl.disable(gl.DEPTH_TEST);
        gl.depthMask(false);

        // Output is premultiplied alpha
        r.enableBlendOneSrcAlpha();

        gl.bindBuffer(gl.ARRAY_BUFFER, r.screenFramePositionBuffer!);
        gl.vertexAttribPointer(p.attributes.corners, 2, gl.FLOAT, false, 0, 0);

        sh.activate();

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, this._framebuffer!.textures[0]);
        gl.uniform1i(p.uniforms.uAccumulate, 0);

        gl.activeTexture(gl.TEXTURE1);
        gl.bindTexture(gl.TEXTURE_2D, this._framebuffer!.textures[1]);
        gl.uniform1i(p.uniforms.uAccumulateAlpha, 1);

        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

        r.enableBlendDefault();
        gl.depthMask(true);
        gl.enable(gl.DEPTH_TEST);
    }

    public resize(width: number, height: number) {
        if (!this._framebuffer) return;
        this._framebuffer.setSize(width, height, true);
    }

    public dispose() {
        if (this._framebuffer) {
            this._framebuffer.destroy();
            this._framebuffer = null;
        }
        this._renderer.handler.removeProgram("weightedOITResolve");
    }
}