Course list http://www.c-jump.com/bcc/
Many technical terms used in this handout are explained in Jason McKesson's online book, Learning Modern 3D Graphics Programming
geometry, model, mesh, object
A single object in 3D space made of triangles, first instantiated in CPU memory and later uploaded to GPU for rendering
VAO, vertex array object
OpenGL structure that stores all of the state needed for drawing an object. Needs to be set it up once per model by glGenVertexArrays(), then we must call glBindVertexArray() every time before drawing the model.
VBO, vertex buffer object
Vertex buffer object represents an array of GPU memory. It is an OpenGL structure that provides access to a buffer of memory on the GPU (probably) C++ program only gets a GLuint (unsigned int) handle that refers to the buffer object.
vertex attributes
Vertex attributes are input parameters to the vertex shaders.
All GLUT and GLEW setup business is handled by the main() function:
int main (int argc, char** argv) { // Initialize GLUT glutInit( &argc, argv ); // Set up some memory buffers for our display glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH ); // Set the window size glutInitWindowSize( 800, 600 ); // Create the window with the title "Triangle" glutCreateWindow( "Hello, World!" ); // Bind the callback functions glutReshapeFunc( change_viewport ); glutDisplayFunc( render ); // glewInit() initializes OpenGL driver entry points // and assignes addresses to all functions in the API: GLenum err = glewInit(); if (GLEW_OK != err) { fprintf(stderr, "GLEW error"); return 1; } /*compile shaders...*/ /*configure light parameters...*/ /*upload data to OpenGL server...*/ /*set up camera projection...*/ // Run window messaging loop glutMainLoop(); return 0; }
An instance of the Model class is constructed as follows:
glGenVertexArrays()
creates vertex array object (can create multiple objects with one call.) Vertex array object stores state needed for uploading and rendering an object in OpenGL. In other words, VAO acts as a top-level descriptor of vertex data. We don't use this object directly: the C++ program refers to the vertex array object by a unique handle ID (an unsigned int.) The Model class keeps VAO handle in the member variable named Model::vao.
glBindVertexArray( vao )
Activates the vertex array object for allocating memory, uploading geometry, or drawing.
glGenBuffers()
OpenGL cannot use CPU memory directly. Instead, we need to allocate some memory on the GPU. glGenBuffers() prepares and returns back a unique handle (an unsigned int) which refers to a buffer object. OpenGL buffer object is an internal OpenGL control structure that keeps track of the GPU memory access, allocation, and dealocation.
Our application calls glGenBuffers() on creation of every model. Vertex attributes (that is, data for vertex shader) are loaded into GL_ARRAY_BUFFER.
If the model is also using the index array of vertex attributes, the indices must be loaded into GL_ELEMENT_ARRAY_BUFFER, so glGenBuffers() is called here the second time (although program could create multiple buffer objects with one call.)
glGetUniformLocation()
umM and umR are GLuint handles that refer to a specific uniform value in a shader program. To find out what these handles are, we call glGetUniformLocation()
This function loads object geometry from a Wavefront OBJ file. The model needs to be loaded only once, during the application loading and initialization stage
The function loads arrays of vertices, normals, and UV coordinates into memory. When arrays are loaded, the corresponding vertex attribute ID is queried from the shader program and stored in the model class for later use (vPosition, vNormal, and vTexCoord.) For example,
vPosition = glGetAttribLocation( shader.program_ID, "vPosition" );
The function returns an unsigned int (GLuint)
If indices[] array is also loaded from the OBJ file,
glBindVertexArray()
Activates the vertex array object
glBindBuffer()
Activates the buffer identified by index_vbo data member. Attaches to the GL_ELEMENT_ARRAY_BUFFER target. This makes the index_vbo buffer an active target. When a buffer is bound and active,
any data uploading goes into that buffer
any drawing will come from that buffer
glBufferData()
Uploads the indices[] array to GPU memory. Specifies the GL_STATIC_DRAW for the a usage token, indicating that the contents of the index buffer is uploaded once and will be used many times, as often as once per frame.
Note that other usage tokens are
GL_DYNAMIC_DRAW indicates that the contents will be modified repeatedly and not reused
GL_STREAM_DRAW -- the contents will be uploaded once and used only once in a while
This function loads the bitmap from a file and uploads it to the GPU. The function should be invoked only once during the model loading stage.
PRE-CONDITION: UVs[] should have already been loaded by the Model::load_OBJ_file() function prior to this call.
The function invokes
load_bmp_file() to load the data from the specified file. This sets up bitmap width, height, size, and bitmap_data in memory.
glGenTextures creates a texture buffer and returns its identifier, tex_buffer_ID.
Next, the texture bitmap is loaded onto the GPU as follows:
glTexImage2D <- GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, bitmap_data glTexParameterf <- GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT glTexParameterf <- GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT glTexParameterf <- GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR glTexParameterf <- GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR glGenerateMipmap <- tex_buffer_ID
The texture calls will be explained later when we cover the textures.
Finally, the ID of the texture variable in the fragment shader is queried from the shader program by the glGetUniformLocation(shader.program_ID) call.
The upload function sends object geometry to the GPU. The function needs to be invoked only once during the model loading stage.
glBindVertexArray( vao )
Activates object's state on the GPU.
glBindBuffer( GL_ARRAY_BUFFER, vbo )
Makes GL_ARRAY_BUFFER active, so the vertex data is uploaded to that particular buffer. The function attaches named buffer object (vbo) to a target named GL_ARRAY_BUFFER, which is used for vertex data, but may be re-bound later for other needs.
glBufferData( GL_ARRAY_BUFFER, buffer_size, NULL, GL_STATIC_DRAW )
Allocates GPU memory, where
buffer_size tells how big the buffer should be, and
GL_STATIC_DRAW specifies that the data will be loaded once and used many times, for example, once per frame.
glBufferSubData( GL_ARRAY_BUFFER, offset, total_vertices, vertices )
Provides the actual data. It is called separately for every component of vertex atributes -- vertices, normals, texture coordinates, and colors. However, the data here is "raw" - OpenGL does not know how the data is organized and what the data type is.
glVertexAttribPointer()
Specifies the data format. For example,
glVertexAttribPointer ( 0, 4, GL_FLOAT, GL_FALSE, 0, 0 );
The parameters are
0 is vertex attribute index, such as zero in layout(location=0) in shader code, or the attribute ID returned by glGetAttribLocation().
4 means four float components per vertex, as in (x, y, z, w). But if we wanted to include vertex color, then the number is different:
(x, y, z, w) = 4
(x, y, z) + (r, g, b, a) = 7
(x, y, z, w) + (r, g, b, a) = 8
GL_FLOAT specifies component type
GL_FALSE means "use the float values directly." Fixed-point data does not need to be normalized (converted to float) in a certain range ( -1, 1 signed or 0, 1 unsigned.)
0 is stride - byte offset (gap) between consecutive vertex attributes
0 is pointer - byte offset of the first component in the buffer
The result of Model::upload_2_server() is
GL_ARRAY_BUFFER identified by Model::vbo is populated with vertex data
The buffer exists on the graphics card
(Image from OpenGL Programming Guide for Mac)
The render function is responsible for drawing objects on every frame.
glClear()
Causes the screen to be cleared.
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL )
Specifies polygon rasterization mode. GL_FRONT_AND_BACK allows recognition for front- and back-facing polygons. GL_FILL, GL_LINE, and GL_POINT are supported rasterization modes.
To render each model,
The model matrix is set in vertex shader, typically in this order:
translate * rotate * scale
The camera view/perspective matrix is set in the shader glUniformMatrix4fv(model.shader.program_ID)
The light position is also set in the shader by glUniform4fv()
The model's Model::render() function is invoked
glutSwapBuffers()
Once all models are rendered, glutSwapBuffers() is called. It is a FreeGLUT command. OpenGL is double-buffered:
one frame buffer contains the image shown on screen,
another buffer is being rendered in the background.
glutSwapBuffers() swapps the buffers, and the next rendering always goes into the off-screen buffer. Each buffer swap generates a new frame on the screen.
PRE_CONDITION: all geometry data must be already loaded by Model::upload_2_server().
This function draws the object encapsulated by the Model class. The function is invoked for each frame by the global render() function.
Prepare model for rendering:
glBindVertexArray( vao )
Activates object's state on the GPU.
glUseProgram( shader.program_ID )
Activates activate shader program on the GPU
glEnableVertexAttribArray( vPosition/vNormal/vColor )
Enables vertex attribute arrays in the vertex shader (that is, vertex shader variables), identified by the vPosition/vNormal/vColor:
#version 130 in vec4 vPosition; in vec4 vColor; out vec4 color; void main () { color = vColor; gl_Position = vPosition; }
Note that vPosition/vNormal/vColor are GLuints obtained by glGetAttribLocation() during model initalization.
glEnableVertexAttribArray
Activates vPosition/vNormal/vColor inputs in the shader
glEnableVertexAttribArray( vTexCoord )
If array of UV texture coordinates are present in the model, this call activates vTexCoord attribute array in the vertex shader.
It is followed by a few additional texture-related calls:
glEnable( GL_TEXTURE_2D )
glBindTexture( GL_TEXTURE_2D, tex_buffer_ID )
glActiveTexture( GL_TEXTURE0 )
glUniform1i( texture )
glUniformMatrix4fv( umM, 1, GL_FALSE, &mM[0][0] )
Sets model matrix in the vertex shader.
glUniformMatrix4fv( umR, 1, GL_FALSE, &mR[0][0] )
Sets the rotation matrix to rotate normals in the vertex shader.
The lighting-related uniform variables are also set in the vertex shader at this stage.
The model is now ready for rendering:
glDrawElements( GL_TRIANGLES, total_indices, GL_UNSIGNED_INT, NULL )
If indices are specified, use indices to render the model
glDrawArrays( GL_TRIANGLES, 0, total_vertices )
Otherwise render the object without indices
The rendering is complete, detach and disable objects that were activated:
glDisableVertexAttribArray( vPosition/vNormal/vColor )
Invoked for vertices, normals, and colors.
glDisableVertexAttribArray( vTexCoord ) and glDisable( GL_TEXTURE_2D )
Invoked for UV coordinates.
GLuint VertexShaderId; GLuint FragmentShaderId; GLuint ProgramId; GLuint VaoId; // vertex array object name GLuint VboId; // vertex buffer object name GLuint ColorBufferId; void CreateVBO() { // Each vertex has four dimensions GLfloat Vertices[] = { -0.8f, -0.8f, 0.0f, 1.0f, 0.0f, 0.8f, 0.0f, 1.0f, 0.8f, -0.8f, 0.0f, 1.0f }; // Each vertex color has four components GLfloat Colors[] = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f }; GLenum ErrorCheckValue = glGetError(); glGenVertexArrays(1, &VaoId); glBindVertexArray(VaoId); glGenBuffers(1, &VboId); glBindBuffer(GL_ARRAY_BUFFER, VboId); glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(0); glGenBuffers(1, &ColorBufferId); glBindBuffer(GL_ARRAY_BUFFER, ColorBufferId); glBufferData(GL_ARRAY_BUFFER, sizeof(Colors), Colors, GL_STATIC_DRAW); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(1); ErrorCheckValue = glGetError(); if (ErrorCheckValue != GL_NO_ERROR) { fprintf( stderr, "ERROR: Could not create a VBO: %s \n", gluErrorString(ErrorCheckValue) ); exit(-1); } }