Objects in OpenGL, and Life Without Orion's Graphics Library

CS 381 Lecture, Dr. Lawlor

You can download all these tiny examples (Zip, Tar-gzip).

There are lots of interesting programming idioms in OpenGL.

Flags

Flags are the simplest idiom in OpenGL.  For example, GL_BLEND represents alpha blending.  "glEnable(GL_BLEND);" turns on alpha blending.  "glDisable(GL_BLEND);" turns off alpha blending.   glEnable and glDisable take any of a giant list of flags, and can be used to turn on and off all sorts of stuff.

Here are the flags I use most often:
Most of the other flags control fixed-function hardware, and aren't useful if you use GLSL.

Objects

There's a standard way that OpenGL deals with stuff that takes some time to create--textures, GLSL programs, framebuffers, arrays of vertices, and so on.  This is the OpenGL "object model".

To create a new OpenGL object of type T, you call a routine named something like glGenT.  This returns a GLuint, which is never zero.  Usually the first time you call the routine, it returns 1, then 2, etc.  OpenGL calls these GLuints "names", but they're just handles, or indices into the hardware's list of resources.

To set OpenGL's current T object, you call a routine named something like glBindT.  Any subsequent operations then refer to this object.  Calling glBindT with an object of 0 goes back to the default object.

Texture Objects

Here's how you set up a texture: call glGenTextures, bind the texture, upload pixels, and set sampling mode.
GLuint texNum=0;
glGenTextures(1,&texNum); /* texNum is now a unique "texture name"--probably the value 1! */
glBindTexture(GL_TEXTURE_2D,texNum); /* texNum is now the current texture--all texture ops now refer to texNum */

/* Fill the currently bound texture (texNum) with pixel data--red, green, blue, white. */
unsigned char texData[]={0xff,0x00,0x00,   0x00,0xff,0x00,   0x00,0x00, 0xff,   0xff,0xff,0xff};
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB8,4,1,0,GL_RGB,GL_UNSIGNED_BYTE,texData);

/* Set the magnification, minification, and wrap modes on the current texture (texNum) */
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);

glBindTexture(GL_TEXTURE_2D,0); /* back to the default texture name--zero */

Note that the last glBindTexture to reset back to the zero texture isn't actually needed, but it's a good habit.

Here's how you set up to draw using a texture: just activate a "texture unit" to process the texture, and the bind the texture name.
glActiveTextureARB(GL_TEXTURE0_ARB); /* we're using texture unit zero (this is the default, actually) */
glBindTexture(GL_TEXTURE_2D,texNum);
Now  you'd copy the texture unit (0) into a GLSL "uniform sampler2D", and call texture2D in GLSL to look up texture values.

Here's how to throw away all the texture data bound to texNum.  Note you can have many textures at once, so only throw out stuff you're really done using!
glDeleteTextures(1,&texNum);

The sequence to keep in mind is:
    Creation: gen, bind, and then set up
    Draw:  bind
    Delete: delete

My "oglTexture" class (in ogl/util.h) wraps these calls.

One slightly odd thing about textures is that you actually *can* have several different textures active at once, but they have to be in different texture "units".  You set the current texture unit to i with "glActiveTextureARB(i+GL_TEXTURE0_ARB);".  A glBindTexture call changes the current texture of the current unit.  So to set texture unit 0 to foo and texture unit 1 to bar, you'd call:
glActiveTextureARB(0+GL_TEXTURE0_ARB);
glBindTexture(GL_TEXTURE_2D,foo);
glActiveTextureARB(1+GL_TEXTURE0_ARB);
glBindTexture(GL_TEXTURE_2D,bar);
glActiveTextureARB(0+GL_TEXTURE0_ARB);
In GLSL, you actually put the texture *unit* number into a "uniform sampler2D" variable as an int.

It's good practice to leave texture unit 0 active--that's the default texture unit.

