Home Reference Source

viewer/ssquad.js

/**
 * A Screen-space quadrangle used for deferred rendering. Currently only used for
 * Order Independent Transparency which is hard-coded here in the constructor.
 *
 * @class SSQuad
 */
export class SSQuad {

    constructor(gl) {
        this.gl = gl;

        let vao = this.vao = gl.createVertexArray();
        gl.bindVertexArray(vao);

        let positionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
            -1., +1.,   -1., -1.,   +1., -1.,
            -1., +1.,   +1., -1.,   +1., +1.
        ]), gl.STATIC_DRAW);
        gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(0);

        let vs_source = `#version 300 es
        layout(location=0) in vec4 vertexPosition;
        out vec2 uv;
        void main() {
            gl_Position = vertexPosition;
            uv = (vertexPosition.xy + 1.) / 2.;
        }`;

        let fs_source = `#version 300 es
        precision highp float;
        uniform sampler2D colorAccumulate;
        uniform sampler2D alphaAccumulate;
        in vec2 uv;
        out vec4 fragColor;
        void main() {
            float a = texture(alphaAccumulate, uv).r;
            vec4 accum = texture(colorAccumulate, uv);
            // pssst I'm just doing random stuff here
            fragColor = vec4(pow(accum.rgb / a, vec3(0.75, 0.75, 0.75)), clamp(accum.a, 0., 1.));
        }`;

        let vs = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vs, vs_source);
        gl.compileShader(vs);
        if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) {
            console.error(gl.getShaderInfoLog(vs));
        }
        let fs = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fs, fs_source);
        gl.compileShader(fs);
        if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
            console.error(gl.getShaderInfoLog(fs));
        }
        let p = this.program = gl.createProgram();
        gl.attachShader(p, vs);
        gl.attachShader(p, fs);
        gl.linkProgram(p);
        if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
            console.error(gl.getProgramInfoLog(p));
        }

        this.colorLocation = gl.getUniformLocation(p, "colorAccumulate");
        this.alphaLocation = gl.getUniformLocation(p, "alphaAccumulate");
    }

    draw(...args) {
        let gl = this.gl;

        gl.disable(gl.DEPTH_TEST);

        gl.activeTexture(gl.TEXTURE1);
        gl.bindTexture(gl.TEXTURE_2D, args[0]);
        gl.activeTexture(gl.TEXTURE2);
        gl.bindTexture(gl.TEXTURE_2D, args[1]);

        gl.useProgram(this.program);
        gl.uniform1i(this.colorLocation, 1);
        gl.uniform1i(this.alphaLocation, 2);

        gl.bindVertexArray(this.vao);
        gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
        gl.drawArrays(gl.TRIANGLES, 0, 6);
        gl.bindVertexArray(null);
    }
}