Blog of roxlu, co-founder of Apollo Media. Contact info[shift+2]apollomedia.nl.

Attributeless Vertex Shader with OpenGL

When doing fullscreen shader passes with openGL I often use a simple technique called "Attribute-less rendering" as described by Robert on his blog. In short this means you store the vertices that you want to render in your vertex shader using a const vec2 array and use the gl_VertexID variable to access the correct element from this array. Great thing about using this technique is that you don't need to setup an VBO or setup the offsets for a Vertex Array Object (VAO).

Recently I want to use this technique to draw simple 2D textures for my Video Capture library. Though I had to find a way to specify the (x,y) offsets and the dimensions where I wanted to draw the texture.

To solve this, I added a vec4 u_pos uniform that holds the offsets in the (x,y) elements and the scale in the (z,w) elements. The values of this u_pos uniform are percentages for of the current viewport dimensions.

Lets say the viewport is 800 x 600. I store a inv_win_w and inv_win_h that I use to convert the x/y/w/h values to percentages:

void CaptureGL::resize(int winW, int winH) {
     inv_win_w = 1.0 / winW;
     inv_win_h = 1.0 / winH;
}

Then whenever you want to draw someting in 2D at a specific location and dimension, I set the vec4 u_pos uniform values like:

void CaptureGL::draw(int x, int y, int w, int h) {
 
     GLint u_pos = glGetUniformLocation(prog, "u_pos"); // NOTE: do this somewhere in your constructor and keep a member
     glUniform4f(u_pos,
        x * inv_win_w, 
        y * inv_win_h,
        w * inv_win_w, 
        h * inv_win_h
     );
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

The shaders that backs up this technique is pasted below. We first offset the vertex position using the u_pos.x and u_pos.y after changing the values to Normalized Device Context (NDC) ranges (-1, 1). Then we apply the scaled vertex position.

/*
 
 u_pos.x = X position in percentage of viewport 0-1. 0% means -1.0
 u_pos.y = Y position in percentage of viewport 0-1. 0% means -1.0
 u_pos.z = WIDTH scale in percentage of viewport. 
 u_pos.w = HEIGHT scale in percentage of viewport. 
 
 */
 
static const char* CAPTURE_GL_VS = ""
  "#version 330\n"
  "uniform vec4 u_pos; " 
  ""
  "const vec2 pos[] = vec2[4]("
  "  vec2(0.0, 1.0), "
  "  vec2(0.0, 0.0), "
  "  vec2(1.0, 1.0), "
  "  vec2(1.0, 0.0)  "
  ");"
  ""
  " const vec2[] tex = vec2[4]( "
  "   vec2(0.0, 0.0), "
  "   vec2(0.0, 1.0), "
  "   vec2(1.0, 0.0), "
  "   vec2(1.0, 1.0) "
  ");"
  ""
  "out vec2 v_texcoord; "
  ""
  "void main() { "
  "  vec2 p = pos[gl_VertexID]; " 
  "  vec2 offset = vec2(u_pos.x * 2.0 - 1.0, u_pos.y * 2.0 - 1.0); "
  "  vec2 scale  = vec2(u_pos.z * p.x * 2.0, u_pos.w * p.y * 2.0); " 
  "  gl_Position = vec4(offset.x + scale.x, "
  "                     offset.y + scale.y, "
  "                     0.0, 1.0);"
  "  v_texcoord = tex[gl_VertexID];"
  "}"
  "";