You can have at least tens of thousands of texture names in your program, and switch between them pretty quickly. However, there are only 32 texture units, so you can have at most 32 textures bound and active in a single fragment program.   This usually isn't a very serious limitation!

Framebuffer Objects

Here's how to create and use a framebuffer object (FBO), for rendering to a texture.  See the documentation in EXT_framebuffer_object
GLuint fb;///< Framebuffer object
glGenFramebuffersEXT(1, &fb); /* Make a new framebuffer object */
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); /* set FB as the current framebuffer object */
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,   /* bind texture name "tex" as the color buffer */
        GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE_2D, tex, 0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); /* back to default framebuffer (the screen) */

// Here's how to draw using the framebuffer (this is fairly expensive: 35us)
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status!=GL_FRAMEBUFFER_COMPLETE_EXT) Quit("Bad framebuffer setup!");
glViewport(0,0,tex_w,tex_h); /* set viewport to size of texture */

// .. drawing calls now go to the our framebuffer...

// Switch back to drawing to screen:
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glViewport(0,0,win_w,win_h);

// Throw away our framebuffer (fairly expensive!)
glDeleteFramebuffersEXT(1,&fb);

You can use any texture as the color buffer for a framebuffer.  Rendered stuff directly affects the currently bound texture.

You can actually bind *multiple* color buffers into a single framebuffer (GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT...).  You can write to these with "gl_FragColor[i]=..." in GLSL.  This is called "Multiple Render Targets", or MRT.  For some reason, you're allowed to rearrange the mapping from gl_FragColor to COLOR_ATTACHMENT, although I always use the identity mapping:
int n_out=2;
int out_names[2];
out_names[0]=GL_COLOR_ATTACHMENT0_EXT; /* destination for gl_FragColor[0] */
out_names[1]=GL_COLOR_ATTACHMENT1_EXT; /* destination for gl_FragColor[1] */
glDrawBuffersATI(n_out,out_names);
Modern hardware only supports up to 4 render targets, and using more than one target does have an appreciable slowdown.  About the only time you really need MRT is when doing weird computations that need many output values (e.g., spectral rendering, or raytracing, or some complicated per-pixel simulation), not when doing normal color rendering.

GLSL Compiled Shader Objects

Here's how to set up a GLSL "shader object" (these routines are documented in the ARB_shader_objects extension)

    /* Make a program object */
    GLhandleARB prog=glCreateProgramObjectARB();

    /* Make vertex shader object */
    GLhandleARB vert=glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
    const char *glsl_code="void main(void) {gl_Position=vec4(vec3(0.8),1.0)*gl_Vertex;}";
    glShaderSourceARB(vert,1,&glsl_code,NULL);
    glCompileShaderARB(vert);
    glAttachObjectARB(prog,vert);

    /* Make fragment shader object */
    GLhandleARB frag=glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
   glsl_code="void main(void) {gl_FragColor=vec4(1.0,0.0,0.0,1.0);}";
    glShaderSourceARB(frag,1,&glsl_code ,NULL);
    glCompileShaderARB(frag);
    glAttachObjectARB(prog,frag);

    /* Link the program.  FIXME: check for link errors, with  glGetObjectParameterivARB and glGetInfoLogARB */
    glLinkProgramARB(prog);

Here's how to set a uniform variable, assuming you've got a "uniform float fooness;" somewhere in your vertex or fragment shader:
glUseProgramObjectARB(prog);
int loc=glGetUniformLocationARB(prog,"fooness");
glUniform1fARB(loc,0.76);
You can save the integer "loc", if you're updating uniform variables all the time.

Here's how to use the program object for rendering:
    glUseProgramObjectARB(prog);
    // Everything we draw (glBegin/glEnd) here will use our programmable shader...
    glUseProgramObjectARB(0);  /* reset back to default fixed-function program */

Here's how to delete a program object:
    glDeleteObjectARB(vert);
    glDeleteObjectARB(frag);
    glDeleteObjectARB(prog);