Filip Smola

Categories

  • Open Sea

Tags

  • C++
  • GLSL
  • OpenGL
  • shaders

To simplify creation and usage of OpenGL shaders, I have now added the ShaderProgram class to the new open_sea::gl namespace. It represents a single shader program with attached shaders. The shaders currently supported by the system are vertex, geometry, fragment and (if the OpenGL context version is at least 4.0) tessellation shaders. The relevant pull request is available here.

Shader Program Creation

The program gets created as soon as you call the ShaderProgram() constructor. This creates an empty shader program and stores a reference to it. After that, you need to attach the actual shaders for the program to have any effect. This is done by calling either attach***File(path) or attach***Source(src) methods, where *** is the shader type. The file methods expect a path to the file that contains the shader source, the source methods expect the actual source as a string. If you try to attach a shader that is not supported by your context (i.e. tessellation shaders on context versions < 4.0), nothing will be attached but the system will act as if the attachment went through (the fact is however logged). This results in that shader being skipped.

After attaching any shader, the program needs to be linked before use. You can do that by calling the link() method. In case you are not sure whether the program is already linked, there is the isLinked() method, however calling link() on a linked program doesn’t try to link the program again.

If you wish to validate the program before use, call the validate() method. All of these methods fairly closely mirror the relevant OpenGL functions and contain logging of any potential errors.

Changing Shaders

In case you want to change a shader attached to the program, you can just attach the new one as you did the original one. This will not create a new shader object, but rather replace the source of the already existing object, recompile and reattach it. After doing this, the program will need to be linked again.

If you want to remove a certain shader without replacing it, use the detach***() methods where *** is the type of shader to remove. This detaches and deletes the shader. All shaders are detached and deleted on shader program destruction.

Using the Shader Program

To use the shader program, call the use() method. This just tells OpenGL to start using the program. When you no longer want to use this shader program, call unset(), which tells OpenGL to not use any shader program (in fact to use the program with ID 0).

There are also two convenience methods for getting the uniform and attribute locations, named accordingly. For the purposes of comparison, two shader programs are equal if their IDs are equal.

Debug UI and Other Functions

There is also a new debug window dedicated to OpenGL statistics. Currently it pulls statistics from the ShaderProgram class on how many shaders of each type there are.

To enable debug logging for OpenGL errors, call the log_errors() function. This enables OpenGL debug messages and attaches a listener to them that writes them to the log.

Conclusion

This addition makes creation of shader programs just a couple of lines, while maintaining a high level of error checking and logging. Anything that could fail in the compilation of shaders, and linking and validation of shader programs is checked and any errors are appropriately logged. In the future, I will also add support for compute shaders, but those will be in a separate objects as their use is fairly different from the other shader types.