Shader Management

Shader management for AnimEngine is bizarre and unfortunately a little bit cumbersome. AnimEngine includes a python script within it’s project source called jsify_shaders.py. This script reads shaders from the shaders/ directory and assembles a single JSON files containing the bulk of shaders from directory.

The script also attempts to identify which shaders should be linked together to create a complete shader program in OpenGL. These shaders are grouped together and the ShaderLibrary class within AnimEngine’s C++ automatically loads, compiles, and links these programs as such. This makes setup of shaders in the code implicit, but also requires extra intervention by the shader coder when creating new shaders.

Creating New Shader Pairs

The most typical use case for creating new shaders is in creating a new vertex shader fragment shader pair. The easiest way to do so is by creating two files with the same filename and different file extensions. One for the vertex shader and one for the fragment shader. For example, if you wanted to create a new shader program for drawing a heightmap displaced model you might create two new files:

heightmap.vs
heightmap.fs

jsify_shaders.py will detect the matching names, and automatically link together the vertex and fragment shaders at startup. The resulting shader program is stored in the ShaderLibrary singleton instance under key "heightmap.

Alternatively, if you wanted to create a shader program that re-used an existing vertex shader with a new fragment shader you can note an explicit pairing in the fragment shader source code. For example, if you want a shader program named "normalMap" that uses vertex shader standard.vs and fragment shader normalMap.fs you would add the following line to the top of normalMap.fs:

// PAIR: vert_standard

jsify_shaders.py will detect this declaration and pair the two shaders.

Warning

The // PAIR: ... statement must appear as the very first line of the GLSL file. Any deviation will be missed by jsify_shaders.py.

Using Built Shader Programs In The Code

Shaders gathered by jsify_shaders.py are ultimately stored as entries in a table where they are associated with their names. In order to use your shader you must first get a reference to the singleton instance of ShaderLibrary using ShaderLibrary::getInstance().

A shader can then be activated by calling ShaderLibrary::makeActive("shaderName") where shaderName is the name of the shader you wish to activate. This name is typically the name of the shader’s source files without a file extension. In the heightmap example in the previous paragraph this name would be "heightmap".

Calling makeActive(...) will find the requested program and call glUseProgram on it’s PID. If you need access to the Program object itself, you can use either ShaderLibrary::getActive() or ShaderLibrary’s overloaded [] operator.

A typical example of this follows:

ShaderLibrary& shlib = ShaderLibrary::getInstance();
shlib.makeActive("heightmap");

glUniform1f(shlib.getActive().getUniform("displaceScalar"), displaceStrength);

draw();