OpenGL Programming/Modern OpenGL Tutorial 02

Now that we have a working example that we understand, we can start adding new features and more robustness to it.

Our previous shader was intentionally minimal so it's as easy as possible, but real-world example use more auxiliary code.

= Managing shaders =

Loading shaders
The first thing to add is a more convenient way to load shaders: it would be much easier for us to load an external file (rather than copy paste it as a C string in our code). In addition, this will allow us to modify the GLSL code without recompiling the C code!

First, we need a function to load a file as string. It's basic C code, it reads a file's contents into an allocated buffer of the size of the file. We rely on SDL's RWops rather than a plain stream, because it supports transparent file loading from Android assets system.

Debugging shaders
Currently if there's an error in our shaders, the program just stops without explaining what error in particular. We can get more information from OpenGL using the infolog:

Abstracting differences between OpenGL and GLES2
When you only use GLES2 functions, your application is nearly portable to both desktops and mobile devices. There are still a couple issues to address:
 * The GLSL  is different
 * GLES2 requires precision hints that are not compatible with OpenGL 2.1.

The  needs to be the very first line in some GLSL compilers (for instance on the PowerVR SGX540), so we cannot use   directives to abstract it in the GLSL shader. Instead, we'll prepend the version in the C++ code: Since we use the same version of GLSL in all our tutorials, this is the most simple solution.

We'll cover  and precisions hints in the next section.

Note: at least one environment (VirtualBox 5.1 3D acceleration with Windows 7 guest) does not support split sources, in which case they need to be strcat'd first. Further testing is welcome.

A reusable function to create shaders
With these new utility functions and knowledge, we can make another function to load and debug a shader:

We now can compile our shaders using simply: as well as display link errors:

Place the new functions in a separate file
We place these new functions in.

Note that we intend to write as few of these functions as possible: the OpenGL Wikibook's goal is to understand how OpenGL works, not how to use a toolkit that we develop.

Let's create a  header file:

Reference the new file in :

And in the :

= Using Vertex Buffer Objects (VBO) for efficiency =

It is good practice to store our vertices directly in the graphic card, using a Vertex Buffer Object (VBO).

In addition, "client-side arrays" support is officially removed since OpenGL 3.0, not present in WebGL, and is slower, so let's use VBOs from now on, even if they are slightly less simple. It's important to know about both ways, because this is used in existing OpenGL code that you may come across.

We implement this in two steps:
 * create a VBO with our vertices
 * bind our VBO before calling

Create a global variable (below the ) to store the VBO handle:

Move the triangle_vertices definition from the render function and place it at the beginning of the init_resources function. Then create one (1) data buffer and make it the current active buffer: We now can push our vertices to this buffer. We specify how the data is organised, and how often it will be used. GL_STATIC_DRAW indicates that we will not write to this buffer often, and that the GPU should keep a copy of it in its own memory. It is always possible to write new values to the VBO. If the data changes once per frame or more often, you could use GL_DYNAMIC_DRAW or GL_STREAM_DRAW.

At any time, we can unset the active buffer like this: glBindBuffer(GL_ARRAY_BUFFER, 0);. In particular, make sure you disable the active buffer if you ever have to pass a C array directly.

In, we slightly adapt the code. We call, and modify the last two parameters of  :

Let's not forget to clean-up on exit:

Now, each time we'll draw our scene, OpenGL will already have all the vertices on the GPU side. For large scenes, with thousands of polygons, this can be a huge speed-up.

= Check the OpenGL version =

Some of your users might not have a graphic card that supports OpenGL 2. This will probably lead your program to crash or display an incomplete scene. You can check this using GLEW (after a call to glewInit succeeds):

Note that some tutorials might just work with some nearly-2.0 cards, such as Intel 945GM which has limited shaders support but official OpenGL 1.4 support.

= SDL error reporting =

Let's print a precise error message when something goes wrong during initialization:

= Alternative to GLEW =

You may meet the following headers in other OpenGL code:

If you do not need to load OpenGL extensions, and if your headers are recent enough, then you can use this instead of GLEW. Our tests showed that Windows users may have outdated headers, and will miss symbols such as GL_VERTEX_SHADER, so we'll use GLEW in these tutorials (plus we'll be ready for loading extensions).

See also the comparison between GLEW and GLee in the APIs, Libraries and acronyms section.

A user reported that using this technique instead of GLEW on an Intel 945GM GPU allowed to bypass the partial OpenGL 2.0 support for simple tutorials. GLEW itself can be made to enable the partial support by adding  before the call to.

= Enabling transparency =

Our program is more maintainable now, but it does exactly the same thing as before! So let's experiment a bit with transparency, and display the triangle with an "old TV" effect.

First, explicitly request an alpha channel in our OpenGL context (doesn't seem necessary, but just in case):

Then explicitly enable transparency (it's off by default) in OpenGL. Add this to before mainLoop:

And last, we modify our fragment shader to define alpha transparency:

is a common maths operator, used to determine if we're on an even or an odd line. Hence one line out of two is transparent, the other is opaque.