WebGL: Drawing 2D Image
Related Topics: Render To Texture, Sprite, Vertex Transformation
Overview

Sometimes you want to draw a 2D image on WebGL screen, for example draw the 3D scene to a frame buffer first, then modify the rendered scene for post processing, finally re-draw the processed framebuffer image back to the screen as a 2D image.
You need to define a rectangle to hold the image bitmap and the dimension of the rectangle, so it can fill the screen relative to the screen viewport. Note that WebGL uses right-handed coordinate system for the window coordinates. The vertical direction, Y-axis is bottom-to-top, not top-to-bottom.
The size of the image rectangle is determined by 4 corners; left, right, bottom and top values. To cover the entire window with an image, the dimension of the image rectangle should be same as the window dimension. For example, if the window size is 100x200, then the 4 edge values (left, right, bottom, top) are 0, 100, 0, 200 respectively.
Quad Class
Similar to Sprite, Quad class is to construct a rectangle to render 2D texture on the screen. There are 2 distinct differences in Quad class. Quad class takes 4 corner values (left, right, bottom, top) to size the rectangle, and it uses only 2D coordinates (x, y) for each vertex. But Sprite uses 3D coordinates.
Quad class provides reverseTextureOrientation() to flip the vertical texture orientation because WebGL is bottom-to-top orientation. It also has a built-in VBO and draw() function to render the quad using the VBO. The following code snippet is to fill an image to the entire screen using Quad class.
// global object
gl = {};
...
// load texture
gl.tex0 = loadTexture(gl, "grid512.png");
// assume the window dim is w x h
let w = ...; // viewport width
let h = .... // viewport height
// create quad and resize it to fill entire window
gl.quad = new quad(gl); // create a quad with default size
gl.quad.set(0, w, 0, h); // left, right, bottom, top
...
// draw quad
gl.bindTexture(gl.TEXTURE_2D, gl.tex0);
gl.quad.draw();
...
Shader for Quad
To draw 2D image in WebGL, we traditionally use an orthographic projection matrix to transform vertices. By multiplying Model, View and Projection matrices for each vertex, the vertex will transform from the object space to the clip space.

However, with a special vertex shader, we can completely omit these transform matrices and matrix computation, and directly provide the normalized clip coordinate of each vertex, ranging between [-1, 1]. This vertex position in this vertex shaderwill become a NDC by dividing by w=1 later. Here is the vertex shader for the 2D image rectangle.
// Vertex Shader for 2D image
// input vertex attribs
attribute vec2 vertexPosition; // (x, y)
attribute vec2 vertexTexCoord0;
// uniforms
uniform vec2 screenDimension; // (w, h)
// output varying variables
varying vec2 texCoord0;
void main(void)
{
// texture coords
texCoord0 = vertexTexCoord0;
// normalized vertex position [-1, 1]
vec2 normPosition = (vertexPosition / screenDimension) * 2.0 - 1.0;
gl_Position = vec4(normPosition, 0.0, 1.0);
}
You may use different Z values for gl_Position in the vertex shader, instead of 0.0. if z=-1.0, the vertex position is at the near plane. if z=1.0, the vertex is at the far plane, because the z value of NDC frustum is also from -1 to 1. If it is 0, then the image rectangle is positioned at the exact middle of NDC frustum.
Example: Drawing Image
This example draws a 2D image using Quad class, which fit in the screen viewport by keeping the image's aspect ratio. Please see resizeQuad() function how to scale the image to fit the screen.
Fullscreen Demo: test_image.html
GitHub Repo: test_image.html, test_image_min.html