Introduction and getting help

First of all, you shall have read the Getting Started section, so that you have the tutorial code and can build and run the first tutorial. Most of what we do in this tutorial is covered in the first lecture. If you need more help on OpenGL, have a look at:

In this tutorial we will start familiarizing ourselves with OpenGL. To do this, there is a simple OpenGL application that you will study. Look at the source-code (lab1_main.cpp). Make sure you read the comments. At the end of this tutorial you should understand all of this as you will need it for future tutorials.

Task 1: Learn to debug.

The first thing we are going to do is to insert a bug in our program! Locate the display() function. The first thing we do there is to clear the screen. We say that we want to clear the color and the depth buffers of our window:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Change this to something illegal, for example:
glClear(GL_BUFFER);

Start State Then press F5 to run the program. You should now be informed that your program has triggered a breakpoint. Press the "break" button and we will use the debugger to find out what went wrong. The little yellow arrow tells you at which line the debugger noticed that something was wrong. This time the program broke because an OpenGL error occured. Look at the console window that starts with your application and you will see that you have actually written out that there was an error, before breaking.

Start State Now find the window called "Call Stack". If you read this list bottom-up, you can see the functions your program has been in before causing the error. Apparently, main() called display(), which called some internal driver stuff that eventually lead to a crash. Double click the "display()" line.

This will take you to the OpenGL call that crashed the code. Apparently, some moron has given the wrong input to the glClear() function. Stop the program (Shift-F5), fix this bug (just reset it to what it was) and run the program again to make sure everything works.

Task 2: Interactive UI elements

To get some interactivity in the tutorials we use the library ImGui to create an overlay with UI elements. These elements, e.g. buttons and sliders, can be used to control various settings and parameters in the coming tutorials. To showcase the basic principles of how it works three sliders have been added to this tutorial. Use them to control the background color of the window. Enable this overlay by uncommenting the gui() call which is right after the call to display() in the main loop.

Run the program again. You should now see a new little window where you can change the color of the background. Look at the gui() function and see that you understand how this happens. You can move, resize minimize this window, and your application will remember this for the next time you run the program.

Task 3: Learn how a triangle is drawn.

Start State The program we will work with currently draws a single, white, triangle on screen, similar to the image on the right. Begin by taking a look at the source code.

Exercise 1

The code initializes vertex buffer objects in three steps, as shown in the left column below. In the right column, fill in the correct description of what the function does from the drop-down menu.

Function Description
glGenBuffers()
glBindBuffer()
glBufferData()

Task 4: Colors

Now, your first task is to change the color of the triangle. If you only change the values in the appropriate vertex buffer object, you’ll notice that nothing happens. The reason is that the vertex- and fragment-shaders are not complete yet. We’ll fix this in the following steps! Take a look at the simple.vert file. The vertex color is declared as the attribute color in the beginning of this shader, but from then on it is ignored. We’ll need to pass the vertex color value on to the fragment shader so declare a second output from the vertex shader (before main()) like this:

out vec3 outColor;
Then, set this output variable in the main() function:
outColor = color;
Now, open the fragment shader (simple.frag) and tell it to expect a variable outColor from the vertex shader:
in vec3 outColor;
And then change this line that currently sets all fragments to be white:
fragmentColor = vec4(1.0, 1.0, 1.0, 1.0);
to instead use the color passed in from the vertex-shader:
fragmentColor.rgb = outColor;
There! Now make sure your vertex buffer object has some fun colors in it and run the program again.

Excercise 2

Somehow the data from your colorBuffer vertex buffer object ends up in the `color’ attribute in the vertex shader (simple.vert). Similarly, the positionBuffer vertex buffer object data ends up in the `position’ attribute. The chart below shows how the program links these different parts together. Start State Objects here are either buffer objects (BO) or vertex array objects (VAO). Specify the type of the objects by writing BO or VAO in the chart in the “Type:” fields. The table below lists four OpenGL calls. Each call corresponds to a link in the chart. Identify which link by writing the numbers 1-2 in the table!

Link Number OpenGL Call / Code
glBindBuffer( GL_ARRAY_BUFFER, colorBuffer );
glVertexAttribPointer(1, 3, GL_FLOAT, false/*normalized*/, 0/*stride*/, 0/*offset*/ );
glEnableVertexAttribArray(1);
glBindBuffer( GL_ARRAY_BUFFER, positionBuffer );
glVertexAttribPointer(0, 3, GL_FLOAT, false/*normalized*/, 0/*stride*/, 0/*offset*/ );
glEnableVertexAttribArray(0);

Excercise 3

The vertex shader (simple.vert) outputs a color (outColor), which is passed to the fragment shader. How is the color output from the vertex shader linked to the color input of the fragment shader? (pick one of the following three alternatives)
using the glBindFragDataLocation() call
by name (i.e. both times the variable is named 'outColor')
by pure luck. Sacrificing cute animals in the name of the Red Book may help.

Task 5: More triangles!

Now, your task is to draw a second triangle. This shall be accomplished by creating a new vertex array object in the initGL() function and then draw that object in the display() function. The second triangle must not cover the existing triangle, you are otherwise free to place both triangles as you please.

Next, you shall add a third triangle to the scene. This time, you shall not create a new VAO. Instead, simply add position and color data to the previous VAO. Again, this triangle may not overlap any of the previous triangles.

The final solution might look something like the picture below.

Solution State

Exercise 4

How many times are the vertex- and fragment-shader’s main() functions executed when a single frame is rendered? (refer to image above)

Vertex Shader Fragment Shader
A single time per frame
Once for each vertex (here: 9 times)
Approximately once for each drawn pixel in the image

Exercise 5

The vertex- and fragment-shaders are linked together into a single program object, which is used (glUseProgram()) to draw the triangle. The chart below illustrates the process. Complete the chart by filling the “Created with:” fields with the name of the OpenGL function that is used to create the objects. Next associate the numbers 1-4 and the letters A-C with the correct OpenGL calls in the table below.

Solution State
Link (number/letter) OpenGL Call/Code
glLinkProgram()
glCompileShader()
glCompileShader()
glShaderSource()
glShaderSource()
glAttachShader()
glAttachShader()

To reiterate; the purpose of this tutorial is to understand how the triangles end up on screen. Working in groups it may be a good idea to interrogate one another, let one explain to the other(s) what each line does, the person listening should make sure that he or she is satisfied that the answer really explains what is going on. And again if the OpenGL specification fails to help, ask an assistant!

When done, show your result to one of the assistants. Have the finished program running and be prepared to explain what you have done.