Note
I know plenty about 3d geometry as a concept, but I am just getting around to learning OpenGL and it is a tricky moving target. Since I have noticed that 1000s of novices are all desperate for a magical resource to go from game player to game creator, I wanted to explicitly say that this is not it. At this point, these notes are disorganized, reflect no coherent strategy, and are probably quite wrong in many places. These are personal notes; if they help you great, but don’t have high expectations.

Contents

Resources

Note
I have a separate document for notes about GLSL.

Reference

Here is the official opengl.org specification for

(Take off the xhtml part of the path for the poorly formated frame based version.)

Tutorials And Resources For Learning

Here are some possibly helpful resources for learning about OpenGL.

  • OpenGLBook.com - This has been one of my favorite resources so far. It covers modern OpenGL but with a minimum of fuss and magical libraries. It has a nice frame speed clock mechanism on the window’s title bar that is interesting. The only downside is that the project is off to a good start but stops about 25% into the objective. Still a good tutorial to start with. Shows a lot of effective glut tricks.

  • Anton’s OpenGL notes are very high quality and extensive. There are some nice diagrams and the pace seems right. Uses glfw instead of glut which might not be a bad idea both for initial simplicity and ultimate target platform simplicity. Good explanations throughout. Covers quite a few advanced and involved topics such as sprite sheets, font generation, particle systems, geometry shaders. Not only a great resource, but Anton is a cool guy too.

  • swiftless.com has some interesting tutorials and examples. Although for an obnoxious proprietary OS, the concepts are cross platform. I found the material solid but a bit jumpy and specific to the examples. This is fine if you can follow along in the same development environment, harder for me.

  • A pretty useful wiki about OpenGL topics.

  • NeHe has a nice set of tutorials with a lot of sample code (48+ sections) which has all been ported to a very wide variety of specific platforms (chose GLUT for the Linux one). Note that these seem to be pretty focused on old fixed pipeline techniques but there is some shader stuff.

  • Lazy Foo’s tutorials, with a nice perspective on what the "modern" way is all about and how to do the "legacy" way. Focus mostly on 2d stuff including textures and custom font creation.

  • OpenGL.org. Actually the entire wiki has great stuff throughout.

  • OpenGL Development On Linux by Etay. Modern OpenGL. Lots of effects and game oriented techniques. This used to advertise something about OpenGL for Linux and then immediately said "Download Windows source". But I pointed that out and now it is less assertive about the Linux experience though it uses glut and should be ok for Linux. I think it’s got one of the better simple examples of using VBOs to create the data. The source in the write up is not complete and the downloaded complete code can be a chore to simplify from the IDE generated hairball supplied. In all, a good resource even if a bit frustrating at times.

  • Another on-line tutorial, this one says it specializes in OpenGL v3.3 and later. It’s a little bit mired in an unappealing proprietary IDE, but the ideas are worth reading and can help. Even not being able to create the exact code the author envisions can be helpful but other times it can be frustrating as in: "If you really want to know more about VAOs, there are a few other tutorials out there, but this is not very important." (From tutorial 2.) So I can’t recreate the working executable and I can’t really get a clear explanation of VAOs. :-( Still, it’s nicely done and will help many and is worth looking at.

  • Learning Modern 3d Graphics Programming is another nicely put together tutorial. This one has good explanations and is worth a read through to get an understanding. However, the code examples were not necessarily complete (just the explanatory highlights) or easily found in one place. More of an OpenGL guide than a follow-along tutorial but quite good.

  • Nice explanation of important things to consider for portability.

  • A very excellent site for looking at examples of GLSL. And another great one is shadertoy.

  • Sphere geometry? An excellent article.

Here are some resources related to OpenGL.

  • OpenGL Mathematics, a vector math library for use with OpenGL (whose vector math functions are, apparently, deprecated).

Acronyms And Jargon

The world of OpenGL is filled with cryptic acronyms. Here’s what they mean.

GLX

"OpenGL Extension to the X Window System". Provides the interface connecting OpenGL and the X Window System. The complete and proper documentation can be found in this annoying PDF.

GLUT

OpenGL Utility Toolkit.

GLFW

This library is a cross platform, mostly free way to open GL contexts. It is more to the point than GLUT and seems more focused. Hard to say what it stands for. "GL frame work"? "GL for wimps"? "Gorilla lips feel wet"?

GLEW

OpenGL Extension Wrangler Library.

GLSL

OpenGL Shading Language

SDL

Simple DirectMedia Layer

OpenGL ES

Embedded Systems - A stripped down/optimized OpenGL specification used on most hand held things, the Raspberry Pi, the PS3, etc.

Vulkan

A.k.a. "glNext". An effort to unify OpenGL and OpenGL ES into one common API. More info here.

GLee

GL Easy Extension library

ARB

This cryptic acronym can be found on the front of many functions. It stands for "Architecture Review Board" and is part of the naming of provisional functionality. You can read more about this here.

TSG

Technical Sub-Group (not related to submersible watercraft).

VAO

Vertex Array Object

VBO

Vertex Buffer Object

IBO

Index buffer object.

ADS

Ambient, Diffuse, Specular. A lighting model.

DevIL

Developer’s Image Library, a "full-featured cross-platform image library". Used for loading textures and other such tasks.

DSA

Direct State Access

How To Determine OpenGL Environment

Sometimes you need to figure out what sort of OpenGL features are available to you. Without getting into specifics, here are some things to start with.

$ glxinfo | grep -i open
$ grep -i gl /var/log/Xorg.0.log

This can be very maddening trying to do remotely over SSH. This is the best I’ve been able to come up with:

    sudo -u `ps -ef | grep '[g]nome-keyring-daemon.*login' | awk '{print
$1}'` glxinfo -display :0 | sed -n '1,/Visuals/p' | less

This terrible hack on CentOS and Ubuntu at this time (2013) if someone else is logged in.

Fancier Diagnostics

There are tons of properties and extents that can be checked. Look at this to get an idea of what I mean. Eventually it might be good to have a diagnostic program that collected this data from a system.

A Quick Python Example

This simple example (inspired by this tutorial) will create an OpenGL window and show a rotating cube. A lot can be done with this!

#!/usr/bin/python
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *

def Cube(): # Put together the cube geometry.
    # Vertices and edges.
    vs= ((1,-1,-1),(1,1,-1),(-1,1,-1),(-1,-1,-1),(1,-1,1),(1,1,1),(-1,-1,1),(-1,1,1))
    es= ((0,1),(0,3),(0,4),(2,1),(2,3),(2,7),(6,3),(6,4),(6,7),(5,1),(5,4),(5,7))
    glBegin(GL_LINES) # Line rendering mode.
    for e in es:
        for v in e:
            glVertex3fv(vs[v])
    glEnd()

def main():
    pygame.init()
    display= (800,600)
    pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
    gluPerspective(45, (display[0]/display[1]), 0.1, 50.0) # FoV,aspect,clip_near,clip_far
    glTranslatef(0.0,0.0,-5) # Where to put the camera.
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
        glRotate(5,15,5,5)
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        Cube() # Resubmit the cube geometry.
        pygame.display.flip() # Swap out double buffer.
        pygame.time.wait(10) # Time seems to be in milliseconds.
main()

GLUT

Looks like Linux people should be concentrating on Freeglut. The authentic GLUT seems encumbered by some questionable (and certainly mysterious) licensing issues.

"GLUT is a window system independent toolkit for writing OpenGL programs, implementing a simple windowing API, which makes learning about and exploring OpenGL programming very easy. GLUT is designed for constructing small to medium sized OpenGL programs, however it is not a full-featured toolkit, so large applications requiring sophisticated user interfaces are better off using native window system toolkits like GTK+ or Motif.

Ubuntu Package Notes
— freeglut.sourceforge.net

GLUT, freeglut anyway, also seems to support a lot of features with text and menus. If you were going to write a console mode, it’d probably be smart to see if freeglut’s features would help. On the other hand, apparently GLUT and freeglut use deprecated functionality that is not compatible with the most modern implementations of OpenGL.

GLUT also can help with the creation of commonly needed geometric constructions. Shapes like sphere, torus, and icosahedron are provided with generation functions. Even the famous teapot is available through GLUT. In some ways, this makes glut a bit dubious; is it using the best modern vertex buffering techniques and geometry shaders for these shapes?

Freeglut also has a bunch of "gaming" functions involving mouse movement, joysticks, and disabling the keyboard repeat. There are also "android" functions involving multi-touch.

I think that freeglut is good for testing and many projects but for a professionally shipped product it may be better to look at GLFW. If you concentrate your energies correctly and write clean code, it should be possible to replace this functionality relatively easily. Perhaps have a framework for multiple different interfaces and choosing the one that works the best.

Installing freeglut

To make sure this is in place…

Ubuntu:

apt-get install libglut3-dev glutg3-dev freeglut3-dev

Fedora/CentOS:

yum install freeglut freeglut-devel

Usage

To use in programs use:

#include <GL/freeglut.h>

To compile use this flag:

-lglut

The full API is available but only a few functions are necessary for simple tests and projects. Actually, that API documentation seems to have a bunch of missing explanations. I’m not sure how easy this will be to work with.

Initialization and Cleanup

Start by getting GLUT in the mood to do anything at all. Seriously, do this first or there will be complaining:

glutInit(&argc, argv);

This is a decent thing to do next so you know what kind of GLUT you’re really dealing with:

fprintf(stderr,"INFO: GLUT_VERSION=%d\n", glutGet(GLUT_VERSION));

Here’s a normal start up function that gave me quite a lot of problems.

glutInitContextVersion(4, 0);

It is supposed to provide hints to GLUT about what version of OpenGL you want to use, but I found it to just cough up seg faults on some platforms. I’m sure it’s important but if you’re having trouble with this command, comment it out and see how it goes.

To set where and how the window(s) will be created:

glutInitWindowSize(width, height);
//glutInitWindowPosition (x, y);

Confirm with:

int windowLeft= glutGet(GLUT_WINDOW_X); // Horizontal position.
int windowWidth= glutGet(GLUT_WINDOW_WIDTH); // Horizontal position.

You can also check for these properties (I’m only listing ones I think might be especially useful):

  • GLUT_FULL_SCREEN - 1 if window is in full screen mode.

  • GLUT_WINDOW_CURSOR - Current cursor (whatever that means)

  • GLUT_WINDOW_DOUBLEBUFFER - 1 if the color buffer is in double mode.

  • GLUT_SCREEN_WIDTH - Screen is this many pixels wide.

  • GLUT_SCREEN_WIDTH_MM - Width of screen in millimeters.

  • GLUT_ELAPSED_TIME - Milliseconds since glutInit or this variable was checked.

  • GLUT_VERSION - Can’t have too much debugging info.

  • GLUT_DISPLAY_MODE_POSSIBLE - Could be a bummer if not.

The position is the upper left corner (including decorations if possible). Probably better to not even use this if you don’t have a reason to; it’s optional.

You can set some modes (aka "state") with something like:

glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);

