This section is compied from our GLSL undergraduate lab and provided as reference.
Goal: To give an overview of GLSL, vertex shaders and fragment shaders. Use this section as a reference when completing subsequent assignments.
GLSL code is similar to C code, but with a strong emphasis on computation.
Most GLSL code performs floating-point calculations. Common
datatypes used are float, vec2, vec3 and
vec4. These datatypes represent 1D, 2D, 3D and 4D vectors.
Arithmetic operations can be performed directly on these
datatypes.
For integer calculations (such as counting loop iterations),
int is available. The bool datatype is also
available.
A small GLSL function can look like:
vec4 applyDirectionalLight(vec3 normal, vec4 originalColor)
{
vec3 lightDirection = normalize(vec3(0.5, 0.8, 0.7));
float strength = dot(lightDirection, normal);
if (strength < 0.0)
strength = 0.0;
vec4 color = originalColor.xyxx * strength;
return color;
}
vec3(0.5, 0.8, 0.7) constructs a new vec3 from three
floating-point values.
dot() calls a predefined math function.
originalColor.xyxx performs "swizzling" on the original
vector: the result is a vec4 whose XYZW elements are taken
from the X, Y, X and X elements of originalColor,
respectively.
You can find a complete list of built-in mathematical functions in the GLSL Language Specification.
Vertex shaders perform per-vertex calculations. That's where
vertices are transformed, per-vertex lighting calculations are done,
and where skeletal animation systems do most of their work.
Fragment shaders perform per-pixel calculations. That's where texture
and lighting colours are combined into one final pixel colour value.
The code for a shader program is enclosed inside the main() function. It takes no arguments, and returns nothing. Communications between OpenGL, the vertex shader and the fragment shader is done by reading/writing global variables.
Variables can have a few different qualifiers:
const - the value is specified at compile-time; it is
read-only for OpenGL, vertex and fragment shaders.
uniform - the value is constant over an entire polygon; it is
read/write for OpenGL, and read-only for fragment and vertex
shaders.
varying - the value will be interpolated over the surface of a
polygon; read/write for OpenGL and vertex shaders, read-only for
fragment shaders.
attribute - a per-vertex attribute; OpenGL supplies this
(typically from a vertex/normal/texcoord array). Read-only for vertex
and fragment shaders.
All variables whose names begin with "gl_" are predefined by OpenGL. These are always present, and they can be used without declaring them first.
Here are some variables that available for reading in both vertex and fragment shaders:
|
Type |
GLSL symbol name |
Meaning |
|---|---|---|
|
uniform mat4 |
gl_ModelViewMatrix |
Contains the GL_MODELVIEW matrix |
|
uniform mat4 |
gl_ProjectionMatrix |
Contains the GL_PROJECTION matrix |
|
uniform mat3 |
gl_NormalMatrix |
Similar to gl_ModelViewMatrix, but used for transforming normals |
|
uniform vec4 |
gl_LightSource[i].position |
GL_POSITION setting for GL_LIGHTi |
|
uniform vec4 |
gl_LightSource[i].ambient |
GL_AMBIENT setting for GL_LIGHTi |
|
uniform vec4 |
gl_LightSource[i].diffuse |
GL_DIFFUSE setting for GL_LIGHTi |
|
uniform vec4 |
gl_LightSource[i].specular |
GL_SPECULAR setting for GL_LIGHTi |
The vertex shader is called once for every vertex that has been queued up for rendering via glBegin()/glEnd(), glDrawElements() or any other mechanism. It can read the current vertex attributes from the following global variables:
|
Type |
GLSL symbol name |
Meaning |
|---|---|---|
|
attribute vec4 |
gl_Color |
Vertex colour |
|
attribute vec3 |
gl_Normal |
Vertex normal |
|
attribute vec4 |
gl_Vertex |
Vertex position |
|
attribute vec4 |
gl_MultiTexCoord0 |
Vertex texture coordinate |
... and it can output a transformed and projected vertex, along with its attributes to:
|
Type |
GLSL symbol name |
Meaning |
|---|---|---|
|
vec4 |
gl_Position |
Transformed and projected vertex position (must always be written) |
|
varying vec4 |
gl_FrontColor |
Vertex colour value |
|
varying vec4 |
gl_TexCoord[i] |
Texture coordinate set i |
OpenGL will take the output from the vertex shader, interpolate the resulting values over the surface of any neighboring polygons, and then run the fragment shader once for every pixel which the polygon is supposed to render to. Any extra varying variables in the vertex shader will also be interpolated over the polygon, and the result is available to the fragment shader.
Here is a subset of variables which the fragment shader can read:
|
Type |
GLSL symbol name |
Meaning |
|---|---|---|
|
vec4 |
gl_FragCoord |
Screen coordinates for the fragment |
|
varying vec4 |
gl_Color |
Interpolated result from vertex shader's gl_FrontColor output |
|
varying vec4 |
gl_TexCoord[i] |
Interpolated result from vertex shader's gl_TexCoord[i] output |
The fragment shader can read these items, any extra varying variables, sample from textures, etc, and must then output a color value to the following variable:
|
Type |
GLSL symbol name |
Meaning |
|---|---|---|
|
vec4 |
gl_FragColor |
Final pixel colour (must be written!) |
You can find a full list of pre-defined variables in the GLSL Language Specification.
|
|