This is a brief documentation/quick reference to the lab material. It is not complete. You can check out the interfaces for additional functionality, but this should cover the needs for most labs and projects.
OpenGL
We work with OpenGL 3.2 and up, which means modern OpenGL. OpenGL 3 was a major revision which made some outdated material deprecated - for a reason. As teacher, I particularly like how the math became much more visible and accessible. Avoiding low performance calls certainly has a point as well.
OpenGL is our “low level”. Many parts will be abstracted by the code below. However, in order not to abstract away too much too early, you work directly in OpenGL in Lab 1, and then we gradually add abstraction calls. But by then you know what is in there, so you can go in and modify as needed. (We are engineers - we know how it works inside!)
One reason for the add-on material is that OpenGL is focused on its own task, drawing, and leaves external tasks like file management to us.
Most common types:
GLfloat floating-point numbers.
GLuint unsigned integer, used for reference numbers to texture objects, VBOs, VAOs etc.
Calls used in most labs and projects
glClear(arg);
Clears the frame buffer, depth buffer and any other buffer specified. Typical arguments: GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
glEnable(arg)
glDisable(arg)
Enables or disables a specific feature. Common arguments: GL_DEPTH_TEST, GL_CULL_FACE, GL_BLEND
Calls that you can use but have convenient abstractions
Note: Many of the following calls are packaged in VectorUtils (see below) for making them easier to use and report errors! They remain for completeness but I recommend using calls like uploadMat4ToShader(), which both activates the shader, gets the variable location, reports if it fails, and uploads!
glUniformMatrix4fv(location, 1, GL_TRUE, matrix.m);
Uploads a matrix to the active shader (declared uniform mat4 in the shader).
glUniform1i(location, value);
Uploads a uniform integer to a shader, for example a boolean or texture unit number.
glUniform1f(location, value);
Uploads a uniform floating-point value to a shader.
glGetUniformLocation(shader, varname)
Gets the location of a variable in a shader. Note that the shader might not be active so if you pass this location to glUniformMatrix4fv or similar it will be passed to the active shader, not necessarily the one you used in the glGetUniformLocation call.
glActiveTexture(GL_TEXTURE0);
Makes a texture unit current.
glBindTexture(GL_TEXTURE_2D, arg);
Makes the texture arg current.
glUseProgram(arg);
Makes the shader arg current.
Calls used in early labs, later packaged in Common.
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vb);
glBindVertexArray(m->vao);
glBindBuffer(GL_ARRAY_BUFFER, m->vb);
glBufferData(GL_ARRAY_BUFFER, m->numVertices*3*sizeof(GLfloat), m->vertexArray, GL_STATIC_DRAW);
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(loc);
glDrawElements(GL_TRIANGLES, m->numIndices, GL_UNSIGNED_INT, 0L);
Calls of interest usually packaged inside the Common.
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenTextures(1, &texture->texID);
GLSL
Here is a quick summary of the most vital features in GLSL, the OpenGL Shading Languages.
Types
int integer
float single precision floating point scalar
vec3 3-component vector
vec4 4-component vector
mat3 3x3 matrix of floats
mat4 4x4 matrix of floats
Modifiers
uniform: Variables set by the host, same for an entire drawing call. Available in both vertex and fragment.
in, vertex shader: Attributes
out, vertex shader: Varying (interpolation)
in, fragment shader: Varying (result of interpolation)
out, fragment shader: Pixel value
Built-in functions
dot dot product
cross cross product
exp exponantial function
sin sinus
cos cosinus
max max of two scalars
min min of two scalar
clamp clamps a scalar beween two scalars
reflect mirrors a vector over another - as in the book
Matrix multiplication or matrix-vector multiplication is made with *
Vector-vector multiplication multiplies component-wise, not very useful.
Type casts are written as functions, int(arg), vec3(arg)… (unlike C where you have the parenthesis around the type)
GLEW
GLEW, the OpenGL Extension Wrangler, is essential for Windows. It is included in the Common/Windows folder. All you need to do to use it is:
#include “glew.h”
and
glewInit();
in the proper places plus recommended error checks.
Notes on the C language
The language syntax should mostly be familiar from many others. Some notes:
Globals are written outside functions.
Any variable declared inside a function is local to that function and has no other scope.
printf() is used for formatted output to the terminal. It is part of stdio, standard IO.
* on a variable dereferences a pointer, so *a is what a points to.
& on a variable is the address to the variable.
* on an argument in a function declaration declares that the argument should be a pointer.
#include compiles another file as part of the file that has the #include. The language is not modular in itself but simulates modularity with #include. (C++ does the same.)
The math library includes most math functions that you need, like sin, cos, max, min…
My lab material, the “Common” folder
I wrote most of this code myself. Significant parts of the OBJ loader was contributed by Mikael Kalms. All parts are written in C with a C++ interface. VectorUtils3 even has operator overloading defined for operations of interest.
MicroGlut
GLUT is the original cross-platform user interface layer for OpenGL, the OpenGL Utility Toolkit. I find it quite straight-forward and simple, and very suitable for lab work. It encourages very little added complexity. However, the original GLUT is quite dated, with too much material only useful from old OpenGL, like the font and model management. MicroGlut strips away all that, plus adds a few useful calls that I found were missing and caused unnecessary work.
Therefore, remember, MicroGlut is not GLUT! It is smaller, more focused, skips the junk, and adds new, nice features,
MicroGlut comes in three separate implementations, for Linux, Mac and Windows. By storing them in separate folders in the Common folder, it is just a matter of search paths to use the right one.
This is not a complete documentation! I list the calls that I find it likely that you want to use. Some less common ones are listed at the end, even less common ones are omitted.
Initialization
Calls used before glutCreateWindow:
void glutInit(int *argcp, char **argv);
Initialize MicroGlut. Should be called first.
void glutInitWindowSize (int width, int height);
Sets a desired window size.
void glutInitWindowPosition (int x, int y);
Sets a desired window position.
void glutInitDisplayMode(unsigned int mode);
Sets the desired display mode. Typical argument: GLUT_DOUBLE | GLUT_DEPTH
void glutInitContextVersion(int major, int minor);
Sets the desired context version. Our default is 3, 2. You may want higher.
int glutCreateWindow (char *windowTitle);
Creates the window and the OpenGL context. Important: Must be called after glutInitDisplayMode, and OpenGL calls can only be called after, because this call creates the OpenGL context!
void glutMainLoop();
Enters the internal main loop of MicroGlut. Typically the last call of the main program. After this point, everything happens in the callback functions.
Callbacks
Events are processed by callback functions. They are typically specified in the main program but may be changed any time. Common callbacks:
void glutReshapeFunc(void (*func)(int width, int height));
Callback called when the window is resized.
void glutDisplayFunc(void (*func)(void));
Sets your display function. Vital!
void glutKeyboardFunc(void (*func)(unsigned char key, int x, int y));
Sets a callback for key presses. Note: You only want this if the key press is what you want. If you want typical first person controls, you probably want to use glutKeyIsDown instead.
Constants for keyboard calls include:
#define GLUT_KEY_LEFT 28
#define GLUT_KEY_UP 29
#define GLUT_KEY_RIGHT 30
#define GLUT_KEY_DOWN 31
#define GLUT_KEY_SPACE ' '
Ordinary ASCII keys are tested by the character.
void glutMouseFunc(void (*func)(int button, int state, int x, int y));
Callback for mouse clicks. The button parameter may be GLUT_LEFT_BUTTON or GLUT_RIGHT_BUTTON, and state can be GLUT_DOWN or GLUT_UP.
void glutPassiveMotionFunc(void (*func)(int x, int y));
Callback for mouse movements.
void glutMotionFunc(void (*func)(int x, int y));
Callback for the mouse being moved while the button is pressed.
void glutTimerFunc(int millis, void (*func)(int arg), int arg);
Sets up a single-shot timer to call a specified function within the specified number of milliseconds. Note: This has been skipped in some versions and may be removed altogether, since the following call is IMHO so much more convenient:
void glutRepeatingTimer(int millis);
New call that will trigger a redisplay at the specified time interval.
Runtime calls
void glutPostRedisplay();
Asks for a redisplay, so the display function will be called as soon as possible. Rarely used since glutRepeatingTimer handles this. Useful if you do not run an animation and want to update only after certain events. Might get removed in the future to slim down the API even more.
void glutSwapBuffers();
Swaps the frame buffers. End your display function with this.
int glutGet(int type);
Gets information specified by the type parameter. Most common: GLUT_ELAPSED_TIME.
char glutKeyIsDown(unsigned char c);
Checks if the specified key is held down. New call, recommended!
char glutMouseIsDown(unsigned char c);
Same thing for mouse buttons. (Not in all implementations yet. Bug note: The input value might not always map to the proper button so try all, 0, 1, 2.)
void glutWarpPointer(int x, int y);
Move mouse pointer, conforms with GLUT. Useful for mouse controlled animation. Make sure you can disable it from the keyboard; your mouse will be stuck if you warp it every frame.
void glutShowCursor();
void glutHideCursor();
Controls the cursor visibility.
For further (less common) calls and constants, see the header files.
GL_utilities
The main part here is the shader loader.
GLuint loadShaders(const char *vertFileName, const char *fragFileName);
This is also where you find printError:
void printError(const char *functionName)
This is a wrapper for glGetError(), but which only gives you an output if an error is signalled. This is a very rough error message. It just indicates that something doesn’t look right, but not what. Typical “errors” can be unused shader variables, a buffer that hasn’t been properly bound. In many cases it is not more than a warning.
Apart from this, there are variants for loading advanced shaders and for creating FBOs. These calls are intended for the advanced course.
VectorUtils3/4
Any OpenGL program needs a math library for vector and matrix operations. VectorUtils3 has most that you need but not too much more in a single file. Even so, it contains a lot of calls. Most are pretty simple though.
Built-in types
The following types (identical to the GLSL counterparts) are defined with several constructors. See the definitions for these.
vec3
vec4
mat3
mat4
vec2 (limited)
Basic vector operations on vec3's. (vec4 not included since I never need them for these kinds of operations.)
vec3 cross(vec3 a, vec3 b);
GLfloat dot(vec3 a, vec3 b);
GLfloat Norm(vec3 a);
vec3 normalize(vec3 a);
vec3 CalcNormalVector(vec3 a, vec3 b, vec3 c);
void SplitVector(vec3 v, vec3 n, vec3 *vn, vec3 *vp);
Matrix operations primarily on 4x4 matrixes
Row-wise by default but can be configured to column-wise (see SetTransposed)
mat4 IdentityMatrix();
mat4 Rx(GLfloat a);
mat4 Ry(GLfloat a);
mat4 Rz(GLfloat a);
mat4 T(GLfloat tx, GLfloat ty, GLfloat tz);
mat4 S(GLfloat sx, GLfloat sy, GLfloat sz);
Mat3 operations
void OrthoNormalizeMatrix(mat4 *R);
mat4 transpose(mat4 m);
mat3 TransposeMat3(mat3 m);
mat4 ArbRotate(vec3 axis, GLfloat fi);
mat4 CrossMatrix(vec3 a);
mat4 MatrixAdd(mat4 a, mat4 b);
Operations obsolete for C++ (use operator overloading or constructors instead), but convenient from C
mat3 MultMat3(mat3 a, mat3 b); // m = a * b
vec3 MultMat3Vec3(mat3 a, vec3 b); // result = a * b
mat4 Mult(mat4 a, mat4 b);
For symmetry, MultMat4 is made a synonym:
#define MultMat4 Mult
vec3 ScalarMult(vec3 a, GLfloat s);
vec3 MultVec3(mat4 a, vec3 b); // result = a * b
vec4 MultVec4(mat4 a, vec4 b);
vec3 SetVector(GLfloat x, GLfloat y, GLfloat z); C++ can use a constructor instead.
vec3 VectorSub(vec3 a, vec3 b);
vec3 VectorAdd(vec3 a, vec3 b);
GLU replacement functions
GLU was a very useful utility library, and these were the most valuable calls there, here rewritten to return mat4’s:
mat4 lookAtv(vec3 p, vec3 l, vec3 v);
mat4 lookAt(GLfloat px, GLfloat py, GLfloat pz,
GLfloat lx, GLfloat ly, GLfloat lz,
GLfloat vx, GLfloat vy, GLfloat vz);
Creates a world-to-view matrix for a camera in p, looking at l, with the up vector v. See the course book on how it works!
mat4 perspective(float fovyInDegrees, float aspectRatio,
float znear, float zfar);
Creates a perspective projection with the frustum with the specified angle and aspect ratio.
mat4 frustum(float left, float right, float bottom, float top,
float znear, float zfar);
Creates a perspective projection with the specified frustum.
mat4 ortho(GLfloat left, GLfloat right, GLfloat bottom,
GLfloat top, GLfloat near, GLfloat far);
Creates a parallel projection matrix with a rectangular frustum.
For creating a normal matrix:
mat3 InvertMat3(mat3 in);
mat3 InverseTranspose(mat4 in);
mat4 InvertMat4(mat4 a);
Inverse and inverse transpose, as described in the course book.
Simple conversions:
These are replaced by constructors.
mat3 mat4tomat3(mat4 m);
mat4 mat3tomat4(mat3 m);
vec3 vec4tovec3(vec4 v);
vec4 vec3tovec4(vec3 v);
Convenient printing calls:
void printMat4(mat4 m);
void printVec3(vec3 in);
C++ overloading operators:
Some overloading doesn’t conform with GLSL. I may change them in the future.
vec3 operator+(const vec3 &a, const vec3 &b) // vec3+vec3
vec3 operator-(const vec3 &a, const vec3 &b) // vec3-vec3
vec3 operator-(const vec3 &a)
float operator*(const vec3 &a, const vec3 &b) // vec3 dot vec3
vec3 operator*(const vec3 &b, double a) // vec3 * scalar
vec3 operator*(double a, const vec3 &b) // scalar * vec3
vec3 operator/(const vec3 &b, double a) // vec3 / scalar
void operator+=(vec3 &a, const vec3 &b) // vec3+=vec3
void operator-=(vec3 &a, const vec3 &b) // vec3-=vec3
void operator*=(vec3 &a, const float &b) // vec3*=scalar
void operator/=(vec3 &a, const float &b) // vec3/=scalar
vec4 operator+(const vec4 &a, const vec4 &b) // vec4+vec4
vec4 operator-(const vec4 &a, const vec4 &b) // vec4-vec4
float operator*(const vec4 &a, const vec4 &b) // vec4 dot vec4
vec4 operator*(const vec4 &b, double a) // vec4 * scalar
vec4 operator*(double a, const vec4 &b) // scalar * vec4
vec4 operator/(const vec4 &b, double a) // vec4 / scalar
void operator+=(vec4 &a, const vec4 &b) // vec4+=vec4
void operator-=(vec4 &a, const vec4 &b) // vec4-=vec4
void operator*=(vec4 &a, const float &b) // vec4 *= scalar
void operator/=(vec4 &a, const float &b) // vec4 /= scalar
Matrix multiplication:
mat4 operator*(const mat4 &a, const mat4 &b)
mat3 operator*(const mat3 &a, const mat3 &b)
vec3 operator*(const mat4 &a, const vec3 &b)
vec4 operator*(const mat4 &a, const vec4 &b)
vec3 operator*(const mat3 &a, const vec3 &b)
Simplified calls with improved error reporting
These calls all upload the specified data to a specified shader, and will report errors to the terminal if the variable is not found. These calls are convenient to avoid accidentally trying to upload to the wrong shader. Many additional calls like this are possible. Feel free to add them with the existing calls as model!
void uploadMat4ToShader(GLuint shader, const char *nameInShader, mat4 m);
void uploadUniformIntToShader(GLuint shader, const char *nameInShader, GLint i);
void uploadUniformFloatToShader(GLuint shader, const char *nameInShader, GLfloat f);
void uploadUniformFloatArrayToShader(GLuint shader, const char *nameInShader, GLfloat *f, int arrayLength);
void uploadUniformVec3ToShader(GLuint shader, const char *nameInShader, vec3 v);
void uploadUniformVec3ArrayToShader(GLuint shader, const char *nameInShader, vec3 *a, int arrayLength);
The following call will bind a texture to a specified unit (just glActiveTexture + glBindTexture).
void bindTextureToTextureUnit(GLuint tex, int unit);
Extra overloads
The following calls are extra overloads for C++ only to conform better with GLSL:
mat3 inverse(mat3 m);
mat4 inverse(mat4 m);
mat3 transpose(mat3 m);
mat4 S(GLfloat s);
mat4 S(vec3 s);
mat4 lookAt(vec3 p, vec3 l, vec3 u);
LittleOBJLoader
This is a small but fairly capable Wavefront OBJ loader. It can load OBJ files, with the option of loading multi-part files as a list of separate models, and it will also load material files. Among its features is the ability to automatically generate normal vector to models that lack them.
The most vital calls are:
Model* LoadModel(const char* name);
Loads a model to CPU, allocates VAO and VBOs, uploads to GPU.
void DrawModel(Model *m, GLuint program, const char* vertexVariableName, const char* normalVariableName, const char* texCoordVariableName);
Draws a model loaded by LoadModel.
void ReloadModelData(Model *m)
Reloads a model from the CPU data. Useful if you modify the model geometry on the CPU.
Note: Some of my demos may still use the older “loadobj”, which is an alder and much less capable version of LittleOBJLoader. It is generally possible to just change the #include and it works!
Why OBJ? Isn’t it outdated? It is excellent for educational purposes. It is so simple that it is actually doable to hand edit it (to a certain extent)!
LoadTGA
This unit loads TGA images. It has decent support for variations, like compression, but you may still find formats that are unsupported.
bool LoadTGATexture(char *filename, TextureData *texture);
Loads a texture and returns the structure describing it.
void LoadTGATextureSimple(char *filename, GLuint *tex);
Loads a texture and only returns the texture object.
bool LoadTGATextureData(char *filename, TextureData *texture);
Loads a texture to CPU memory but does not upload to the GPU.
Apart from these, the unit also can save to TGA, and holds the TextureData structure from which you can access the raw texture data.
Note: There is also a bigger “LoadTexture” package that includes this and three more formats. See my “Packages” archive!