To get a window in action, create it with:

winID= glutCreateWindow("Window Title Goes Here");

Save the int that it returns since it comes in handy as your window ID.

Another title fiddling function which can be useful for diagnostics is:

glutSetWindowTitle("New Title is const char*");

Now that the window is created (and not after) it is a good time to register some callback functions that will be called during various GLUT managed events:

glutReshapeFunc(resizeCB);
glutDisplayFunc(renderCB);
glutIdleFunc(idleCB);
glutTimerFunc(0, timerCB ,0);

Of course you have to have the callback functions previously defined somewhere.

Here are some simple versions that work:

void renderCB(void){
    doActualOpenGLStuffHere()
    glutSwapBuffers();
    glutPostRedisplay();
    } // End function renderCB
void resizeCB(int W, int H){
    glViewport(0, 0, W, H);
    } // End function resizeCB
void idleCB(void){
    glutPostRedisplay();
    } // End function idleCB
void timerCB(int Value){
    // It's ok for this one to be blank.
    } // End function timerCB

Remember, put all these, or prototypes of them, somewhere ahead of their registration as callbacks.

Error Handling

void glutInitErrorFunc   (void (*callback)(const char *fmt, va_list ap) );
void glutInitWarningFunc (void (*callback)(const char *fmt, va_list ap) );

