Help end child hunger

GLSL Tutorial – Color Example

 
Prev: Hello World Next: Lighting
 

In the previous example we saw how to transform the geometry sent from the application to draw a model with a color defined as a constant in the fragment shader. In here we’re going to explore some more flexible ways of setting color for a 3D model.

The first approach is to consider color as a vertex attribute. This implies that, in the application side, we must create another buffer to hold the color values per vertex, and add this buffer to the Vertex Array Object. On the vertex shader we’ll call this new vertex attribute as color, and declare it as an attribute, using the in keyword. We’re also going to declare an output variable called colorV, and we’re going to assign to it the value of the attribute color, so that is value is sent to the fragment shader.

The relevant lines are highlighted in the vertex shader:

#version 330

layout (std140) uniform Matrices {
	mat4 pvm;
} ;

in vec4 position;
in vec4 color;

out vec4 colorV;

void main() 
{
	colorV = color;
	gl_Position = pvm * position ;
}

A graphical primitive, such as a triangle, is defined with vertex attributes, namely position and, in this case, color. The color for each pixel inside the triangle will be the result of the interpolation based on the vertices’ colors and the value stored on gl_Position. For instance, in a triangle, if each vertex has a distinct color consisting of one of the RGB channels, the point in the middle of the triangle will be gray.

When the variable color finally arrives at the fragment shader it will hold the interpolated value. The fragment shader has no need to perform any computation, and the only difference regarding the previous example is that the output of the fragment shader is assigned to the fragment’s input colorV instead of a constant.

#version 330

in vec4 colorV;

out vec4 outputF;

void main()
{
	outputF = colorV;
}

One of the matching mechanisms for variables across shaders is name based. The variable has the same name in both shaders. colorV, being declared as out in the vertex shader and in in the fragment shader.

A varying color per vertex is not a common approach because in most cases the color of a model, or parts of it, is constant, and passing a constant color as an attribute is a waste of bandwidth and memory since the color must be replicated for every vertex. Furthermore, when considering more complex shading algorithms, the “color” of an object is determined, not by a single vec4, but by a set of properties, hence each vertex would require a lot of unnecessary memory.

Usually each OpenGL draw call has a single color associated with it, and to implement this we resort to uniforms. Before each draw call we can set the respective color using a uniform variable, regardless of vertex count.

Under this approach, the color is constant for every vertex in the draw call, hence interpolation is not required. We can use the uniform variable directly in the fragment shader, skipping the interpolation stage for this values.

The vertex shader is as follows:

#version 330

layout (std140) uniform Matrices {
	mat4 pvm;
} ;

in vec4 position;

void main() 
{
	gl_Position = pvm * position ;
}

our fragment shader needs to declare a uniform variable to hold the color, color. This variable can be set from the application. Until then we can define any particular color, for instance red (1,0,0,1), as its default value.

The fragment shader is as follows:

#version 330

uniform vec4 color = vec4(1.0, 0.0, 0.0, 1.0);

out vec4 outputF;

void main()
{
	outputF = color;
} 

From the OpenGL side we have a new task in this example: setting the uniform variable myColor. To achieve this we need first to query the location of the uniform variable and use that location to pass the required color value to the shaders. As shown in section Uniform Variables this can be done as follows:

float myFloats[4] = {1.0f, 0.8f, 0.1f, 1.0f};
// p is the program's name
GLint myLoc = glGetUniformLocation(p, "color");
glProgramUniform4fv(p, myLoc, 1, myFloats);

With VSL it is a little bit simpler since the library automatically keeps track of the locations of every uniform variable. In the previous OpenGL code all that we need to change/add are the highlighted lines in the function responsible for setting up the shaders.

GLuint setupShaders() {

	// Shader for models
	shader.init();
	shader.loadShader(VSShaderLib::VERTEX_SHADER, "shaders/color.vert");
	shader.loadShader(VSShaderLib::FRAGMENT_SHADER, "shaders/color.frag");

	// set semantics for the shader variables
	shader.setProgramOutput(0,"outputF");
	shader.setVertexAttribName(VSShaderLib::VERTEX_COORD_ATTRIB, "position");

	shader.prepareProgram();

	float c[4] = {0.0f, 1.0f, 0.0f, 1.0f};
	shader.setUniform("myColor", c);

	printf("InfoLog for Hello World Shader\n%s\n\n", shader.getAllInfoLogs().c_str());

	return(shader.isProgramValid());
}

As we’re playing with color we’re going to explore another option which in this case is particularly interesting. Our model from the previous example is a cube where local coordinates vary between 0.0 and 1.0. This is a match to the way color varies. Both have three components and their ranges match. So what would our cube look like if instead of a user defined color we did output its position in local coordinates?

The vertex shader to try this requires that we use a variable to store the local vertex coordinates, and pass it on to the fragment shader. This is similar to the first version, the only difference being that the value being assigned to this new variable is not the color attribute but the input vertex coordinates, or position.

#version 330

layout (std140) uniform Matrices {
	mat4 pvm;
} ;

in vec4 position;

out vec4 color;

void main()
{
	color = position;
	gl_Position = pvm * position ;
}

The fragment shader is similar to the first approach:

#version 330

in vec4 color;

out vec4 outputF;

void main()
{
	outputF = color;
}

and the final result is:

More than only playing with ways to paint a cube, this last approach can be one of the powerful debug strategies in our hands. As opposed to C, C++, or almost any other language, there is no console to print error messages or individual values. True, we can set a complex system with transform feedback and image load/store to get every piece of information we want from a shader, still nothing beats color output to eliminate some of the most common problems.

The above result tells us that we’re getting the correct coordinates in the vertex shader! This is not always as trivial to spot as in this case, still outputting data, coordinates in this case, as color is one way to troubleshoot a shader.

The source code and a Visual Studio 2010 project can be found in here. In the shaders source code we find preprocessor directives, similar to those in C or C++. This allow us to compact the two last scenarios in a single pair of shaders. Just uncomment/comment the define line in both shaders to try each approach.

 

Prev: Hello World Next: Lighting
 

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: