OpenGL vertex transformations

Every successive OpenGL transformation function transforms a coordinate system relative to prior transformation. These transformation functions are:


First, don't be confused by OpenGL programming paradigm of first calling glRotate()/glTranslate() (v.v.) before calling glVertex(). Also don't be confused by words "model" and "view" in modelview matrix -- just think of it as a matrix that maps a logical (object) coordinate system to eye coordinate system. Regardless of order of calling OpenGL functions, rendering pipeline resembles:

logical vertex --> modelview matrix --> eye coordinate system --> 2D screen

OpenGL has a notion of a pre-transformed coordinate system since arguments passed to glVertex*() are coordinates in that system. I'm going to refer to that system as logical coordinate system (other names such as world/virtual/object are applicable).

When submitting vertexs of a model via glVertex*(), model can be thought of as existing first in a logical pre-transformed coordinate system. Functions glTranslate() and glRotate() transform (map) logical coordinate system (along with logical vertexes you submitted) to eye coordinate system.

To illustrate, imagine two squares of slightly different sizes are rendered with logical coordinate system identity mapped to eye system:

 glVertex().. // large square
 glVertex().. // small square

  |                |
  |    +-----+     |
  |    | +-+ |     |
  |    | | | |     |
  |    | +-+ |     |
  |    +-----+     |
  |                |

OpenGL vertex transformation

(Remember eye coordinate system is fixed and actually exists in physical space.) Next, logical coordinate system is translated. Ie, logical coordinate system is moved relative to fixed eye coordinate system. Note that same values are submitted to glVertex in every case -- perceived movements/rotations are all due to glTranslate/glRotate.

 glVertex()..  // large square
 glTranslate() // move logical coordinate system relative to eye system
 glVertex()..  // small square

  |                |
  |    +-----+     |
  |    |     |+-+  |
  |    |     || |  |
  |    |     |+-+  |
  |    +-----+     |
  |                |

OpenGL vertex transformation

glRotate() is called instead of glTranslate which results in smaller square rotating:

 glVertex()..    // large square
 glRotate(45deg) // rotate logical coordinate system relative to eye system
 glVertex()..    // small square

  |               |
  |    +----+     |
  |    | /\ |     |
  |    | \/ |     |
  |    +----+     |
  |               |

OpenGL vertex transformation

