OpenGL Programming/Modern OpenGL Tutorial 03

= Attributes: pass additional vertex information =

We may need more than just plain coordinates in our program. For instance: colors. Let's pass RGB color information to OpenGL.

We passed the coordinates using an attribute, so we can add a new attribute for colors. Let's modify our globals:

And our init_resources:

Now in the  procedure, we can pass 1 RGB color for each of our 3 vertices. I chose yellow, blue and red, but feel free to use your favorite colors :)

Let's tell OpenGL once we're done with the attribute, at the end of the function:

Last, we declare it also in our vertex shader:

At this point, if we run the program, we'll get: Could not bind attribute v_color

This is because we didn't use v_color yet.

Problem is: we want to color in the fragment shader, not in the vertex shader! Let's now see how to...

= Varyings: pass information from the vertex shader to the fragment shader =

Instead of using an attribute, we'll use a varying variable. It's: So it's a communication channel between the two shaders. To understand why it's interpolated, let's see an example.
 * An output variable for the vertex shader
 * An input variable for the fragment shader
 * It's interpolated.

We need to declare our new varying, say, in both shaders.

In triangle.v.glsl:

and in triangle.f.glsl:

(Note: if you're using GLES2, check the section on portability below.)

Let's see the result:



Wow, there's actually more than 3 colors!

OpenGL interpolates the vertex value for each pixel. This explains the varying name: it varies for each vertex, and then it varies even more for each fragment.

We didn't need to declare the varying in the C code - that's because there's no interface between the C code and the varying.

= Interweaving coordinates and colors =

To better understand the glVertexAttribPointer function, let's mix both attributes in a single C array:

's 5th element is the stride, to tell OpenGL how long each set of attributes is - in our case 5 floats:

It works just the same!

Note that for colors, we start at the 3rd element of the array, where the first color is - that's the offset of the first element.

Why ? We saw that, in early versions of OpenGL, it was possible to pass a pointer to a C array directly (rather than a buffer object). This is now deprecated, but the  prototype remained unchanged, so we do as if we passed a pointer, but we pass an offset really.

An alternative for sport:

Note the use of  to specify the first color offset.

= Uniforms: pass global information =

The opposite of attribute variables are uniform variables: they are the same for all the vertices. Note that we can change them on a regular basis from the C code - but each time a set of vertices is displayed on screen, the uniform will be constant.

Let's say that we want to define the triangle's global transparency from our C code. As with attributes, we need to declare it. A global in the C code: Then we declare it in the C code (still after the program linking):

Note: we could even target a specific array element in the shader code with, e.g.  !

In addition, for uniform, we also explicitly set its non-varying value. Let's request, in, that the triangle be very little opaque:

We now can use this variable in our fragment shader:

Note: if you don't use the uniform in your code,  will not see it, and fail.

= OpenGL ES 2 portability =

In the previous section, we mentioned that GLES2 requires precision hints. These hints tells OpenGL how much precision we want for our data. The precision can be: For instance,  can often be used for colors, and it is recommended to use   for vertices.

We can specify the precision on each variable:

or, we can declare a default precision:

Sadly these precision hints do not work on traditional OpenGL 2.1, so we need to include them only if we're on GLES2.

GLSL includes a preprocessor, similar to the C preprocessor. We can use directive such as  or.

Only the fragment shader requires an explicit precision for floats. The precision is implicitly  for vertex shaders. For fragment shaders,  might not be available, which can be tested using the   macro.

We can improve our shader loader so it defines a default precision on GLES2, and ignore precision identifiers on OpenGL 2.1 (so we can still set the precision for a specific variable if needed):

Keep in mind that the GLSL compiler will count these prepended lines in its line count when displaying error messages. Setting  sadly does not reset this compiler line count.

= Refreshing the display =

Now it would be quite nice if the transparency could vary back and forth. To achieve this,
 * we can check the number of seconds since the user started the application;  gives that
 * apply the maths sin function on it (the sin function goes back on forth between -1 and +1 every 2.PI=~6.28 units of time)
 * before rending the scene, prepare a logic function to update its state.

In mainLoop, let's call the logic function, before the :

Let's add a new logic function

Also remove the call to  in.

Compile and run...

We've got our first animation!

It is common that OpenGL implementation wait for the screen's vertical refresh before updating the physical screen's buffer - this is called vertical sync. In that case, the triangle will be rendered around 60 times per second (60 FPS). If you disable vertical sync, the program will keep updating the triangle continuously, resulting in a higher CPU usage. We'll meet again the vertical sync when we create applications with perspective changes.

= Notes =