Creating a GLSL Library
A Texturing ExampleThe vertex shader used in this example isn't very exciting so lets just get it out of the way.
/**
* \file Texturing.vert
*/
void main()
{
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = ftransform();
}
All the vertex shader does is copy the multi-texture coordinate over for the fragment shader to use. The attribute array gl_MultiTexCoord0 is not available in the fragment shader so we need to populate the gl_TexCoord[0] value with it. Then the vertex position gets transformed according to the current model view matrix.
Now its time to write our Texturing library function. There are six options for how to texture a geometry in OpenGL
/**
* \file Texturing.frag
*/
const int REPLACE = 0;
const int MODULATE = 1;
const int DECAL = 2;
const int BLEND = 3;
const int ADD = 4;
const int COMBINE = 5;
void applyTexture2D(in sampler2D texUnit, in int type, in int index, inout vec4 color)
{
// Read from the texture
vec4 texture = texture2D(texUnit, gl_TexCoord[index].st);
if (type == REPLACE)
{
color = texture;
}
else if (type == MODULATE)
{
color *= texture;
}
else if (type == DECAL)
{
vec3 temp = mix(color.rgb, texture.rgb, texture.a);
color = vec4(temp, color.a);
}
else if (type == BLEND)
{
vec3 temp = mix(color.rgb, gl_TextureEnvColor[index].rgb, texture.rgb);
color = vec4(temp, color.a * texture.a);
}
else if (type == ADD)
{
color.rgb += texture.rgb;
color.a *= texture.a;
color = clamp(color, 0.0, 1.0);
}
else
{
color = clamp(texture * color, 0.0, 1.0);
}
}
At the top of the code we create a set of constant integers to denote what type of texturing we want to apply to the geometry. The function itself takes four arguments. The first is a sampler2D which is the texture unit holding the texture we want to apply. Second is the type which is one of the constant integers we defined. Third is the index of the texture coordinates we wish to use. And finally color is the current color of the texel which will be modified and returned from the function. The code itself is taken from the Orange Book and can also be created using 3Dlabs ShaderGen tool.
So now we have a function that will replicate OpenGL's texturing facility. Now we just need to create the other fragment shader that uses this function.
/**
* SingleTexture.frag
*/
void applyTexture2D(in sampler2D texUnit, in int type, in int index, inout vec4 color);
uniform sampler2D TexUnit0;
uniform int TexturingType;
void main()
{
vec4 color = gl_Color;
applyTexture2D(TexUnit0, TexturingType, 0, color);
gl_FragColor = color;
}
The most important thing to remember when creating a shader library is to use forward declarations. The applyTexture2D function is in a different file so when this file is compiled the compiler needs to have knowledge that there is a function called applyTexture2D and what its parameters are. When the shaders are linked together the linker will figure out the rest.
Other than that the shader is fairly straightforward. There are two uniforms, the sampler2D which just points to the texture unit we wish to use, and the integer TexturingType which denotes what OpenGL texturing to emulate. The main function just takes the color of the vertex and passes that along with TexUnit0, TexturingType and the index of the texture coordinates to the function. The result of which is used as the fragment color. Its worth noting that OpenGL does not provide a mechanism to query what the environment of the texture is (minus the texture environment color, gl_TextureEnvColor[]). So if you send via glTexEnv that you want to use GL_ADD, there is no way inside the shader to determine that is being requested by the program.
|