Event Loop

GLUT is responsible for starting an event loop that checks input devices and calls the applications correct callback functions when necessary. Once things are initialized, start the event loop, generally in main() with:

glutMainLoop();

For fancy program flow control, there is also glutLeaveMainLoop() and glutMainLoopEvent().

Et Al

Here are some interesting miscellaneous functions:

  • glutLeaveFullScreen

  • glutFullScreenToggle

GLEW

"The OpenGL Extension Wrangler Library (GLEW) is a cross-platform open-source C/C++ extension loading library. GLEW provides efficient run-time mechanisms for determining which OpenGL extensions are supported on the target platform. OpenGL core and extension functionality is exposed in a single header file. GLEW has been tested on a variety of operating systems, including Windows, Linux, Mac OS X, FreeBSD, Irix, and Solaris."

sudo apt-get install libglew1.5 libglewmx1.5 libglew1.5-dev libglewmx1.5-dev glew-utils

Include in programs with:

#include <GL/glew.h>

Note that this line probably should be the first or else other libraries (GLFW for one) will not work. You can also add:

#define GLEW_STATIC

If you want to statically link this.

And when compiling use this flag:

-lGLEW

I discovered that the glew-utils package (on Ubuntu) provides /usr/bin/glewinfo which is extremely handy for assessing what kind of OpenGL you can support.

Compiling

