A short introduction to GLSL

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

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.

GLSL and shaders

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.