Notice how submitted vertexes are transformed thru current state of modelview matrix. Also notice that by transforming modelview matrix and then submiting vertexes of a particular object, object animation (transformations around an object's coordinate system) can be done.

OpenGL ES 2 programmable pipeline

This describes how to draw a set of vectors and attributes
using OpenGL ES 2.0 fully-programmable pipeline paradigm.

First, understand that OpenGL ES 2.0 programmable pipeline is based on:
- user-defined attributes
- buffer objects

Also understand that:
- Optimization using indexs (ELEMENT_ARRAY) won't work
  because a vertex has different texture coordinates.

This new OpenGL ES 2.0 paradigm is very different from traditional OpenGL,
and is even different from shader-based full OpenGL versions.
Key to understanding is that user-defined attributes are now central.
Familar predefined attributes such as glVertex no longer apply.
App has to define its own attributes and use them on C/C++ and shader sides.

Note that OpenGL ES 2.0 is still burdened with some legacy,
and terminology is confusing and very badly named.

Beware that OpenGL man pages are erroneously out-of-date.
When in doubt, read OpenGL ES 2.0 specification contained in a PDF file.

Attributes (vertex attributes):
Traditional hardwired attributes of a vertex were: position, color, texture coordinates, normal vectors.
In OpenGL ES 2.0, attributes have become generalized and user-defined.
A vertex's position is an attribute (so in a way, a vertex itself is an attribute).

Attribute elements vs. arrays:
What is an attribute array?
How are attributes turned into arrays?

[Sudo-code description:]
Think of an attribute as a magic register that is assigned value of an element.
There is hardware around vertex shader that runs this loop before executing vertex shader:

attribute vec3 atr_vector;
for ( uint i = 0; i < attribCount; ++i )
    atb_position = attribs_position[i];
    atb_color    = attribs_color[i];

[A second description:]
Understand how an attribute for a vertex (its position) is defined in a vertex shader:
At vertex shader level, attribute defines one particular vector/vertex.
At a higher-level, an array of vectors is submitted by C/C++ program.
Transformation of an array of vectors/attributes to a single shader attribute
is done by glDrawElements().  See next section.

Buffer objects:
Buffer objects are a way to allocate memory on graphics card (server-side memory).
Its long name is "vertex buffer object" and abbreviated as "vbo".
But here term "buffer object" is preferred.

Advantage is of buffer objects is speed: geometry only needs to be uploaded once
(traditional OpenGL or OpenGL ES 1.0 inefficiently uploaded every frame).

Create buffer objects:
Begin by creating a "buffer object".

glGenBuffers( 1, &mBufferObjectVectors );

Then buffer objects must be bound:

glBindBuffer( GL_ARRAY_BUFFER, mBufferObjectVectors );

Newly bound buffer objects define empty storage: they contain nothing yet.
The acting of binding sets implied target for glBufferData() (see next section).

Uploading vertexs and vertex attributes to a buffer object:
Recall that examples of vertex attributes are texture coordinates, normal vectors, etc.
To upload vertexs and vertex attributes, call glBufferData().
glBufferData() uploads/copies from client-side to server-side memory.
Uploading only needs to be done once.

glBindBuffer( GL_ARRAY_BUFFER, mBufferObjectVectors );
glBufferData( GL_ARRAY_BUFFER, mVectors.size() * sizeof(fp) * 3, &mVectors[0], GL_STATIC_DRAW );

Enabling vertex attributes:
Then many buffer objects will be allocated.
When a particular geometry node will be drawn,
corresponding buffer objects need to be enabled for drawing.

Vertexs and vertex attributes are enabled using glEnableVertexAttribArray().
Notice OpenGL doesn't even distinguish between vertexs and vertex attributes in this context,
as glEnableVertexAttribArray() enables both.
Difference is how C/C++ app and user-defined shaders use them.

glVertexAttribPointer( defs::ATTRIB_VECTORS, 3, GL_FLOAT, GL_FALSE, 0, 0 );
glEnableVertexAttribArray( defs::ATTRIBS_VECTORS );

Purpose of glVertexAttribPointer() is to define array.
Purpose of glEnableVertexAttribArray() is to enable using an attribute for a vertex array.

These code snippets reveal a fact: attributes are user-defined.
Notice defs::ATTRIBS_* are defined as C++ constants.
Later they will be used by shaders.

They can be addressed by "pointer" parameter to glVertexAttribPointer().
Here they are referred in C++ names as "offsets":

glBindBuffer( mOglBufferObject );
glVertexAttribPointer( defs::ATTRIBS_VECTORS, 3, GL_FLOAT, GL_FALSE, 0, mBufferObjectOffsetVectors );
glVertexAttribPointer( defs::ATTRIBS_VECTORS, 3, GL_FLOAT, GL_FALSE, 0, mBufferObjectOffsetNormalVectors );

Drawing with attribute arrays:
Recall that original purpose of glDrawArrays() was to draw a set of primitives
using enabled client-side "vertex arrays".  This paradigmn was extended in
programmable-pipeline to draw using enabled server-side "vertex attributes".
Combination of glVertexAttribPointer() and glDrawArrays()
can be used to select attribute arrays and draw them as one batch.

Original function glVertexPointer() had a C pointer to client-side memory.
New function glVerterAttribPoiter() also has a C pointer but
in this mode is redefined to be an offset within a server-side "buffer object"
(confirmed by OpenGL ES 2.0 PDF spec).

void glVertexAttribPointer( GLuint index,
                            GLint size,
                            GLenum type,
                            GLboolean normalized,
                            GLsizei stride,
                            const GLvoid * pointer);

void glDrawElements( GLenum mode, GLsizei count, GLenum type, GLsizeiptr indices );

One or multiple buffer objects:
Multiple attribute arrays can be stored in same buffer object.
This is the more efficient way.

Or a buffer object can be allocated for one attribute array.
This is more flexible and perhaps easier to implement in a scene-graph.

Drawing using one buffer object:
glVertexAttribPointer( defs::ATTRIB_POSITION, ..., mBufferObjectOffset_position );
glEnableVertexAttribArray( defs::ATTRIB_POSITION );

glVertexAttribPointer( defs::ATTRIB_COLOR,    ..., mBufferObjectOffset_color );
glEnableVertexAttribArray( defs::ATTRIB_COLOR );

glDrawArrays( ... );

Drawing using multiple buffer objects:
Function glBindBuffer() makes a buffer object current,
which then affects/defines source used by glVertexAttribPointer().
Offset will be 0 for each.

glBindBuffer( GL_ARRAY_BUFFER, mVectorNode->OglGetBufferObject() );
glVertexAttribPointer( defs::ATTRIB_POSITION, ..., 0/*offset*/ );
glEnableVertexAttribArray( defs::ATTRIB_POSITION );

glBindBuffer( GL_ARRAY_BUFFER, mColorNode->OglGetBufferObject() );
glVertexAttribPointer( defs::ATTRIB_COLOR,    ..., 0/*offset*/ );
glEnableVertexAttribArray( defs::ATTRIB_COLOR );

glDrawArrays( ... );

Vertex shader:
// Vertex shader:
uniform mat4   uni_mvp_matrix;      // modelviewMatrix * projectionMatrix
attribute vec3 atr_vector;          // input vector/vertex
attribute vec3 atr_normalVector;    // input normal vector
void main()
    gl_Position       = uni_mvp_matrix * atr_vector;
    vec3 normalVector = uni_mvp_matrix * atr_normalVector;

OpenGL ES 3

(OpenGL has become a hack with a vocabulary of confusing misnomers.)

In new OpenGL programming paradigm, task of CPU program
is to send data and parameters to shader programs running on GPU.

First understand there's really no such thing as a "vertex array" nor even a "vertex".
Instead, there are arrays of (vertex) attributes.
Word "vertex" is nothing more than an adjective for word "attribute".
Attributes are what matters -- they are inputs to vertex shader.
Attributes include position, normals, texture coordinates, etc.
So now 3D position is an attribute (not a vertex).
Attributes are user-defined -- their purpose is defined by
shader that user is responsible for writing.

CPU program sends arrays of vertex attributes to a shader.
In C++ terms, sending a vertex attributes is like passing an array of structs,
in which each member of struct is an enabled attribute.

Objects involved in drawing:

- vertex shader
- fragment shader
- textures
- uniforms
- buffers
- vertex attributes
- VBOs
- VAOs

OGL misnomer translation:

"vertex array" --> "attribute array" or "array of shader input parameters".

"vertex attribute" --> Means associations of a vertex: 3D position, normals, texture coordinates, colors, etc.
                       in context of a shader, means shader inputs.

"attributes" --> Either synonym of "vertex array" or shader inputs.

"vertex array object (VAO)" --> "struct of bindings".

"vertex buffer object (VBO)" --> "graphics buffer" (buffer allocated in graphics memory).

"vertex attrib pointer" --> Either pointer (void*) to a vertex array in CPU memory,
                            or an offset of vertex array in a graphics buffer.

"element" --> This means an "index"!  Indeed!
              Full phrase is "element index".

Example of the most basic drawing:

  glVertexAttribPointer( idVA, ..., vectors );  // 3D position vectors are actualy "attributes"
  glEnableVertexAttribArray( idVA );            // enable sending this attrib array to shader
  glDrawArrays( GL_TRIANGLES, ... );            // send all enabled attrib arrays to shader

This is an iterative operation.
On each iteration, one element from each of enabled attrib arrays will be sent to vertex shader.
Attributes can be thought of GPU registers which are automagically loaded with elements of attrib arrays.

vertex array, vertex attrib pointers, glVertexAttribPointer():

Vertex attributes such as 3D position, normals, texture coordinates, etc,
originate as C arrays in CPU memory.

glVertexAttribPointer() has two modes.  Its last arg is void* ptr.
Either it loads a vertex array from CPU memory,
or if a graphics buffer is bound (GL_ARRAY_BUFFER),
ptr turns into an offset into a vertex array stored in a graphics buffer.

For speed, before rendering, allocate a graphics buffer containing a vertex array,
then at every draw iteration, bind to graphics buffer [glBindBuffer()].

Either way, a vertex array will be sent to shader by glDraw*().
A vertex array must be enabled by calling glEnableVertexAttribArray().

For more speed, use VAOs to eliminate calls to glVertexAttribPointer(), glEnableVertexAttribArray(), glBindBuffer()
in draw loop.


A VBO (Vertex Buffer Object) is a buffer allocated in graphics memory.

A vertex array can be pre-loaded from CPU memory into a VBO.
Afterwards, draw loop only needs to bind to VBO
(that tells shader where to directly fetch vertex attributes from).

So VBOs are a improvement over older OGL which required always uploading vertexs from CPU to graphics memory.

For drawing primitives, VBOs can hold either vertex arrays or indexs to vertexs.
A VBO created with GL_ARRAY_BUFFER contains a vertex array (array of attributes).
A VBO created with GL_ELEMENT_ARRAY_BUFFER contains indexs ("element", another horrible misnomer, means "index"!)
(VBOs can be used to contain other types of data but this discussion is for drawing).

Example code:

glGenBuffers( 2, &amp;mVBOs[0] );

// Allocate a graphics buffer, load it with 3D vectors.
glBufferBind( mVBOs[0] );
glBufferData( GL_ARRAY_BUFFER, vectors.size() * 3 * sizeof(fp), &amp;vectors[0], GL_STATIC_DRAW );

// Allocate a graphics buffer of indexs ("element indices") that reference first VBO:
glBufferBind( mVBOs[1] );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, vectorIndexs.size() * sizeof(GLint), &amp;vectorIndexs[0], GL_STATIC_DRAW );