Here is an attempt to have a minimal but complete OpenGL program. The point of this is to make sure the simplest thing works before trying complicated things. This will ensure that you have OpenGL libraries installed and that your basic tool chain can do the job.

#include <GL/freeglut.h>
static void dispfun() {
    glClear(GL_COLOR_BUFFER_BIT);
    glutSwapBuffers();
    }
int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
    glutInitWindowSize(640, 480);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("Window's Title");
    glutDisplayFunc(dispfun);
    glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
    glutMainLoop();
    return 0;
    }

This can successfully be compiled (on a Linux system) with:

g++ -lglut -lGL -o simplest simplest.cc

This should open up a green 640x480 OpenGL window whose upper left corner will be 100 pixels from the top and the left.

A Decent Example

I found some ancient but good examples here:

I liked the glutplane.c one. It compiled with the following command:

g++ glutplane.c -lglut -lGL -o glutplane

You can use space to put the planes in motion. Right button on the mouse brings up the GLUT-based menu which is defined in the program. Escape quits.

For a very simple example, the cube.c simply rendered a recognizable shape in the simplest way possible.

Although most of these examples are astonishingly well-behaved (for code written in 1994!) there were some fixes necessary to get stuff working. Here are some.

Any line with an EXT in it needs the EXT removed since most of the functionality that was in "external" add ons then are included in the main package now.

glPolygonOffsetEXT(factor, bias);

Also there were some lines like:

gluTessCallback(tobj, GLU_BEGIN, glBegin);
gluTessCallback(tobj, GLU_VERTEX, glVertex2fv);
gluTessCallback(tobj, GLU_END, glEnd);

These were not, apparently, cast correctly but it’s fixable with something like:

gluTessCallback(tobj, GLU_BEGIN, (void(*)())&glBegin );
gluTessCallback(tobj, GLU_VERTEX, (void(*)())&glVertex2fv );
gluTessCallback(tobj, GLU_END, (void(*)())&glEnd );

Fixed vs. Programmable Pipeline or Older vs. Newer

It seems there are two OpenGL strategies. The first is the "Fixed Pipeline". This seems pretty widely supported as it is, I believe, the original way of doing things. All of the original SGI OpenGL examples use this. All hardware before about 2005 supports this (and presumably all OpenGL software written before then uses it). Basically OpenGL versions before 3.0 are oriented towards fixed pipeline (aka immediate mode) and later versions are programmable pipeline.

The problem with fixed pipeline OpenGL is, one, it is deprecated and, two, it is much less efficient for many things. Unfortunately for beginners, what it is unequivocally more efficient at is helping beginners accomplish simple OpenGL tasks. I find it a bit strange that the Lords of OpenGL have decided to not just progress away from this kind of programming but also to cut off legacy support. There is a byzantine system of "extensions" which can generally be used to get the functionality you need and, I suppose, in the future that software rendering will be a perfectly fine option if you need fixed pipeline legacy support. After all, if you’re interested in fixed pipeline programming, you’re not really interested in superlative performance optimization anyway.

The more modern way of using OpenGL which is not universally supported on all legacy systems is the "Programmable Pipeline". The programmable pipeline uses shaders to explicitly control how things are rendered. Shaders are programs that (ideally) run on the GPU hardware. There are several types of shaders, the most notable being vertex shaders and fragment shaders. Here’s Wikipedia on shaders.

The bigger picture here is that you need to think of your graphics card (or even your integrated graphics chip) as its own separate computer. They have their own separate memory, timing clock, instruction set, etc. Instead of just writing software for the obvious computer, now you have to also write software that provides special software for a totally different computer system, one that specializes only in tasks related to graphics.

This special software is written in a special language, GLSL (GL Shader Language). In addition to loading the graphics computing resources with shader software, much of the trick of modern programmable pipeline programming involves packing up your data into efficient structures to send to the graphics computer. I think of it like building a ship in a bottle. There is a bottleneck for which you must make all kinds of weird and inconvenient accommodations, but the results of building the thing in the bottle are eventually kind of magical if it all works out.

Problems

I have an ancient laptop which barely has OpenGL (ahem Intel945GM). While doing experiments, I ran into:

OpenGL 2.1 not supported!
Unable to initialize graphics library!

I could see from apt-cache show libgl1-mesa-dev that I was using Mesa 7.7 and the Mesa site said that version 7.0 is a stable release featuring 2.1 support. So what to do?

Turns out that the answer was to temporarily disable hardware acceleration/rendering with something like:

LIBGL_ALWAYS_SOFTWARE=1 ./my_opengl_sample

Then it worked fine for illustration purposes.

Loading Data To The GPU

Some code that looks like this can be useful. The important things here is the functions and, roughly, their order.

