You have seen a few short, simple examples of shader programs written in GLSL. In fact, shader programs are often fairly short, but they are not always so simple. To understand the more complex shaders that we will be using in the rest of this book, you will need to know more about GLSL. This section aims to give a short introduction to the major features of the language. This is a rather technical section. You should read it to get some familiarity with GLSL, and then use it as a reference when needed.

For WebGL 1.0, shaders must be written in version 1.00 of GLSL ES. WebGL 2.0 can use either version 1.00 or version 3.00, but some features of WebGL 2.0 are only available when shaders are written in GLSL ES 3.00. Although the two versions of GLSL are very similar, there are major differences and incompatibilities. Unless otherwise noted, the discussion here applies to both versions.

The vertex shader and the fragment shader in a shader program must be written using the same version of GLSL. A GLSL ES 3.00 shader program must begin with the line

#version 300 es

This must be the very first line of the shader source code. It cannot even be preceded by blank lines or comments. A shader program that does not start with this line is a version 1.00 shader. A version 1.00 shader does not include a declaration of the version number.

Variables in GLSL must be declared before they are used. GLSL is a strictly typed language, and every variable is given a type when it is declared.

GLSL has built-in types to represent scalars (that is, single values), vectors, and matrices. The scalar types are float, int, and bool. Version 3.00 adds an unsigned integer type, uint. A GPU might not support integers or booleans on the hardware level, so it is possible that the int and bool types are actually represented as floating point values.

The types *vec2*, *vec3*, and *vec4* represent vectors of two,
three, and four floats. There are also types to represent vectors
of ints (*ivec2*, *ivec3*, and *ivec4*) and
bools (*bvec2*, *bvec3*, and *bvec4*) —
and, in version 3.00, of unsigned integers (*uvec2*, *uvec3*, and *uvec4*).
GLSL has very flexible notation for referring to the components of a vector.
One way to access them is with array notation. For example, if *v* is a four-component
vector, then its components can be accessed as *v*[0], *v*[1], *v*[2], and
*v*[3]. But they can also be accessed using the dot notation as
*v.x*, *v.y*, *v.z*, and*v.w*. The component names *x*,
*y*, *z*, and *w* are appropriate for a vector that holds coordinates.
However, vectors can also be used to represent colors, and the components of *v*
can alternatively be referred to as *v.r*, *v.g*, *v.b*, and *v.a*.
Finally, they can be referred to as *v.s*, *v.t*, *v.p*, and *v.q* —
names appropriate for texture coordinates.

Furthermore, GLSL allows you to use multiple component names after the dot, as in
*v.rgb* or *v.zx* or even *v.yyy*. The names can be in any order, and
repetition is allowed. This is called swizzling,
and *v.zx* is an example of a swizzler. The notation *v.zx* can be used in
an expression as a two-component vector. For example, if *v* is
*vec4*(1.0,2.0,3.0,4.0), then *v.zx* is equivalent to *vec2*(3.0,1.0),
and *v.yyy* is like *vec3*(2.0,2.0,2.0). Swizzlers can even be used on
the left-hand side of an assignment, as long as they don't contain repeated components.
For example,

vec4 coords = vec4(1.0, 2.0, 3.0, 4.0); vec3 point = vec3(5.0, 6.0, 7.0); coords.yzw = coords.wyz; // Now, coords is (1.0, 4.0, 2.0, 3.0) point.xy = coords.xx; // Now, point is (1.0, 1.0, 7.0)

A notation such as *vec2*(1.0, 2.0) is referred to as a "constructor," although
it is not a constructor in the sense of Java or C++, since GLSL is not object-oriented,
and there is no *new* operator. A constructor in GLSL consists of a type name
followed by a list of expressions in parentheses, and it represents a value of the type
specified by the type name. Any type name can be used, including the scalar types.
The value is constructed from the values of the expressions in parentheses. An expression
can contribute more than one value to the constructed value; we have already seen this
in examples such as

vec2 v = vec2( 1.0, 2.0 ); vec4 w = vec4( v, v ); // w is ( 1.0, 2.0, 1.0, 2.0 )

Note that the expressions can be swizzlers:

vec3 v = vec3( 1.0, 2.0, 3.0 ); vec3 w = vec3( v.zx, 4.0 ); // w is ( 3.0, 1.0, 4.0 )

