12.2 - GLSL Data Types and Variables¶
GLSL allows for three basic types of data:
- bool: Boolean values; true or false
- int: integer values; whole numbers in a certain range, -n..n
- float: floating point values; numbers with a fractional component
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:
- bvec2, bvec3, bvec4: 2, 3, and 4-component Boolean vectors
- ivec2, ivec3, ivec4: 2, 3, and 4-component integer vectors
- vec2, vec3, vec4: 2, 3, and 4-component floating point vectors
- mat2, mat3, mat4: 2x2, 3x3, and 4x4 floating point matrices
There are three other specialized data types:
- sampler2D: a reference to a TEXTURE_2D texture unit (which has an attached texture object)
- samplerCube: a reference to a SAMPLER_CUBE texture unit
- void: used to identify functions that do not return a value or parameter lists to a function that are empty.
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.