glGenVertexArrays(1, &VaoId);
glBindVertexArray(VaoId);
glGenBuffers(1,&VboId);
glBindBuffer(GL_ARRAY_BUFFER, VboId);
glBufferData(GL_ARRAY_BUFFER, BufferSize, Vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0,4,GL_FLOAT,GL_FALSE,VertexSize,0);
glVertexAttribPointer(1,4,GL_FLOAT,GL_FALSE,VertexSize,(GLvoid*)RgbOffset);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glGenBuffers(1, &IndexBufferId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);

Then * glDrawArrays() draws the active GL_ARRAY_BUFFER. * glDrawElements() draws the indices of the active GL_ARRAY_BUFFER as specified by the buffer bound to the GL_ELEMENT_ARRAY_BUFFER.

Vertex Array Objects (VAO)

This is pretty confusing (since there are also "Vertex Attribute Arrays") but VAO is definitely referring to Vertex Array Objects. This part of OpenGL is, for me, poorly exposed to the programming interface. I think the terminology and function names are misleading and murky. In the book "OpenGL Insights" Edward Angel rightly says, "Next comes the most difficult part to explain. We allocate a VAO and a VBO… The basic idea that we are setting up storage is clear by why we need a VBO and a VAO in such a simple program is a tough one…" Yup.

I think the best technically correct thing that can be said about them is that it provides access to all the data that is involved in vertex processing. This is done through references (pointers) to things like vertex buffers, index buffers, and possibly more. The important idea here is that once you’ve set this data structure up, you can bind to it which will cause the GPU to use that data set and if you bind to it again at a later time, you save all of the overhead of reloading all that data into the GPU.

Strategic Outline
initialization:
    for each batch
        generate, store, and bind a VAO
        bind all the buffers needed for a draw call
        unbind the VAO

main loop/whenever you render:
    for each batch
        bind VAO
        glDrawArrays(...); or glDrawElements(...); etc.
    unbind VAO
Another Strategic Outline
void Setup() {
    glGenVertexArrays(..);
    glBindVertexArray(..);
    // now setup all your VertexAttribPointers that will be bound to this VAO
   glBindBuffer(..);
   glVertexAttribPointer(..);
   glEnableVertexAttribArray(..);
}

void Render() {
    glBindVertexArray(vao);
    // that's it, now call one of glDraw... functions
    // no need to set up vertex attrib pointers and buffers!
    glDrawXYZ(..)
}

Buffer Objects

Since the OpenGL as implemented on a GPU can not see system memory simple memory allocation is not sufficient. To provide data to OpenGL for any purpose, you must send it to the card explicitly. In the early implementations of OpenGL, this could be done as the application computer hardware came up with each small part of the graphics related data. But since there is a penalty for sending data to the GPU, this was restructured into a more compact (faster) data package which could, importantly, be reused on the GPU thus saving subsequent redundant transfers. Strategically speaking, it’s a good idea to isolate any static data that the GPU will need (asset geometry, textures, etc) and send it to the GPU ahead of time. Then the directives to do things with the data will be executed optimally. OpenGL is one of those cases where optimization is never premature.

The various types of buffer objects are listed in the documentation for glBindBuffer and there are 13 of them! The classic ones that have been around since version 2 are: GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER, and GL_PIXEL_UNPACK_BUFFER.

Debugging

I got this from a Valve presentation:

// A debugging callback
void APIENTRY myErrorCB( GLenum _source, GLenum _type, GLuint _id,
GLenum _severity, GLsizei _length, const char* _message, void* _userParam)
{printf("%s\n", _message);}

glDebugMessageCallbackARB(myErrorCB, NULL);
glEnable(GL_DEBUG_OUTPUT);

ARB_debug_output

Variable Naming

Here is a variable naming convention from [Bailey 12] that I think is reasonable.

a

per-vertex attribute from the application

u

uniform variable from the application

v

from a vertex shader

tc

from a tessellation control shader

te

from a tessellation evaluation shader

g

from a geometry shader

f

from a fragment shader

There were some OpenGL state variables that have been deprecated in favor of arbitrary user defined variables. Here is a plausible translation based on the previous convention.

#define uModelViewMatrix

gl_ModelViewMatrix

#define uProjectionMatrix

gl_PorjectionMatrix

#define uModelViewProjectionMatrix

gl_ModelViewProjectionMatrix

#define uNormalMatrix

gl_NormalMatrix

#define uModelViewMatrixInverse

gl_ModelViewMatrixInverse

#define aColor

gl_Color

#define aNormal

gl_Normal

#define aVertex

gl_Vertex

#define aTexCoord0

gl_MultiTexCoord0