Harold Serrano

View Original

An introduction to the OpenGL Shading Language

Introduction

The OpenGL Shading Language (GLSL) is a programming language designed for OpenGL shaders. It is based on the ā€œCā€ programming language. As any other programming language, GLSL contains data types such as int, float, bool. It can handle loops and conditional statements like for-loops, while-loops and if-else statements. What makes it different from any other language is that it contains data types especifically designed to handle 3D mathematics. e.g., vec2, vec3, mat2, mat3.

The following discussion applies only to OpenGL ES 2.0

GLSL Data Types

The most common data-types in GLSL are: * Scalar Type * Vector Type * Matrix Type

Scalar Data Types

Scalar types include int, float and bool. Each representing the value of an integer, a floating point or a boolean, respectevely. For example:

int myInt=23;
float myfloat=2.0;
bool isTrue=true;

Vector Data Types

Vector types are data types designed to handle Vector operations. They are declared as: vec2, vec3, vec4. Each representing a vector with 2, 3 and 4 components, respectively. For example:

//Example of a vec2 vector
vec2 position=vec2(4.0,3.0);
//Example of a vec3 vector
vec3 color=vec3(1.0,3.0.2.0);
//Example of a vec4 vector
vec4 light=vec4(1.0,0.0,1.0,1.0);

Accessing a Vector component

The components of a vector can be accessed by using the ā€œ.ā€ notation and specifying the x, y, z or a component of the vector. For example:

//Declaring and defining a vec4 vector
vec4 light=vec4(1.0,0.0,1.0,1.0);
//Accessing the x-component of a vector
float xValue=light.x;
//Accessing the y-component of a vector
float yValue=light.y;
//Accessing the z-component of a vector
float zValue=light.z;
//Accessing the a-component of a vector
float alphaValue=light.a;

Matrix Data Types

Matrix types are data types designed to handle Matrix operations. They are declared as: mat2, mat3, mat4. Each representing a matrix of 2x2, 3x3, and 4x4 dimensions, respectively. For example:

//Example of a 2x2 Matrix
mat2 space=mat2(1.0,0.0,  //1st column
                0.0,1.0); //2nd column
//Example of a 3x3 Matrix
mat3 space=mat3(1.0,0.0,0.0,  //1st column
                0.0,1.0,0.0,  //2nd column
                0.0,0.0,1.0); // 3rd column
//Example of a 4x4 Matrix
mat4 space=mat4(1.0,0.0,0.0,0.0,  //1st column
                0.0,1.0,0.0,0.0,  //2nd column
                0.0,0.0,1.0,0.0,  //3rd column
                0.0,0.0,0.0,1.0); //4th column

Note: The components of the matrix are provided in column major order.

Accessing a Matrix component

mat3 space=mat3(1.0,0.0,0.0,
                0.0,1.0,0.0, 
                0.0,0.0,1.0);
 space[4][4]=3.0;  //1
 float value=space[4][4]; //2
 space[1]=vec3(2.0,1.0,0.0); //3
 vec3 newVector=space[1]; //4
  • Line 1. The bottom right component of a matrix is set to float value of 3.0.
  • Line 2. A float variable is set with the value of the bottom right component of the matrix.
  • Line 3. The first column vector of a matrix is set with a vector.
  • Line 4. A vector is set with the first column vector of the matrix.

GLSL Qualifiers

Variables in GLSL can take in different behaviors. Some variables can only receive data. Others can only provide data. Some variables can only be used in a vertex shader, while other variables can only be used in a fragment shader or both. To differentiate these type of variables, GLSL uses Type Qualifiers.

Attributes

A variable that can only receive data in a vertex shader is declared with the Attribute qualifier. 

//declaring an attribute in a shader
attribute vec4 light;

Varying

As you recall, in the first stage of the rendering pipeline, all data goes through the Vertex Processing stage. If a variable in a vertex shader needs to be passed down to the fragment shader, it needs to be qualified with the varying qualifier. These type of variables are passed down between the vertex and fragment shader during each rendering.

//declaring a varying variable in a shader
varying vec3 color;

Uniforms

Variables that are global and need to be used by both the vertex and fragment shader are qualified as Uniform.

//declaring a Uniform variable in a shader
uniform mat4 MVPMatrix;

Precision of variables

Variables can also be qualified with high, medium or low precision. For example:

//Declaring a vector with high precision
highp vec3 color=vec3(1.0,0.0,0.0);
//Declaring a vector with medium precision
mediump vec3 color=vec3(1.0,0.0,0.0);
//Declaring a vector with low precision
lowp vec3 color=vec3(1.0,0.0,0.0);

In the vertex shader, the use of a precision qualifier is optional. In a fragment shader the precision of a variable must be declared. The actual range of a precision qualifier is dependent on the specific OpenGL implementation.

GLSL Built-in Variables

There are two very important built-in variables in GLSL. Without their use in shaders, no rendering will occur. They are:

  • gl_Position;
  • gl_FragColor;

gl_Position

The gl_Position is used by the vertex shader to hand over the transformed vertex position to the OpenGL pipeline.

gl_FragColor

The gl_FragColor is used by the fragment shader to hand over the color of the fragment to the OpenGL pipeline.

The gl_Position and gl_FragColor variables are mandatory in the vertex and fragment shaders, respectively.

GLSL Statements

Like other programming languages, GLSL also supports conditional statements and loops such as:

  • if-else statements
if(i != 0) { 
    aFunction(); 
    } else { 
    bFunction(); 
    }
  • while loops
while(i <= 99) {
    aFunction();
    }
  • for loops
for(int i = 0; i <= 99; i++) { 
    aFunction(); 
    }
  • do loops
do { 
    aFunction(); 
    }while(i <= 99);