Extra values from the last parameter will be dropped. This makes is possible to use a constructor to shorten a vector. However, it is not legal to have extra parameters that contribute no values at all to the result:

vec4 rgba = vec4( 0.1, 0.2, 0.3, 0.4 ); vec3 rgb = vec3( rgba ); // takes 3 items from rgba; rgb is (0.1, 0.2, 0.3) float r = float( rgba ); // r is 0.1 vec2 v = vec2( rgb, rgba ); // ERROR: No values from rgba are used.

As a special case, when a vector is constructed from a single scalar value, all components of the vector will be set equal to that value:

vec4 black = vec4( 1.0 ); // black is ( 1.0, 1.0, 1.0, 1.0 )

When constructing one of the built-in types, type conversion will be applied
if necessary. For purposes of conversion, the boolean values *true/false* convert
to the numeric values zero and one; in the other direction, zero converts to *false*
and any other numeric value converts to *true*. As far as I know, constructors
are the **only** context in which GLSL does automatic type conversion.
For example, you need to use a constructor to assign an int value
to a float variable, and it is illegal to add an int
to a float:

int k = 1; float x = float(k); // OK; "x = k" would be a type mismatch error x = x + 1.0; // OK x = x + 1; // ERROR: Can't add values of different types.

The built-in matrix types are *mat2*, *mat3*, and *mat4*.
They represent, respectively, two-by-two, three-by-three, and four-by-four matrices
of floating point numbers. (There are no matrices of integers or booleans, but there
are some additional matrix types for representing non-square matrices.)
The elements of a matrix can be
accessed using array notation, such as *M*[2][1]. If a single index is used, as
in *M*[2], the result is a vector. For example, if *M* is of type
*mat4*, then *M*[2] is a *vec4*. Arrays in GLSL, as in OpenGL,
use column-major order. This means that *M*[2] is column
number 2 in *M* rather than row number 2 (as it would be in Java), and *M*[2][1] is
the element in column 2 and row 1.

A matrix can be constructed from the appropriate number of values, which can
be provided as scalars, vectors or matrices. For example, a *mat3* can be
constructed from nine float or from three *vec3* parameters:

mat3 m1 = mat3( 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 ); vec3 v = vec3( 1, 2, 3 ); mat3 m2 = mat3( v, v, v );

Keep in mind that the matrix is filled in column-major order; that is, the first three numbers go into column 0, the next three into column 1, and the last three into column 2.

As a special case, if a matrix *M* is constructed from a single scalar value, then
that value is put into all the diagonal elements of *M* (*M*[0][0],
*M*[1][1], and so on). The non-diagonal elements are all set equal to zero.
For example, *mat4*(1.0) constructs the four-by-four identity matrix.

The only other built-in types are the so-called "sampler types", which are used for accessing textures. The sampler types can be used only in limited ways. They are not numeric types and cannot be converted to or from numeric types. The will be covered in the next section.

A GLSL program can define new types using the *struct* keyword. The syntax is the
same as in C, with some limitations. A struct is made up of a sequence
of named members, which can be of different types. The type of a member can be
any of the built-in types, an array type, or a previously defined struct type. For example,

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

This defines a type named *LightProperties*. The type can be used to declare variables:

LightProperties light;

The members of the variable *light* are then referred to as *light.position*,
*light.color*, and *light.intensity*. Struct types have constructors, but their
constructors do not support type conversion: The constructor must contain a list of
values whose types exactly match the types of the corresponding members in the struct.
For example,

light = LightProperties( vec4(0.0, 0.0, 0.0, 1.0), vec3(1.0), 1.0 );

GLSL also supports arrays. Only one-dimensional arrays are allowed. The base type of an array can be any of the basic types or it can be a struct type. The size of the array must be specified in the variable declaration as an integer constant. For example

int A[10]; vec3 palette[8]; LightProperties lights[3];

In version 1.00, there are no array constructors, and it is not possible to initialize an array as part of its declaration. Version 3.00 does have array constructors, and it allows type names such as "int[10], representing an array of 10 integers:

int[4] B; // B is an array of 4 ints; GLSL ES 3.00 only! B = int[4] (2, 3, 5, 7); // Array constructor; GLSL ES 3.00 only!

