Show Menu


Shaders supersede outmoded rendering functions of older OpenGL versions, and can handle a wide variety of rendering operations, such as transformation matrices and lighting.

To bring shader capabilities into our engine, we designed and built a system that will allow for flexible ease of use while maintaining high performance. This system is built around a few goals that we have outlined for our project:

The first goal, to eliminate hard-coded-shader implementation, means that the engine must support any given shader parameter as defined in the shader source. Because Clairvoyance is not aware of what shaders it will be using for a client application, it is important that a programmer can relay the necessary information on how to handle a shader at the time he is writing his code.

Reflection Shader

Reflection Shader

The values stored in a shader can be generalized into two categories: attributes, which refer to in and out variables, and uniforms, which are constants that can be shared across multiple programs.

Clairvoyance recognizes a variety of shader attributes predefined by GLSL, such as vertex position, normals, colour, and texture coordinates. Clairvoyance sets up these attributes as per the guidelines outlined by nVidia. Predefined attributes have a reserved location (index) that is used to bind them to a program, and the engine binds them appropriately. By binding the predefined attributes, we eliminate the need for a user to perform this action himself.

for(size_t i = 0; i < numAttributes; i ++)
       const GLAttribute& attribute = sPredefinedGLAttributes[i];
Refraction Shader

Refraction Shader

Uniforms are a major component of any shader: common uniforms include the world, view, and projection matrices, surface material properties, like ambience and shininess, as well as lighting properties. Because there is such a wide range of possible uniforms, it is impossible to account for them all. Clairvoyance allows for users to provide a uniform definition in a client application so that it can be executed properly. Using a common uniform is made easy – the programmer need only define the uniform and link it to the appropriate enumeration:

sp->queueShaderConstantDefinition("model_matrix", MAT4);
sp->queueShaderConstantDefinition("view_matrix", MAT4);
sp->queueShaderConstantDefinition("projection_matrix", MAT4);
sp->getDefaultProgramParameters()->setAutoConstant("model_matrix", ShaderProgramParameters::MODEL_MATRIX);
sp->getDefaultProgramParameters()->setAutoConstant("view_matrix", ShaderProgramParameters::VIEW_MATRIX);
sp->getDefaultProgramParameters()->setAutoConstant("projection_matrix", ShaderProgramParameters::PROJECTION_MATRIX);

Our second goal is to allow for an unlimited number of shader programs to be used in the client application. This means that a user can define as many shaders as he needs when building his application to achieve whatever effect he is aiming for. This is important because it allows for individual shaders to be compounded into multiple shader programs, and for multiple shader programs to exist simultaneously.

To achieve this, we built the shader system to be as abstract as possible, and made intelligent use of code patterns. For example, we decoupled a shader from its parameters, allowing for a set of parameters to be defined and shared across different programs. Manager classes handle shaders and shader programs to provide efficient creation and management of these objects.

ShaderProgramManager* spm = ShaderProgramManager::getInstance();
ShaderProgramPtr sp = spm->createShaderProgram("My Program");

Thirdly, we wanted to make using shaders convenient for the programmer. At an end-user level, a programmer does not have to deal with how the back-end of the engine works. This includes details on how the uniforms and attributes are linked to the shader source, how these parameters are updated, or how a shader gets used.

Clairvoyance deals with shader communication through a variety of classes. An important example is the AutomaticParameterSource class, which automatically pulls important values from the application, such as the world, view, and projection matrices, and passes them into the shader.

Dispersion Shader

Dispersion Shader

Overall, the system works well, and provides a level of convenience that we require for meeting our game specifications.

Using Shaders

Shaders supersede outmoded rendering functions of older OpenGL versions, and can handle a wide variety of rendering operations, such as transformation matrices and lighting.