Shaders

Once the vertex data is uploaded, a shader program needs to be run to process the data. Shader programs are programs that run on the GPU and are very similar to CPU programs. They reside in memory and can access data structures in memory, they are composed of assembly instructions (GPU assembly), and they run on a processor (the GPU execution units). They differ in that shader programs run as part of the graphics pipeline and have defined roles in processing graphics. They also cannot currently recurse or access heap memory.

The two primary shaders are the vertex shader and the fragment shader. The vertex shader takes as input vertex attributes and outputs at least a vertex position. The fragment shader takes as input interpolated vertex attibutes and outputs a color (almost always). The pipeline looks like this:

The shaders are the blue boxes. The white boxes represent automatic steps that take place. After the vertex shader, the vertex attributes are interpolated across the primitive surface. These interpolated fragments are the input to the fragment shader. After the fragment shader, the fragments are composed into the final output image.

Shader syntax

We will use GLSL as our shading language. It has C style syntax with flexible graphics data types and functions. Loop and conditional constructs are similar to C: if, for, while, etc. Here is an example vertex and fragment shader in GLSL. Vertex shader:

in vec2 pos;
in vec3 color;
out vec4 smoothColor;

void main()
{
	gl_Position=vec4(pos.xy, 0, 1);
	smoothColor=vec4(color.xyz, 1);
}
Fragment shader:
in vec4 smoothColor;
out vec4 fragColor;

void main()
{
	fragColor = smoothColor;
}

Types

GLSL supports scalars such as:

float f; //single precision float
int i;   //signed int
uint u;  //unsigned int
bool b;  //boolean
It also supports vector types in the format [type]vec{n} where type is the datatype and n is the size:
vec4  a; //vector of 4 float
ivec3 b; //vector of 3 signed int
uvec2 c; //vector of 2 unsigned int vector
bvec2 d; //vector of 2 boolean vector
Vectors components are accessed with the .xyzw, rgba, or stpq members:
vec4 a
a.x = 4.0;     //set first component to 4
vec2 b = a.yz; //make vec2 from the middle components
vec2 c = a.gb; //make vec2 from the middle components
Vectors and scalar can be used naturally:
vec4 a;
vec4 b;
vec4 c = a * b; //component wise multiply of a and b

float d;
vec4 c;
vec4 e = d * c; //scale c by d
Variables can be created with constructors:
float a = float(4);
vec4 b = vec4(1, 2, 3, a);
vec4 c = vec4(1, b.yz, a);
Input and output variables require qualifiers to describe their use. Vertex shader input and output:
in type x;  //an input attribute
out type y; //an output attribute for interpolation stage
Fragment shader:
in type z;  //an input interpolated fragment (matches vertex output)
out type w; //an output color

Making shaders

Shaders are created with the following OpenGL functions:

I provide a class that will use these to create shaders for you: ShaderManager. Invoke it like this:
shaderHandle = ShaderManager::shaderFromFile(vertPath[], fragPath[], vertPathCount, fragPathCount).

After creating a shader, you may then use the returned handle:

glUseProgram(shaderHandle); //activate a shader program
... draw stuff ...
glUseProgram( 0 );          //deactivate all shader programs

Uniform values

Uploading data to the GPU is time consuming, but you can upload small values that change rarely without too much cost. These small values are generally constant and are called 'uniform' values. You can specify uniforms in shaders with the uniform qualifier. You can get and use uniforms on the host as follows:

GLint uniformSlot = glGetUniformLocation(shaderHandle, "uniform name in shader");
glUniform1f(uniformSlot, single_value_to_upload);
The suffix on glUniform controls the details of type and size of the upload.