Array indexing uses the usual syntax, such as *A*[0]
or *palette*[*i*+1] or *lights*[3].*color*.
In GLSL ES 1.00, there are some strong limitations on the expressions that can be used as array
indices. With one exception, an expression that is used as the index for an array
can contain only integer constants and *for* loop variables (that is, variables
that are used as loop control variables in *for* loops). For example,
the expression *palette*[*i*+1] would only be legal inside a *for* of the form
*for (int i = ...*. The single exception is that
arbitrary index expressions can be used for arrays of *uniforms* in a
vertex shader (and then only if the array does not contain samplers).
Note that these restrictions do not apply in GLSL ES 3.00.

Just as in C, there is no check for array index out of bounds errors. It is up to the programmer to make sure that array indices are valid.

Variable declarations can be modified by various qualifiers. You have seen
examples of the qualifiers *attribute*, *uniform*, and *varying*.
These are called storage qualifiers.
The qualifiers *attribute* and *varying* do not exist in version 3.00;
instead, an attribute is declared in the vertex shader using the storage qualifier *in*,
and a varying variable is declared using *out* in the vertex shader and
*in* in the fragment shader. The *uniform* qualifier is used in both
versions. Only global variables, not local variables in function definition,
can be attribute, uniform, or varying variables.

The *attribute* qualifier can only be used in a GLSL ES 1.00
vertex shader, and it only applies to the built-in floating point types
float, *vec2*, *vec3*, *vec4*,
*mat2*, *mat3*, and *mat4*. (Matrix attributes are not supported directly
on the JavaScript side. A matrix attribute has to be treated as a set of vector attributes,
one for each column. The attribute locations for the columns are successive integers,
and the WebGL function *gl.getAttribLocation* will return the location for the first column.
Matrix attributes would be rare, though perhaps useful for instanced drawing,
and I won't go into further detail about them here.)

In GLSL ES 3.00, the *in* qualifier on a vertex shader variable defines it to be an attribute variable,
and it can be applied to integer and unsigned integer scalars and vectors, as
well as to the floating point types.

Also in GLSL ES 3.00, the *out* qualifier can be used on integer and floating point
scalars and vectors in the fragment shader. In version 1.00, a fragment shader has the
predefined variable *gl_FragColor* of type vec4 to specify the color of the pixel.
In version 3.00, a fragment shader can have multiple outputs, and the outputs
are not necessarily colors. Because the output type does not have to be vec4,
it is not possible to have a predefined output variable. For now, we will only use one
fragment shader output representing a color. So, a version 3.00 fragment shader will
have one *out* variable of type vec4. (When we discuss
framebuffers in Section 7.4, we will see
how multiple outputs can be used.)

Both the vertex shader and the fragment shader can use *uniform* variables.
The same variable can occur in both shaders, as long as the types in the two shaders are
the same. Uniform variables can be of any type, including array and
structure types. Now,
JavaScript only has functions for setting uniform values that are scalar variables,
vectors, or matrices. There are no functions for setting the values of structs or
arrays. The solution to this problem requires treating every component of a
struct or array as a separate uniform value. For example, consider the declarations

struct LightProperties { vec4 position; vec3 color; float intensity; }; uniform LightProperties light[4];

The variable *light* contains twelve basic values, which are of type *vec4*, *vec3*,
or *float*. To work with the *light* uniform in JavaScript, we need twelve variables
to represent the locations of the 12 components of the
uniform variable. When using *gl.getUniformLocation* to get the location of
one of the 12 components, you need to give the full name of the component in the GLSL
program. For example: *gl.getUniformLocation*(*prog*, "*light[2].color*").
It is natural to store the 12 locations in an array of JavaScript objects that
parallels the structure of the array of structs on the GLSL side. Here is
typical JavaScript code to create the structure and use it to initialize the
uniform variables:

lightLocations = new Array(4); for (i = 0; i < light.length; i++) { lightLocations[i] = { position: gl.getUniformLocation(prog, "light[" + i + "].position" ); color: gl.getUniformLocation(prog, "light[" + i + "].color" ); intensity: gl.getUniformLocation(prog, "light[" + i + "].intensity" ); }; } for (i = 0; i < light.length; i++) { gl.uniform4f( lightLocations[i].position, 0, 0, 0, 1 ); gl.uniform3f( lightLocations[i].color, 1, 1, 1 ); gl.uniform1f( lightLocations[i].intensity, 0 ); }

For uniform shader variables that are matrices,
the JavaScript function that is used to set the value of the uniform
is *gl.uniformMatrix2fv* for a *mat2*,
*gl.uniformMatrix3fv* for a *mat3*, or *gl.uniformMatrix4fv* for a *mat4*.
Even though the matrix is two-dimensional, the values are stored in a one dimensional array.
The values are loaded into the array in column-major order. For example, if
*transform* is a uniform *mat3* in the shader, then JavaScript can set
its value to be the identity matrix with

transformLoc = gl.getUniformLocation(prog, "transform"); gl.uniformMatrix3fv( transformLoc, false, [ 1,0,0, 0,1,0, 0,0,1 ] );

In Version 1.00, the second parameter **must** be *false*. In Version 3.00,
the second parameter can be *true* to indicate that the entries of the matrix are
provided in row-major rather than column-major order.
Note that the 3 in *uniformMatrix3fv* refers to the number of rows and columns in
the matrix, not to the length of the array, which must be 9. (By the way, it is OK
to use a typed array rather than a normal JavaScript array for the value of a uniform.)

A varying variable should be declared with the same name and type in both the
vertex shader and fragment shader. In version 1.00, the storage qualifier for
declaring varying variables is *varying*, and it can only be used
for for the built-in floating
point types (*float, vec2, vec3, vec4,
mat2, mat3, and mat4*) and for arrays of those types.

In version 3.00, a varying variable can also be an integer or unsigned integer
scalar or vector. But there is a complication because it doesn't make
sense to apply interpolation to integer values. So, a varying variable of integer
type must be declared with the additional qualifier *flat*, which means it
will not be interpolated. Instead, the value from the first vertex of a triangle or line segment
will be used for every pixel. (Floating point varying variables can
also, optionally, be declared as *flat*.) For example:

flat in ivec3 A; // GLSL ES 3.00 fragment shader only!

Another possible storage qualifier is *const*, which means that the value
of the variable cannot be changed after it has been initialized. The declaration
of a *const* variable must include initialization.

A variable declaration can also be modified by precision qualifiers.
The possible precision qualifiers are *highp*, *mediump*, and *lowp*.
A precision qualifier sets the minimum range of possible values for an integer variable or the
minimum range of values and number of decimal places for a floating point variable.
GLSL doesn't assign a definite meaning to the precision qualifiers, but mandates some
minimum requirements. For example, in version 1.00, *lowp* integers must be able to represent values
in at least the range −2^{8} to 2^{8}; *mediump* integers, in the
range −2^{10} to 2^{10}; and *highp* integers, in the range
−2^{16} to 2^{16}. For version 3.00, *highp* variables always
use 32 bits, and the requirements for *mediump* and *lowp* are higher.
It is even possible that all values are 32-bit values and the precision qualifiers have
no real effect. But GPUs in embedded systems can be more limited.

A precision qualifier can be used on any variable declaration, including local variables and function parameters. If the variable also has a storage qualifier, the storage qualifier comes first. For example

lowp int n; varying highp float v; uniform mediump vec3 colors[3];

A varying variable can have different precisions in the vertex and in the fragment shader.
The default precision for integers and floats in the vertex shader is *highp*.
Fragment shaders are not required to support *highp*, although it is likely that
they do so, except perhaps on older mobile hardware. In the fragment shader,
the default precision for integers is *mediump*, but floats do not have a default
precision. This means that every floating point variable in the fragment shader has
to be explicitly assigned a precision. Alternatively, it is possible to set a default
precision for floats with the statement

precision mediump float;

This statement was used at the start of each of the fragment shaders
in the previous section. Of course, if the
fragment shader does support *highp*, this restricts the precision unnecessarily.
You can avoid that by using this code at the start of the fragment shader:

#ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif

This sets the default precision to *highp* if it is available and
to *mediump* if not. The lines starting with "#" are preprocessor directives—an aspect
of GLSL that I don't want to get into.

The next qualifier, invariant,
is even more difficult to explain, and it has only a limited use. Invariance
refers to the requirement that when the same expression is used to compute the value of
the same variable (possibly in different shaders), then the value that is assigned to
the variable should be exactly the same in both cases. This
is not automatically the case. For example, the values
can be different if a compiler uses different optimizations or evaluates the operands
in a different order in the two expressions. The *invariant* qualifier on the
variable will force the compiler to use exactly the same calculations for the two
assignment statements. The qualifier can only be used on declarations of varying variables.
It must be the first qualifier in the declaration. For example,

invariant varying mediump vec3 color;

It can also be used to make the predefined variables such as *gl_Position* and
*gl_FragCoord* invariant, using a statement such as

invariant gl_Position;

Invariance can be important in a multi-pass algorithm that applies
two or more shader programs in succession to compute an image. It is important, for
example, that both shaders get the same answer when they compute *gl_Position*
for the same vertex, using the same expression in both vertex shaders. Making *gl_Position*
invariant in the shaders will ensure that.

The last type of qualifier, a "layout" qualifier, is only available in version 3.00.
It can be used to specify the integer ID of an attribute variable, as an alternative to
using the JavaScript function *gl.getAttribLocation*() to query the ID. An example
would be

layout(location = 0) in vec3 a_coords; // GLSL ES 3.00 vertex shader only!

The same kind of layout qualifier can be used on an *out* variable in a GLSL ES 3.00
fragment shader that has multiple outputs. In that case, it specifies which of several
output destinations should be used for that variable.

Expressions in GLSL can use the arithmetic operators +, −, *,
/, ++ and −− for integer and floating
point values. In version 3.00, the remainder operator, %, as well as left and right
shift and bitwise logical operators, are also available for integer types.
There is no automatic type conversion in expressions. If *x*
is of type float, the expression *x*+1 is illegal.
You have to say *x*+1.0 or *x*+*float*(1).

The arithmetic operators have been extended in various ways to work with vectors and matrices.
If you use * to multiply a matrix and a vector, in either order, it multiplies
them in the linear algebra sense, giving a vector as the result.
The types of the operands must match in the obvious way; for example,
a *vec3* can only be multiplied by a *mat3*, and the result is a *vec3*.
When used with two matrices of the same size, * does matrix multiplication.

If +, −, *, or / is used on a
vector and a scalar of the same basic type, then the operation is performed on each element
of the vector. For example, *vec2*(3.0,3.0) / 2.0 is the
vector *vec2*(1.5,1.5), and 2*ivec3(1,2,3) is the vector *ivec3*(2,4,6).
When one of these operators is applied to two vectors of the
same type, the operation is applied to each pair of components, and the result is
a vector. For example, the value of

vec3( 1.0, 2.0, 3.0 ) + vec3( 4.2, -7.0, 1.7 )

is the vector *vec3*(5.2,-5.0,4.7). Note in particular that the usual vector
arithmetic operations—addition and subtraction of vectors, multiplication of a vector
by a scalar, and multiplication of a vector by a matrix—are written in the natural
way is GLSL.

The relational operators <, >, <=, and >= can only be applied to integer and floating point scalars, and the types of the two operands must match exactly. However, the equality operators == and != have been extended to work on all of the built-in types except sampler types. Two vectors are equal only if the corresponding pairs of components are all equal. The same is true for matrices. The equality operators cannot be used with arrays, but they do work for structs, as long as the structs don't contain any arrays or samplers; again, every pair of members in two structs must be equal for the structs to be considered equal.

GLSL has logical operators !, &&, ||, and ^^ (the last one being an exclusive or operation). The operands must be of type bool.

Finally, there are the assignment operators =, +=, −=, *=, and /=, with the usual meanings.

GLSL also has a large number of predefined functions, more than I can discuss here. All of the functions that I will mention here require floating-point values as parameters, even if the function would also make sense for integer values.

Most interesting, perhaps, are functions for vector algebra. See Section 3.5
for the definitions of these operations. These functions have simple formulas, but they
are provided as functions for convenience and because they might have efficient hardware
implementations in a GPU. The function *dot*(*x,y*)
computes the dot product *x*·*y* of two vectors of the same length.
The return value is a float; *cross*(*x,y*) computes the
cross product *x*×*y*, where the parameters and return value are of type
*vec3*; *length*(*x*) is the length of the vector *x* and
*distance*(*x*,*y*) gives the distance between two vectors;
*normalize*(*x*) returns a unit vector that points in the same direction as *x*.
There are also functions named *reflect* and *refract* that can be used to
compute the direction of reflected and refracted light rays; I will cover them when
I need to use them.

The function *mix*(*x,y,t*) computes *x**(1−*t*) + *y***t*.
If *t* is a float in the range 0.0 to 1.0, then the return value is a linear mixture, or weighted average,
of *x* and *y*. This function might be used, for example, to do alpha-blending of two colors.
The function *clamp*(*x,low,high*) clamps *x* to the range *low* to *high*;
the return value could be computed as *min*(*max*(*x,low*),*high*).
If *rgb* is a vector representing a color, we could ensure that all of the components of
the vector lie in the range 0 to 1 with the command

rgb = clamp( rgb, 0.0, 1.0 );

If *s* and *t* are floats, with *s < t*, then
*smoothstep*(*s,t,x*) returns 0.0 for *x* less than *s* and
returns 1.0 for *x* greater than *t*. For values of *x* between *s*
and *t*, the return value is smoothly interpolated from 0.0 to 1.0. Here is an
example that might be used in a fragment shader for rendering a *gl.POINTS*
primitive, with transparency enabled:

float dist = distance( gl_PointCoord, vec2(0.5) ); float alpha = 1.0 - smoothstep( 0.45, 0.5, dist ); if (alpha == 0.0) { discard; // discard fully transparent pixels } gl_FragColor = vec4( 1.0, 0.0, 0.0, alpha );

This would render the point as a red disk, with the color fading smoothly from opaque
to transparent around the edge of the disk, as *dist* increases from 0.45 to 0.5.
Note that for the functions *mix*, *clamp*, and *smoothstep*, the *x*
and *y* parameters can be vectors as well as floats. In that case, they operate on
each component of the vector individually.

The usual mathematical functions are available in GLSL, including *sin*, *cos*, *tan*,
*asin*, *acos*, *atan*, *log*, *exp*, *pow*, *sqrt*,
*abs*, *floor*, *ceil*, *min*, and *max*. (In version 3.00, *abs*,
*min*, and *max* also apply to integer types.)
For these functions, the parameters can be any of the types float,
*vec2*, *vec3*, or *vec4*. The return value is of the same type,
and the function is applied to each component separately. For example,
the value of *sqrt*(*vec3*(16.0,9.0,4.0)) is the vector
*vec3*(4.0,3.0,2.0). For *min* and *max*, there is also a
second version of the function in which the first parameter is a
vector and the second parameter is a float. For those versions, each component
of the vector is compared to the float; for example, *max*(*vec3*(1.0,2.0,3.0),2.5)
is *vec3(2.5,2.5,3.0)*.

The function *mod*(*x,y*) computes the modulus, or
remainder, when *x* is divided by *y*. The return value is computed as
*x* − *y**floor(*x*/*y*).
As with *min* and *max*, *y* can be either a vector or a float.
The *mod* function can be used as a substitute for the % operator, which
is not supported in GLSL ES 1.00.

There are also functions for working with sampler variables. I will discuss some of them in the next section.

A GLSL program can define new functions, with a syntax similar to C. Unlike C, function names can be overloaded; that is, two functions can have the same name, as long as they have different numbers or types of parameters. A function must be declared before it is used. As in C, it can be declared by giving either a full definition or a function prototype.

Function parameters can be of any type. The return type for a function can be any type except for array types. A struct type can be a return type, as long as the structure does not include any arrays. When an array is used a formal parameter, the length of the array must be specified by an integer constant. For example,

float arraySum10( float A[10] ) { float sum = 0.0; for ( int i = 0; i < 10; i++ ) { sum += A[i]; } return sum; }

Function parameters can be modified by the qualifiers *in*, *out*,
or *inout*. The default, if no qualifier is specified, is *in*.
The qualifier indicates whether the parameter is used for input to the function,
output from the function, or both. For input parameters, the value of the
actual parameter in the function call is copied into the formal parameter
in the function definition, and there is no further interaction between
the formal and actual parameters. For output parameters, the value
of the formal parameter is copied to the actual parameter when
the function returns. For an *inout* parameter, the value is
copied in both directions. This type of parameter passing is referred to as
"call by value/return." Note that the actual parameter for an *out*
or *inout* parameter must be something to which a value can be
assigned, such as a variable or swizzler.
(All parameters in C, Java, and JavaScript are input parameters, but passing
a pointer as a parameter can have an effect similar to an *inout* parameter.
GLSL, of course, has no pointers.) For example,

void cumulativeSum( in float A[10], out float B[10]) { B[0] = A[0]; for ( int i = 1; i < 10; i++ ) { B[i] = B[i-1] + A[i]; } }

Note that recursion is not supported for functions in GLSL.

The only control structures in GLSL ES 1.00 for WebGL are the *if* statement and a very
restricted form of the *for* loop. There is no *while* or *do..while*
loop, and there is no *switch* statement. However, all of these are supported in
GLSL ES 3.00.

*If* statements are supported with the full syntax from C, including
*else* and *else if*. In version 3.00, the syntax for all control
structures is pretty much the same as in C.

In a *for* loop in a version 1.00 shader, the loop control variable must be declared in the loop,
and it must be of type int or float. The
initial value for the loop control variable must be a constant expression (that is,
it can include operators, but all the operands must be literal constants or *const*
variables) The code inside the loop is not allowed to change the value of
the loop control variable. The test for ending the loop can only have the
form *var op expression*, where *var* is the loop
control variable, the *op* is one of the relational or equality operators,
and the *expression* is a constant expression. Finally, the update expression
must have one of the forms *var*++, *var*--,
*var*+=*expression*, or *var*-=*expression*,
where *var* is the loop control variable, and *expression* is a constant
expression. Of course, this is the most typical form for *for* loops in
other languages. Some examples of legal first lines for *for* loops:

for (int i = 0; i < 10; i++) for (float x = 1.0; x < 2.0; x += 0.1) for (int k = 10; k != 0; k -= 1)

In version 3.00, these restrictions do not apply. Note that all loops
can include *break* and *continue* statements.

WebGL puts limits on certain resources that are used by WebGL and its GLSL programs, such as the number of attribute variables or the size of a texture image. The limits are due in many cases to hardware limits in the GPU, and they depend on the device on which the program is running, and on the implementation of WebGL on that device. The hardware limits can be lower on mobile devices such as tablets and phones, but modern tablets and phones have pretty impressive GPUs. Although the limits can vary, WebGL imposes a set of minimum requirements that all implementations must satisfy. I will give the minimums for WebGL 1.0. The minimums for WebGL 2.0 are greater.

For example, any WebGL implementation must allow at least 8 attributes in
a vertex shader. The actual limit for a particular implementation might be
more, but cannot be less. The actual limit is available in a GLSL program
as the value of a predefined constant, *gl_MaxVertexAttribs*. More conveniently,
it is available on the JavaScript side as the value of the expression

gl.getParameter( gl.MAX_VERTEX_ATTRIBS )

Attribute variables of type *float*, *vec2*, *vec3*, and *vec4* all
count as one attribute against the limit. For a matrix-valued attribute, each column counts as a separate
attribute as far as the limit goes.

Similarly, there are limits on varying variables, and there are separate limits on uniform variables in the vertex and fragment shaders. (The limits are on the number of four-component "vectors." There can be some packing of separate variables into a single vector, but the packing that is used does not have to be optimal. No packing is done for attribute variables.) The limits must satisfy

gl_MaxVertexAttribs >= 8; gl_MaxVertexUniformVectors >= 128; gl_MaxFragmentUniformVectors >= 16; gl_MaxVaryingVectors >= 8;

There are also limits in GLSL on the number of texture units, which means essentially the number of textures that can be used simultaneously. These limits must satisfy

gl_MaxTextureImageUnits >= 8; // limit for fragment shader gl_MaxVertexTextureImageUnits >= 0; // limit for vertex shader gl_MaxCombinedTextureImageUnits >= 8; // total limit for both shaders

Textures are usually used in fragment shaders, but they can sometimes be useful
in vertex shaders. Note however, that *gl_MaxVertexTextureImageUnits* can be zero,
which means that implementations are not required to allow
texture units to be used in vertex shaders. (This possibility is for WebGL 1.0 only.)

There are also limits on other things, including viewport size, texture image size,
line width for line primitives, and point size for the *POINTS* primitive.
All of the limits can be queried from the JavaScript side using *gl.getParameter*().

The following demo shows the actual values of the resource limits on the device on which you are viewing this page. The demo shows the limits for a WebGL 1.0 graphics context. You can use it to check the capabilities of various devices on which you want your WebGL programs to run. In general, the actual limits will be significantly larger than the required minimum values.