2.4. Language Overview
Because of its success as a standard, OpenGL has been the target of our efforts to define an industry-standard, high-level shading language. The shading language that has been defined as a result of the efforts of OpenGL ARB members is called the OpenGL Shading Language. This language has been designed to be forward looking and to eventually support programmability in other areas as well.
2.4.1. Language Design Considerations
In the past few years, semiconductor technology has progressed to the point at which the levels of computation that can be done per vertex or per fragment have gone beyond what is feasible to describe by the traditional OpenGL mechanisms of setting state to influence the action of fixed pipeline stages. A natural way of taming this complexity and the proliferation of OpenGL extensions is to replace parts of the pipeline with user-programmable stages. This has been done in some recent OpenGL extensions, but the programming is done in assembly language. It can be difficult and time consuming to write shaders in such low-level languages, and maintaining such code can be costly. As programmable graphics hardware evolves, the current crop of shader assembly languages may also prove to be cumbersome and inefficient to support in hardware.
The ideal solution to these issues was to define a forward-looking, hardware-independent, high-level language that would be easy to use and powerful enough to stand the test of time and that would drastically reduce the need for extensions. These desires were tempered by the need for fast implementations within a generation or two of hardware.
The following design goals were fundamental to the design of the OpenGL Shading Language.
2.4.2. C Basis
As stated previously, the OpenGL Shading Language is based on the syntax of the ANSI C programming language, and at first glance, programs written in this language look very much like C programs. This is intentional, to make the language easier to use for those most likely to be using it, namely, those developing graphics applications in C or C++.
The basic structure of programs written in the OpenGL Shading Language is the same as it is for programs written in C. The entry point of a set of shaders is the function void main(); the body of this function is delimited by curly braces. Constants, identifiers, operators, expressions, and statements are basically the same for the OpenGL Shading Language as they are for C. Control flow for looping, if-then-else, and function calls are virtually identical to C.
2.4.3. Additions to C
The OpenGL Shading Language has a number of language features that have been added because of its special-purpose nature as a language for encoding graphics algorithms. Here are some of the main things that have been added to the OpenGL Shading Language that are different from ANSI C.
Vector types are supported for floating-point, integer, and Boolean values. For floating-point values, these vector types are referred to as vec2 (two floats), vec3 (three floats), and vec4 (four floats). Operators work as readily on vector types as they do on scalars. To sum vectors v1 and v2, you simply would say v1 + v2. Individual components of a vector can be accessed either with array syntax or as fields of a structure. Color values can be accessed by appending .r to the name of a vector variable to access the first component, .g to access the second component, .b to access the third, and .a to access the fourth. Position values can be accessed with .x, .y, .z, and .w, and texture values can be accessed with .s, .t, .p, and .q. Multiple components can be selected by specification of multiple names, like .xy.
Floating-point matrix types are also supported as basic types. The data type mat2 refers to a 2 x 2 matrix of floating-point values, mat3 refers to a 3 x 3 matrix, and mat4 refers to a 4 x 4 matrix. This is a convenient type for expressing the linear transformations common in 3D graphics. Columns of a matrix can be selected with array syntax, yielding a vector whose components can be accessed as just described.
A set of basic types called SAMPLERS has also been added to create the mechanism by which shaders access texture memory. Samplers are a special type of opaque variable that access a particular texture map. A variable of type sampler1D can be used to access a 1D texture map, a variable of type sampler2D can be used to access a 2D texture map, and so on. Shadow and cube map textures are also supported through this mechanism.
Qualifiers have been added to manage the input and output of shaders. The attribute, uniform, and varying qualifiers specify what type of input or output a variable serves. Attribute variables communicate frequently changing values from the application to a vertex shader, uniform variables communicate infrequently changing values from the application to any shader, and varying variables communicate interpolated values from a vertex shader to a fragment shader.
Shaders written in the OpenGL Shading Language can use built-in variables that begin with the reserved prefix "gl_" in order to access existing OpenGL state and to communicate with the fixed functionality of OpenGL. For instance, both vertex and fragment shaders can access built-in uniform variables that contain state values that are readily available within the current rendering context. Some examples are gl_ModelViewMatrix for obtaining the current modelview matrix, gl_LightSource[i] for obtaining the current parameters of the ith light source, and gl_Fog.color for accessing the current fog color. The vertex shader must write the special variable gl_Position in order to provide necessary information to the fixed functionality stages between vertex processing and fragment processing, namely, primitive assembly, clipping, culling, and rasterization. A fragment shader typically writes into one or both of the special variables gl_FragColor or gl_FragDepth. These values represent the computed fragment color and computed fragment depth. These values are submitted to the back-end fixed functionality fragment operations such as alpha testing, stencil testing, and depth testing, before reaching their ultimate destination, the frame buffer.
A variety of built-in functions is also provided in the OpenGL Shading Language in order to make coding easier and to take advantage of possible hardware acceleration for certain operations. The language defines built-in functions for a variety of operations:
2.4.4. Additions from C++
The OpenGL Shading Language also includes a few notable language features from C++. In particular, it supports function overloading to make it easy to define functions that differ only in the type or number of arguments being passed. This feature is heavily used by the built-in functions. For instance, the dot product function is overloaded to deal with arguments that are types float, vec2, vec3, and vec4.
The concept of constructors also comes from C++. Initializers are done only with constructors in the OpenGL Shading Language. Using constructors allows for more than one way of initializing variables.
Another feature borrowed from C++ is that variables can be declared when they are needed; they do not have to be declared at the beginning of a basic block. The basic type bool is supported as in C++.
As in C++, functions must be declared before being used. This can be accomplished either with the function's definition (its body) or just with a prototype.
2.4.5. C Features Not Supported
Unlike ANSI C, the OpenGL Shading Language does not support automatic promotion of data types. Compiler errors are generated if variables used in an expression are of different types. For instance, an error is generated for the statement float f = 0; but not for the statement float f = 0.0;. This approach might seem like a bit of a nuisance, but it simplifies the language by eliminating the need for type promotion rules. It also removes a class of confusing mistakes made when argument type is the basis for calling a set of overloaded functions.
The OpenGL Shading Language does not support pointers, strings, or characters, or any operations based on these. It is fundamentally a language for processing numerical data, not for processing character or string data, so there is no need for these features to complicate the language. To lower the implementation burden (both for the compiler and for the graphics hardware), there is no support for double-precision floats; byte, short, or long integers; or unsigned variants of these.
A few other C language features that were eliminated from consideration in order to simplify the OpenGL Shading Language (or because there was no compelling need for them at the time) are unions, enumerated types, bit fields in structures, and bitwise operators. Finally, the language is not file based, so you won't see any #include directives or other references to file names.
2.4.6. Other Differences
There are a few areas in which the OpenGL Shading Language provides the same functionality as C but does so in a different way. One of these is that constructors, rather than type casts, are used for data type conversion. Constructors, not C-style initializers, are also used for variable initialization. There is no support at all for type casting without conversion, so constructors keep the language type safe. Constructors use the syntax of a function call, where the function name is the name of the desired type and the arguments are the values that will be used to construct the desired value.
Constructors allow a much richer set of operations than simple type casts or C-style initializers, and the flexibility that this richness provides comes in quite handy for dealing with vector and matrix data types. In addition to converting from one scalar type to another, constructors can create a larger type out of a smaller type or reduce a larger type to a smaller type. For instance, the constructor vec3(1.0, 2.0, 3.0) constructs a vec3 data type out of three scalar values, and the constructor vec3(myVec4) strips the fourth component from myVec4 to create a vec3 value.
The other area of difference is that, unlike the call-by-value calling convention used by C, the OpenGL Shading Language uses CALL BY VALUE-RETURN. Input parameters are copied into the function at call time, and output parameters are copied back to the caller before the function exits. Because the function deals only with copies of the function parameters, there are no issues regarding aliasing of variables within a function. Function parameters are identified as input parameters with the qualifier in, they are identified as output parameters with the qualifier out, and they are identified as both input and output parameters with the qualifier inout; if no qualifier is present, they are identified as input parameters.