3.5. Qualifiers and Interface to a ShaderQualifiers prefix both variables and formal function parameters. The qualifiers that modify formal function parameters (const, in, out, and inout) are discussed in Section 3.6.2. This section focuses on the other qualifiers, most of which form the interfaces of the shaders to their outside world. The following is the complete list of qualifiers (used outside of formal function parameters).
Getting information into and out of a shader is quite different from more typical programming environments. Information is transferred to and from a shader by reading and writing built-in variables and user-defined attribute, uniform, and varying variables. The most common built-in variables were shown in the example at the beginning of this chapter. They are gl_Position for output of the homogeneous coordinates of the vertex position and gl_FragColor for output of the fragment's color from a fragment shader. The complete set of built-in variables is provided in Chapter 4. Examples of attribute, uniform, and varying qualified variables were seen briefly in the opening example for getting other information into and out of shaders. Each is discussed in this section. Variables qualified as attribute, uniform, or varying must be declared at global scope. This is sensible since they are visible outside of shaders and, for a single program, they all share a single name space. Qualifiers are always specified before the type of a variable, and because there is no default type, the form of a qualified variable declaration always includes a type. attribute float Temperature; const int NumLights = 3; uniform vec4 LightPosition[NumLights]; varying float LightIntensity; 3.5.1. Attribute QualifiersAttribute-qualified variables (or attributes) enable an application to pass frequently modified data into a vertex shader. They can be changed as often as once per vertex, either directly or indirectly by the application. Built-in attributes, like gl_Vertex and gl_Normal, read traditional OpenGL state, and user-defined attributes can be named by the coder. Attributes are limited to floating-point scalars, floating-point vectors, and matrices. Attributes declared as integers or Booleans are not allowed, nor are attributes declared as structures or arrays. This is, in part, a result of encouraging high-performance frequent changing of attributes in hardware implementations of the OpenGL system. Attributes cannot be modified by a shader. Attributes cannot be declared in fragment shaders. 3.5.2. Uniform QualifiersUniform qualified variables (or uniforms), like attributes, are set only outside a shader and are intended for data that changes less frequently. They can be changed at most once per primitive. All data types and arrays of all data types are supported for uniform qualified variables. All the vertex and fragment shaders forming a single program share a single global name space for uniforms. Hence, uniforms of the same name in a vertex and fragment program will be the same uniform variable. Uniforms cannot be written to in a shader. This is sensible because an array of processors may be sharing the same resources to hold uniforms and other language semantics break down if uniforms could be modified. Recall that unless a sampler (e.g., sampler2D) is a function parameter, the uniform qualifier must be used when it is declared. This is because samplers are opaque, and making them uniforms allows the OpenGL driver to validate that the application initializes a sampler with a texture and texture unit consistent with its use in the shader. 3.5.3. Varying QualifiersVarying qualified variables (or varyings) are the only way a vertex shader can communicate results to a fragment shader. Such variables form the dynamic interface between vertex and fragment shaders. The intention is that for a particular attribute of a drawing primitive, each vertex might have a different value and these values need to be interpolated across the fragments in the primitive. The vertex shader writes the per-vertex values into a varying variable, and when the fragment shader reads from this variable, it gets back a value interpolated between the vertices. If some attribute were to be the same across a large primitive, not requiring interpolation, the vertex shader need not communicate it to the fragment shader at all. Instead, the application could pass this value directly to the fragment shader through a uniform qualified variable. The exception to using varying variables only for interpolated values is for any value the application will change often, either per triangle or per some small set of triangles or vertices. These values may be faster to pass as attribute variables and forwarded as varying variables because changing uniform values frequently may impact performance. The automatic interpolation of varying qualified variables is done in a perspective-correct manner. This approach is necessary no matter what type of data is being interpolated. Otherwise, such values would not change smoothly across edges introduced for surface subdivision. The non-perspective-correct interpolated result would be continuous, but its derivative would not be, and this effect can be quite visible. A varying qualified variable is written in a vertex shader and read in a fragment shader. It is illegal for a fragment shader to write to a varying variable. However, the vertex shader may read a varying variable, getting back what it has just written. Reading a varying qualified variable before writing it returns an undefined value. 3.5.4. Constant QualifiersVariables qualified as const (except for formal function parameters) are compile-time constants and are not visible outside the shader that declares them. Both scalar and nonscalar constants are supported. Structure fields may not be qualified with const, but structure variables can be declared as const and initialized with a structure constructor. Initializers for const declarations must be formed from literal values, other const qualified variables (not including function call parameters), or expressions of these. const int numIterations = 10; const float pi = 3.14159; const vec2 v = vec2(1.0, 2.0); const vec3 u = vec3(v, pi); const struct light { vec3 position; vec3 color; } fixedLight = light(vec3(1.0, 0.5, 0.5), vec3(0.8, 0.8, 0.5)); All the preceding variables are compile-time constants. The compiler may propagate and fold constants at compile time, using the precision of the processor executing the compiler, and need not allocate any runtime resources to const qualified variables. 3.5.5. Absent QualifierIf no qualifier is specified when a variable (not a function parameter) is declared, the variable can be both read and written by the shader. Nonqualified variables declared at global scope can be shared between shaders of the same type that are linked in the same program. Vertex shaders and fragment shaders each have their own separate global name space for nonqualified globals. However, nonqualified user-defined variables are not visible outside a program. That privilege is reserved for variables qualified as attribute or uniform and for built-in variables representing OpenGL state. Unqualified variables have a lifetime limited to a single run of a shader. There is also no concept corresponding to a static variable in a C function that would allow a variable to be set to a value and have its shader retain that value from one execution to the next. Implementation of such variables is made difficult by the parallel processing nature of the execution environment, in which multiple instantiations run in parallel, sharing much of the same memory. In general, writable variables must have unique instances per processor executing a shader and therefore cannot be shared. Because unqualified global variables have a different name space for vertex shaders than that for fragment shaders, it is not possible to share information through such variables between vertex and fragment shaders. Read-only variables can be shared if declared as uniform, and variables written by a vertex shader can be read by the fragment shader only through the varying mechanism. |