Type Qualifier (GLSL)
The OpenGL Shading Language defines a number of type qualifiers, which are applied to global and locally defined variables. These qualifiers change particular aspects of the variable. They come in a number of different categories.
- 1 Storage qualifiers
- 1.1 Default
- 1.2 Constant qualifier
- 1.3 Shader stage inputs and outputs
- 1.4 Uniforms
- 1.5 Layout qualifiers
- 1.6 Interface blocks
- 2 Precision qualifiers
- 3 Invariance qualifiers
- 4 Qualifier order
- 5 Built-in redeclaration
- 6 Deprecated qualifiers
If no storage qualifier is specified when declaring a global variable, then the variable is just a normal global variable. All global variables have a scope of the particular shader stage. So if you have two vertex shader objects that define
int someValue;, when you link them into a program, they will both be referring to the same value.
If no storage qualifier is specified for a local variable, then the variable is a local variable that can be modified.
Global or local variables can be declared with the
const qualifier. This means that the variable's value cannot be changed after it is initialized. This also means that the variable declaration must initialize the variable. This should be familiar to C++ users or users of C99.
The initializer for a const variable must be a constant expression. Constant expressions are expressions that only contain:
- Literal values (3, 2.4, etc)
- A previously defined global or local variable that is qualified as const.
- A type constructor that is given only constant expressions as arguments.
- A built-in function call who's arguments are all constant expressions. Exempt from this are the texture functions.
- A operator who's arguments are constant expressions (5+4).
Shader stage inputs and outputs
Global variables declared with the
in qualifier are shader stage input variables. These variables are given values by the previous stage (possibly via interpolation of multiple output values). These variables are not constant (in the sense of
const), but they cannot be changed by user code.
Global variables declared with the
out qualifier are shader stage output variables. These values are passed to the next stage of the pipeline (possibly via interpolation of several output values). The shader must set all output variables at some point in its execution (unless the fragment shader runs the
discard command), except for those outputs that are not read by the next shader.
Neither qualifier can be used on local variables.
These variables can be of any non-sampler basic type. They cannot be of struct types, but they can be arrays. There are usually very strict limits on the number of input and outputs available to a shader stage.
These variables are how the shader communicates to the earlier and later parts of the pipeline. Each variable name and type will match with an equivalent variable name and type on the next or previous shader stage. So, for each vertex shader output, the fragment shader will have a matching input with the same name and type. Using the same name with a different type is a linker error.
Also, it is allowed to have output variables that are not read by the next stage. It is not allowed to have input variables not written by the previous stage.
There are some special cases with input and output variables for different stages.
Vertex shader inputs
Inputs for vertex shaders are attributes. They are passed from vertex arrays to the vertex shader.
Array inputs can be defined. Each array value takes up one attribute index, sequentially from the first. So this input:
in float values will take up 5 attributes.
Matrix inputs take up one attribute index for each row.
Geometry shader inputs
Fragment shader outputs
Fragment shader outputs cannot be matrices or booleans; they must be floating-point or integer vectors or scalars.
Each fragment shader output corresponds to a draw buffer, set with the glDrawBuffers command. The association between outptu values and draw buffers is made with glBindFragDataLocation before linking the program.
Fragment shader inputs can have interpolation qualifiers. Interpolation qualifiers can also be used on the outputs from the final shader stage before the rasterizer. This traditionally has been the veretx shader, but it could be the geometry shader or the tessellation evaluation shader.
The interpolation qualifiers for corresponding values must match; failure to do so is a linker error.
Interpolation qualifiers control how interpolation happens across a triangle or other primitive. There are three basic interpolation qualifiers.
flat means that there is no interpolation. The value given to the fragment shader is based on the provoking vertex conventions.
noperspective means that there will be linear interpolation in window-space. This is usually not what you want, but it can have its uses.
smooth, the default, means to do perspective-correct interpolation.
centroid qualifier only matters when multisampling. If this qualifier is not present, then the value is interpolated to the pixel's center, anywhere in the pixel, or to one of the pixel's samples. This sample may lie outside of the actual primitive being rendered, since a primitive can cover only part of a pixel's area. The
centroid qualifier is used to prevent this; the interpolation point must fall within both the pixel's area and the primitive's area.
This is useful for parameters or computations that would have undefined values if they fell outside of the primitive's area. A square root is only defined for positive numbers, so if you are taking the square root of an interpolated value, you may need to use
Global variables can be declared with the
uniform qualifier. This means that the value does not change between multiple executions of a shader during the rendering of a primitive (ie: during a glDraw* call). These values are set by the user from the OpenGL API.
Uniforms, inputs, and outputs can also have layout qualifiers. Different stages have their own layout qualifiers, and the different types can have their own as well.
All layout qualifiers are defined with a common syntax:
layout(qualifier1, qualifier2 = value, ...) variable definition
The qualifier values are specific to a particular use of layouts. The qualifiers are order-independent, unless otherwise noted. Some qualifiers can have values assigned to them, as with qualifier2 in the above example. This values must be literals.
Some layout qualifiers can apply to all inputs or outputs. In which case, the varible definition can just be
Vertex shader attribute index
Vertex shader inputs can specify the attribute index that the particular input uses. This is done with this syntax:
layout(location = attribute index) in vec3 position;
With this syntax, you can forgo the use of
glBindAttribLocation entirely. If you try to combine the two and they conflict, the layout qualifier always wins.
Attributes that take up multiple attribute slots will be given a sequential block of that number of attributes in order starting with the given attribute. For example:
layout(location = 2) in vec3 values;
This will allocate the attribute indices 2, 3, 4, and 5.
Geometry shader primitives
Geometry shaders take a particular primitive type as input and return a particular primitive type as outputs. Also, geometry shaders have a defined maximum number of vertices that they can output. These specifications cannot be used on a variable definition; it can only be used on the qualifiers
out as a whole.
layout(primitive type) in;
layout(primitive type, max_vertices = integer value) out;
For inputs, the primitive type can be any of the following:
For outputs, the primitive type can be any of the following:
Fragment shader coordinate origin
gl_FragCoord predefined variable represents the location of the fragment in window-space. There are two layout qualifiers that can affect this. These are specified by redeclaring the predefined variable.
origin_upper_left specifies that gl_FragCoord will have the (0, 0) in the upper-left of the screen. The standard OpenGL convention is to have it in the lower-left. This does not change the Z or W of the gl_FragCoord value.
pixel_center_integer specifies that the X and Y of gl_FragCoord will be shifted by a half-pixel, so that the center of each pixel is an integer value. The standard OpenGL convention puts the integer values at the corner of the pixel.
These are used as follows:
layout(origin_upper_left) in vec4 gl_FragCoord;
Fragment shader buffer output
Fragment shader outputs can specify the buffer index that a particular output writes to. This uses the same syntax as vertex shader attributes:
layout(location = output index) out vec4 outColor;
As with vertex shader inputs, this allows the user to forgo the use of
glBindFragDataLocation. Similarly, the values in the shader override the values provided by this function.
For dual source blending, the syntax includes a second qualifier:
layout(location = output index, index = dual output index) out vec4 outColor;
Again, this allows one to forgo the use of
When using OpenGL 4.1 or an implementation that provides support for ARB_separate_shader_objects, all input and output variables can have layout
location qualifiers. The meaning is unchanged for vertex shader inputs and fragment shader outputs. But for communication between shader stages, the meaning is changed.
Normally, when linking shaders into a program, the output variables from one stage are matched with the input variables from another stage. However, when linking programs together dynamically in a program pipeline object, an alternative method of linking is allowed. The name linking is allowed, but so is linking by location.
For example, given a vertex shader that provides these outputs:
layout(location = 0) out vec4 color; layout(location = 1) out vec2 texCoord; layout(location = 2) out vec3 normal;
Normally, the fragment shader would have to use exactly those names. This is not required when using separate programs. If an output has a location specified, the next stage can provide that location with a different name. So a matching fragment shader could look like this:
layout(location = 0) in vec4 diffuseAlbedo; layout(location = 1) in vec2 texCoord layout(location = 2) in vec3 cameraSpaceNormal;
Uniform block layout
Uniforms in a uniform block can have layout qualifiers that specify how the memory is assigned by the shader compiler.
Inputs, outputs, and uniforms can be grouped in interface blocks. These have particular properties.
There are three precision qualifiers:
lowp. They have no semantic meaning or functional effect. They can apply to any floating-point type (vector or matrix), or any integer type.
All variables of a certain type can be declared to have a precision by using the
precision statement. It's syntax is as follows:
precision precision-qualifier type;
In this case, type can only be
int. This will affect all collections of that basic type. So
float will affect all floating-point scalars, vectors, and matrices. The
int type will affect all signed and unsigned integer scalars and vectors.
There is a way to qualify certain output variables as being invariant. This allows different programs to compute the exact same answer, assuming certain conditions are met.
invariant qualifier must be the first qualifier in a definition. The
gl_Position predefined variable can be redeclared to be invariant with this:
Qualifiers always come in a particular order. For non-parameter values, the order is always this:
invariant-qualifier interpolation-qualifier layout-qualifier other-storage-qualifier precision-qualifier
centroid qualifier, if present, must immediately precede
out. For the purpose of ordering, it is considered part of the
out storage qualifier, not part of the interpolation qualifier.
The GLSL defines a number of predefined variables at the various shader stages. These variables are defined with a particular set of qualifiers, as stated in the above article. If you wish to use them with a different qualifier, you can redeclare them. You must use the same type, however. And some variables cannot be redeclared with a new qualifier; gl_Position in the vertex shader for example cannot use an interpolation qualifier at all.
attribute qualifier is effectively equivalent to an input qualifier in vertex shaders. It cannot be used in any other shader stage. It cannot be used in interface blocks.
varying qualifier is equivalent to the input of a fragment shader or the output of a vertex shader. It cannot be used in any other shader stages. It cannot be used in interface blocks.