12.2 - GLSL Data Types and Variables

GLSL allows for three basic types of data:

GLSL facilitates the manipulation of vectors and matrices. A vector or matrix is always composed of values of the same basic data type. If a data type starts with a ‘b’ it contains Boolean values; if it starts with an ‘i’ it contains integer values; if it starts with anything else it contains floating point values. The vector and matrix data types are:

There are three other specialized data types:

Precision of int and float Data Types

Most programming languages define the range of values that can be stored in a specific data type. For example, an “int” in Java can store values in the range -2,147,483,648(-231) to 2,147,483,647 (231 -1). GLSL let’s you pick from three different precision levels for the basic data types.

The following precisions are minimum requirements. The GPU hardware can use more precision if it wants to, but never less. If a GPU can’t support a program’s requested precision, it will fail to compile. Don’t worry too much about the precision of data types, because this concept goes away in higher versions of GLSL.

Boolean values do not have a precision. A boolean value is either true or false.

Integers can have one of three possible precisions:

precision Range of values Specific Range
lowp (-28,28) -256 ... +256
mediump (-210,210) -1024 ... +1024
highp (-216,216) -65,536 ... +65,536

Floats can have one of three possible precisions:

precision Range of values Fractional accuracy
lowp (-2,2) 2-8 = 0.00390625
mediump (-214,214) 2-10 = 0.0009765625
highp (-262,262) 2-16 = 0.0000152587

Individual variables can have different precisions, or all variables of a particular type can be the same precision using a “precision statement,” such as:

precision highp int;
precision mediump float;

Constant Values (Literals)

Boolean constants are either true or false.

Integers can be specified in decimal, octal, or hexadecimal (base 10, 8, or 16), base on their leading character. For example:

int alpha = 176;   // base 10 starts with a non-zero digit
int beta  = 0176;  // base 8 starts with 0
int gamma = 0x176; // base 16 starts with 0x

Floats are specified using a series of digits that include a decimal point, an exponent, or both. Floats are always in base 10. For example:

float delta   = 1.;
float epsilon = 0.3421;
float phi     = 2e4;
float theta   = 2.45e-2

Your can create constants using the const storage qualifier. The compiler guarantees that the value will not be changed during shader execution. For example:

const float pi = 3.141592653589793;
const int number_lights = 5;

Variables

Variable names must start with a letter (a-z,A-Z) or the underscore character, ‘_’. A variable name can contain letters (a-z,A-Z), digits (0-9), and the underscore character, ‘_’. User variable names are not allowed to start with gl_.

All variables must be declared before they can be used.

Variables can be declared with, or without, an initialization value.

float alpha;
float beta = 5.0;

Storage Qualifiers

Some variable are used to pass data between the web browser and a shader program, and between a shader program and object buffers. These special variables must be designated with a “storage qualifier”. (Variables that do not have a “storage qualifier” as used for storing constants and performing calculations.)

  • uniform: The variable is assigned a value from the JavaScript code before a gl.drawArrays() call is made. The value is accessible in both the vertex and fragment shader.
  • attribute: The variable is assigned a value from a object buffer as a series of graphics primitives are rendered. The value is only accessible in the vertex shader.
  • varying: The variable is assigned a value by a vertex shader and automatically interpolated across the surface of a graphics primitive before a fragment shader receives it. The value can be used in a fragment shader, but not changed.

User Defined Aggregate Data Types

You can create new data types that contain a combination of values. A struct data type can contain values of different data types. The array data type requires that all values in the array be of the same data type.

A struct is a good way to organize values that logically go together. The identifier after the keyword struct is the structure name.

struct my_light {
  float intensity;
  vec3 position;
  vec4 color;
};

An array is a good way to organize values that logically go together if they all have the same data type. The size of the array must be a constant. Array indexes are zero-subscripted. Individual elements of an array must be assigned individually.

      float    frequencies[3];
const int      numLights = 2;
      my_light lights[numLights];

frequencies[0] = 0.23;
frequencies[1] = 0.67;
frequencies[2] = 0.82;

Vector Components

The individual element of a vector can be accessed using array notation, [2], or “dotted notation”, .x. The names of the vector components are x,y,z,w, or r,g,b,a, or s,t,p,q. You can use any of these names on a vector, regardless of the actual data in the vector, but the intent is to use x,y,z,w when you are accessing geometric data, r,g,b,a when you are accessing color data, and s,t,p,q when you are accessing texture data. The array notation always accesses a single component. The “dotted notation” returns either a single component or a new vector depending on the number of field names that is used. This is best explained by studying the following examples:

vec3 alpha = vec3(1.0, 2.0, 3.0);
vec4 a;
vec3 b;
vec2 c;
float d;

b = alpha.xyz;  // b is now (1.0, 2.0, 3.0)
d = alpha[2];   // d is now 3.0
a = alpha.xxxx; // a is now (1.0, 1.0, 1.0, 1.0)
c = alpha.zx;   // c is now (3.0, 1.0)
b = alpha.rgb;  // b is now (1.0, 2.0, 3.0)
b = alpha.stp;  // b is now (1.0, 2.0, 3.0)
a = alpha.yy;   // compiler error; the right hand side is a 2-component vector,
                // while "a" is a 4-component vector.

Using multiple property names to create a new vector is called swizzle notation. Swizzle notation can also be used on the left-hand side of an assignment statement, with the exception that each field name can only be used once. This is best explained by studying the following examples:

alpha.xx  = vec2(10.0, 20.0)      // compiler error
alpha.zxy = vec3(3.0, 4.0, 5.0);  // alpha is now (4.0, 5.0, 3.0)
alpha.zx  = vec2(10.0, 20.0)      // alpha is now (20.0, 5.0, 10.0)
alpha.xyz = vec2(10.0, 20.0)      // compiler error

Constructors and Data Type Conversions

You can convert one data type to another data type using a “cast”, which is a “call” to a conversion function that has the same name as the data type. Casting is important because GLSL does not support equations with mixed data types.

int a = 37;
float b = float(a) * 2.3;

Constructors also have the same name as their associated data types. A call to a constructor creates a value of the indicated data type and must be sent the correct number of initial values, but those values can be any combination of variables that contain the appropriate number of initialization values. Please study these examples:

vec3 alpha = vec3(1.0, 2.0, 3.0);
vec4 beta = vec4(4.0, 5.0, 6.0, 7.0);

vec3 delta = vec3(alpha.xy, beta.w); // delta is now (1.0, 2.0, 7.0)
vec4 gamma = vec4(alpha[2], beta.rrr); // gamma is now (3.0, 4.0, 4.0, 4.0)

Glossary

data type
The description of a memory location that specifies three things: 1) the number of bits used to represent a value, 2) the meaning of the bits, and 3) the valid operations that can be performed on the bits.
integer
A whole number, e.g., -5, 37, 0
float
A number that can represent fractions, e.g., 2.1, -6.74
precision
The number of bits used to represent a value. The number of bits determines the possible range of values.
constant
A value that never changes after its initial assignment.
variable
A memory location whose value can change as a program executes.
storage qualifier
Designates certain variables for special uses. These variables are used to exchange data between CPU and GPU components.
swizzle notation
The use of a vector’s property names to access and create new vectors.
cast
An operation that changes the data type of a value.
constructor
An operation that creates and initializes a variable of a particular data type.
Next Section - 12.3 - GLSL Control Structures