Rendering a fullscreen quad using WebGL
Asked Answered
A

2

5

I have a framebuffer to which I rendered my scene and now I want to render this to a "fullscreen" quad. How can I set my camera and what should I put in my vertex shader in order to render the framebuffer's texture to the whole screen.

I've tried creating a fullscreen quad like this

var gl = this.gl;
var quad_vertex_buffer = gl.createBuffer();
var quad_vertex_buffer_data = new Float32Array([ 
    -1.0, -1.0, 0.0,
     1.0, -1.0, 0.0,
    -1.0,  1.0, 0.0,
    -1.0,  1.0, 0.0,
     1.0, -1.0, 0.0,
     1.0,  1.0, 0.0]);
gl.bufferData(quad_vertex_buffer, quad_vertex_buffer_data, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, quad_vertex_buffer);
gl.vertexAttribPointer(this.shaderProgram.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
//gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
gl.drawArrays(gl.TRIANGLES,0, 6);

but it still renders everything black. Any ideeas or examples/tutorials I can follow?

Alkalosis answered 8/6, 2014 at 9:54 Comment(0)
Y
5

It is really not a big deal, once you get how to use Vertex Buffers and shaders. Then you can easily write an utility function to do it. Here is one I normally use, if you are looking for a reference:

drawFullScreenQuad : function(shaderProgram) {

    if (!shaderProgram)
    {
        utils.warning("Missing the shader program!");
        return;
    }

    // Only created once
    if (this.screenQuadVBO == null)
    {
        var verts = [
            // First triangle:
             1.0,  1.0,
            -1.0,  1.0,
            -1.0, -1.0,
            // Second triangle:
            -1.0, -1.0,
             1.0, -1.0,
             1.0,  1.0
        ];
        this.screenQuadVBO = this.gl.createBuffer();
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.screenQuadVBO);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(verts), this.gl.STATIC_DRAW);
    }

    // Bind:
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.screenQuadVBO);
    this.gl.enableVertexAttribArray(shaderProgram.vertexAttributes.vertexPositionNDC);
    this.gl.vertexAttribPointer(shaderProgram.vertexAttributes.vertexPositionNDC, 2, this.gl.FLOAT, false, 0, 0);

    // Draw 6 vertexes => 2 triangles:
    this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);

    // Cleanup:
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
},

Then you can go fancy like I did and compute the texture coordinates on-the-fly in the vertex shader:

Vertex Shader:

precision lowp float;

// xy = vertex position in normalized device coordinates ([-1,+1] range).
attribute vec2 vertexPositionNDC;

varying vec2 vTexCoords;

const vec2 scale = vec2(0.5, 0.5);

void main()
{
    vTexCoords  = vertexPositionNDC * scale + scale; // scale vertex attribute to [0,1] range
    gl_Position = vec4(vertexPositionNDC, 0.0, 1.0);
}

Fragment Shader:

precision mediump float;

uniform sampler2D colorMap;
varying vec2 vTexCoords;

void main()
{
    gl_FragColor = texture2D(colorMap, vTexCoords);
}

The important point to note are the vertexes in Normalized Device Coordinates (NDC), so you just pass the vertexes in the [-1,1] range and forward them directly to gl_Position without the need to multiply by a projection matrix.

Yesman answered 9/6, 2014 at 1:48 Comment(5)
I'm getting a Error: program.vertexAttributes is undefined on this.gl.enableVertexAttribArray(shaderProgram.vertexAttributes.vertexPositionNDC); what did i forget?Phrensy
@JeroenvanLangen, shaderProgram is a user defined type, not a WebGL builtin, so you need to define your own. That code snipped is not complete, it is just for demonstration.Yesman
a, tnx. I'm just started to checkout the webgl. i did some dx11, but it's still a change..Phrensy
FYI: NDC is after the perspective divide (see GL spec). Did you mean clip space coordinates?Betel
@gman, yes you are correct. I always used to get the terminology wrong. That is indeed clip space, not ndc.Yesman
B
5

Why do you need a camera to render a fullscreen quad? Rendering a fullscreen quad is pretty much the simplest thing you can do in WebGL. Given the buffer you already setup just use a shader like this

vertex shader:

attribute vec4 v_position;

void main() {
  gl_Position = v_position;
}     

fragment shader:

precision mediump float;

void main() {
   gl_FragColor = vec4(0,1,0,1); // green
}

You should get a green screen.

There's a few bugs in the code though. You need to bind the buffer before you try up put data in it. You need to reference the buffer through the bind point, not the buffer object.

old (incorrect)

gl.bufferData(quad_vertex_buffer, quad_vertex_buffer_data, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, quad_vertex_buffer);

new (correct)

gl.bindBuffer(gl.ARRAY_BUFFER, quad_vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, quad_vertex_buffer_data, gl.STATIC_DRAW);

Example:

const vs = `
    attribute vec4 v_position;

    void main() {
      gl_Position = v_position;
    }     
`;

const fs = `
    precision mediump float;

    void main() {
       gl_FragColor = vec4(0,1,0,1); // green
    }
`;