OGL will know to uses indexes with a VBO created with GL_ELEMENT_ARRAY_BUFFER is bound/active.


("Vertex Array Object" is one of the worst misnomers ever.)
(Just use mnemonic "VAO" and think of it as a parameter struct.)

A VAO is an implicit struct of bindings stored in graphics memory.
When a VAO is bound, it remembers parameters set by OGL calls to set vertex array (VA) state.
Thereafter, all of those parameters can be made current by binding VAO.
So it's an optimization to reduce calling OGL functions by calling them once:

OGLES3 book: "Each VAO contains a full state vector that
             describes all of vertex buffer bindings and vertex client state enables.
             After binding, subsequent calls that change vertex array state
             glBindBuffer, glVertexAttribPoint, glEnableVertexAttribArray will the VAO."

// Create VAO and load parameters into it.
glGenVertexArray();   // horrible misnomer

glBindBuffer();               // implict VAO parameter
glBindBuffer();               // implict VAO parameter
glVertexAttribPointer();      // implict VAO parameter
glEnableVertexAttribArray();  // implict VAO parameter

These gl*() calls can be thought of as implicit assignments to a hidden VAO struct:

VAO.buffer[0] = ...;
VAO.buffer[1] = ...;
VAO.vaPtr[0] = ...;
VAO.vaEnable[0] = true;

    // Binding VAO again reloads parameters so those functions don't need to be called anymore.

Efficient drawing --however--:

Fastest general drawing technique is "indexed-drawing":
- Use VBOs to allocate attributes in graphics memory.
- Use VAOs to reapply bindings.
- Use index-drawing.  Call glDrawElements().

Indexed-drawing is based on a graphics buffer of indexs to vertexs,
which avoids repeating vertexs as most vertexs as shared by triangles.
GPUs have a "vertex cache" that saves transformation,
using an index to lookup a transformed vertex.

However, what prevents using indexed-drawing is when same vector has different normal vectors.
Different normal vectors are produced by Blender for "smooth shading".
In this case, non-indexed glDrawArrays() must be used.