![]() |
VOOZH | about |
In this article we'll see how to render a triangle using OpenGL. A triangle is probably the simplest shapes you can draw in OpenGL after points and lines and any complicated geometry that you make will me made up of number of triangles joined together.
We'll be using the programmable pipeline, so we'll be writing simple shader programs as well and compiling them and using them later for rendering. Now this will make our program a bit lengthy but the cool thing is we have do this only once and then reuse the code we've already written. This is actually true for most of the OpenGL code and hence we tend to write most of OpenGL codes in different functions and reuse them multiple times later.
The libraries that we'll be using for this is Glew and Glut and Ç++ as our programming language.
Setting up the Environment
The complete Program(Explanation follows after the program)
Output
Now when you run the program you should have a red triangle on the screen that should look something like the image below.
main() Function
Main function sets up our window. We initialize glut, glew, specify windows size, its position on the screen, specify display mode(which just tells what color space will be used by the window and will it use single or double buffer), set windows title, set the callback method that glut will use to draw stuff on this window and finally make the window visible.
loadDataInBuffers() Function
A triangle is made up of three coordinate points. These points(stored as floating point array) needs to be sent to graphics processor memory.
The way we refer to a location in system memory is relatively easy, just get a pointer to the memory location but using your GPU's memory is not that straightforward.
We do this by something known as buffer objects.
Buffer objects are the way you upload your data to the V-RAM and refer to it later.
There are usually three steps that you need to follow to get your data into the video memory.
Remember that OpenGL expects vertex coordinates in the range of [-1, 1]. Anything outside this range is clipped. However, we are free to choose our own coordinate system in-fact, it is a very common practice to define our own coordinate system and define our objects according to this coordinate system and later change it to the OpengGL coordinate system.
But in this post we'll travel down the easier road and specify our triangle's coordinates in [-1, 1] range. FYI coordinates in these range are known as normalized device coordinate(NDC).
Shaders
Throughout the rendering pipeline the input data(here vertex coordinates) go through various stages. We control these stages using something called Shaders
Shaders are programs that execute on GPU. Shaders in OpenGL are written in a special language commonly known as GLSL(OpenGL shading language) which you will come to notice is very similar to C and C++. Shaders give you direct control over the graphics pipeline. GLSL was formally included into the OpenGL 2.0 core in 2004 by the OpenGL ARB.
The two shaders that you will need all the time are
The shader code is stored as strings global variable. We'll link this variable to the data in our openGL program later. The vertex position are passed to gl_Position without modification.
Note: It is a common practice to put your shader codes in different files and later store that files contents in string variables.
After you done writing your shader you need to create shader objects, attach their corresponding source code, compile them and check for errors if there are.
compileShaders() Function
This function compiles the shader code, checks for error and creates shader object and returns its id.
We'll call this function once for vertex and fragment shader
linkProgram() Function
Shaders that you write for rendering needs to clubbed together into a shader program.
This function clubs these shader objects into a shader program, links them(that sort of creates an executable file for that program) and check for errors that may occur in this process.
init() Function
It uses all the above functions and puts everything together.
We use what is known as VAO(Vertex Array Objects).
VAO: A Vertex Array Object (VAO) is an OpenGL Object that stores all of the state needed to supply vertex data. It stores the format of the vertex data as well as the Buffer Objects providing the vertex data arrays. Note that VAOs do not copy, freeze or store the contents of the referenced buffers, if you change any of the data in the buffers referenced by an existing VAO, those changes will be seen by users of the VAO.
A shader program assigns to each of the input variable of vertex shader(aka vertex attributes) and uniform variables(variables whose values do not change for the primitive we are rendering) a position that we need to know if we want to link them to their data source.
display() Function
Remember we haven't yet told GPU to start rendering. display Function is what gives this command to the GPU.
Glut calls this callback function whenever it needs to draw stuff to the screen.