var gl = document.querySelector("canvas").getContext("webgl");
var shader_program = twgl.createProgram(gl, [vs, fs]);
gl.useProgram(shader_program);
var vertexPositionAttribute = gl.getAttribLocation(shader_program, "v_position");
var quad_vertex_buffer = gl.createBuffer();
var quad_vertex_buffer_data = new Float32Array([ 
    -1.0, -1.0, 0.0,
     1.0, -1.0, 0.0,
    -1.0,  1.0, 0.0,
    -1.0,  1.0, 0.0,
     1.0, -1.0, 0.0,
     1.0,  1.0, 0.0]);
gl.bindBuffer(gl.ARRAY_BUFFER, quad_vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, quad_vertex_buffer_data, gl.STATIC_DRAW);
gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vertexPositionAttribute)
gl.drawArrays(gl.TRIANGLES, 0, 6);
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>

Another example with a simple pattern to show it's working

const vs = `
    attribute vec4 v_position;

    void main() {
      gl_Position = v_position;
    }     
`;

const fs = `
    precision mediump float;

    void main() {
       gl_FragColor = vec4(fract(gl_FragCoord.xy / vec2(16., 32.)),0,1); 
    }
`;

var gl = document.querySelector("canvas").getContext("webgl");
var shader_program = twgl.createProgram(gl, [vs, fs]);
gl.useProgram(shader_program);
var vertexPositionAttribute = gl.getAttribLocation(shader_program, "v_position");
var quad_vertex_buffer = gl.createBuffer();
var quad_vertex_buffer_data = new Float32Array([ 
    -1.0, -1.0, 0.0,
     1.0, -1.0, 0.0,
    -1.0,  1.0, 0.0,
    -1.0,  1.0, 0.0,
     1.0, -1.0, 0.0,
     1.0,  1.0, 0.0]);
gl.bindBuffer(gl.ARRAY_BUFFER, quad_vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, quad_vertex_buffer_data, gl.STATIC_DRAW);
gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vertexPositionAttribute)
gl.drawArrays(gl.TRIANGLES, 0, 6);
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>

I'd suggest you read some WebGL tutorials.

Betel answered 9/6, 2014 at 1:29 Comment(0)
Y
5

It is really not a big deal, once you get how to use Vertex Buffers and shaders. Then you can easily write an utility function to do it. Here is one I normally use, if you are looking for a reference:

drawFullScreenQuad : function(shaderProgram) {

    if (!shaderProgram)
    {
        utils.warning("Missing the shader program!");
        return;
    }

    // Only created once
    if (this.screenQuadVBO == null)
    {
        var verts = [
            // First triangle:
             1.0,  1.0,
            -1.0,  1.0,
            -1.0, -1.0,
            // Second triangle:
            -1.0, -1.0,
             1.0, -1.0,
             1.0,  1.0
        ];
        this.screenQuadVBO = this.gl.createBuffer();
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.screenQuadVBO);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(verts), this.gl.STATIC_DRAW);
    }

    // Bind:
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.screenQuadVBO);
    this.gl.enableVertexAttribArray(shaderProgram.vertexAttributes.vertexPositionNDC);
    this.gl.vertexAttribPointer(shaderProgram.vertexAttributes.vertexPositionNDC, 2, this.gl.FLOAT, false, 0, 0);

    // Draw 6 vertexes => 2 triangles:
    this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);

    // Cleanup:
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
},

Then you can go fancy like I did and compute the texture coordinates on-the-fly in the vertex shader:

Vertex Shader:

precision lowp float;

// xy = vertex position in normalized device coordinates ([-1,+1] range).
attribute vec2 vertexPositionNDC;

varying vec2 vTexCoords;

const vec2 scale = vec2(0.5, 0.5);

void main()
{
    vTexCoords  = vertexPositionNDC * scale + scale; // scale vertex attribute to [0,1] range
    gl_Position = vec4(vertexPositionNDC, 0.0, 1.0);
}

Fragment Shader:

precision mediump float;

uniform sampler2D colorMap;
varying vec2 vTexCoords;

void main()
{
    gl_FragColor = texture2D(colorMap, vTexCoords);
}

The important point to note are the vertexes in Normalized Device Coordinates (NDC), so you just pass the vertexes in the [-1,1] range and forward them directly to gl_Position without the need to multiply by a projection matrix.

Yesman answered 9/6, 2014 at 1:48 Comment(5)
I'm getting a Error: program.vertexAttributes is undefined on this.gl.enableVertexAttribArray(shaderProgram.vertexAttributes.vertexPositionNDC); what did i forget?Phrensy
@JeroenvanLangen, shaderProgram is a user defined type, not a WebGL builtin, so you need to define your own. That code snipped is not complete, it is just for demonstration.Yesman
a, tnx. I'm just started to checkout the webgl. i did some dx11, but it's still a change..Phrensy
FYI: NDC is after the perspective divide (see GL spec). Did you mean clip space coordinates?Betel
@gman, yes you are correct. I always used to get the terminology wrong. That is indeed clip space, not ndc.Yesman

© 2022 - 2024 — McMap. All rights reserved.