Shader Lib in Action
| Prev: Source | Next: Specification |
Shader Lib in Action
Usage of this lib should be pretty straightforward. First we declare a variable of type VSShaderLib.
VSShaderLib shader;
The first step to start using shader, is to call init. This creates the OpenGL program to which shaders will be attached to.
Afterwards we may load the shaders, providing a filename. The following example shows how to do this:
shader.init(); shader.loadShader( VSShaderLib::VERTEX_SHADER, "shaders/dirlightdiffambpix.vert"); shader.loadShader( VSShaderLib::FRAGMENT_SHADER, "shaders/dirlightdiffambpix.frag");
To bind a fragment output variable we can use function setProgramOutput. Let’s assume that the fragment shader has an output variable named outputF, and we want to bind it to output index 0. The following code shows how to do it:
shader.setProgramOutput(0,"outputF");
Next we can establish the semantics of the input attributes, for instance as shown in the following snippet:
shader.setVertexAttribName( VSShaderLib::VERTEX_COORD_ATTRIB, "position"); shader.setVertexAttribName( VSShaderLib::TEXTURE_COORD_ATTRIB, "texCoord"); shader.setVertexAttribName( VSShaderLib::NORMAL_ATTRIB, "normal"); shader.setVertexAttribName( VSShaderLib::TANGENT_ATTRIB, "tangent"); shader.setVertexAttribName( VSShaderLib::BITANGENT_ATTRIB, "bitangent");
Actually, we are establishing the locations of these attributes. Using pre defined locations, makes life easier when we want to use a shader to draw multiple objects created with different libs or functions.
After we have set the locations for both fragment outputs and input attributes, we can call prepareProgram. This is a long function, that links the program, and gets all uniform data, including names, locations, data types, offsets, and so on.
shader.prepareProgram();
After this step the shader is ready to be used.
To set uniform values for an int we can do as follows:
shaderTess.setUniform("texUnit", 0);
The function takes the name of the uniform, and its value. There are variations of this function for floats and pointers (for vecs and matrices).
Another scenario is when a uniform is in a block. For instance consider the following block:
layout (std140) uniform Material {
vec4 diffuse;
vec4 ambient;
vec4 specular;
vec4 emissive;
float shininess;
int texCount;
};
In this case we can use the following function to set the diffuse member of the block:
float diffuse[4] = {0.8f, 0.8f, 0.8f, 1.0f};
shader.setBlockUniform("Material", "diffuse", diffuse);
It is also possible to set the full block at once. The following snippet of code shows how. Note that we must have a data structure that maps exactly how the memory is laid out in the uniform block. In the case of the block above this is pretty simple.
struct MyMaterial{
float diffuse[4];
float ambient[4];
float specular[4];
float emissive[4];
float shininess;
int texCount;
};
...
MyMaterial myMat;
...
shader.setBlock("Material", &myMat);
Beware with byte alignment. Block are great, but they must be handled with care. Always check the offsets and strides to make sure your data in the application matches the data in the block. This is also true when setting individual uniforms. Always check your strides, particularly for matrices. For instance a mat3 declared in a uniform block requires the same memory as a 3×4 matrix. This implies that to set a mat3 in a block we should provide a 3×4 matrix, where the last element of each column, for column major matrices is ignored.
If you’re having problems with uniforms inside a block, just try placing that uniform outside the block. If the error is gone, then, most likely the problem is related to the data stride.
Once all uniforms are set and we want to use our program for rendering then we can write:
glUseProgram(shader.getProgramIndex());
| Prev: Source | Next: Specification |
