JavaScript EditorFree JavaScript Editor     Ajax Editor 

Main Page
Previous Page
Next Page

3.1. Example Shader Pair

A program typically contains two shaders: one vertex shader and one fragment shader. More than one shader of each type can be present, but there must be exactly one function main between all the fragment shaders and exactly one function main between all the vertex shaders. Frequently, it's easiest to just have one shader of each type.

The following is a simple vertex and fragment shader pair that can smoothly express a surface temperature with color. The range of temperatures and their colors are parameterized. First, we show the vertex shader. It is executed once for each vertex.

// uniform qualified variables are changed at most once per primitive
uniform float CoolestTemp;
uniform float TempRange;

// attribute qualified variables are typically changed per vertex
attribute float VertexTemp;

// varying qualified variables communicate from the vertex shader to
// the fragment shader
varying float Temperature;

void main()
    // compute a temperature to be interpolated per fragment,
    // in the range [0.0, 1.0]
    Temperature = (VertexTemp - CoolestTemp) / TempRange;

       The vertex position written in the application using
       glVertex() can be read from the built-in variable
       gl_Vertex. Use this value and the current model
       view transformation matrix to tell the rasterizer where
       this vertex is.
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

That's it for the vertex shader. Primitive assembly follows the preceding vertex processing, providing the rasterizer with enough information to create fragments. The rasterizer interpolates the Temperature values written per vertex to create values per fragment. Each fragment is then delivered to a single execution of the fragment shader, as follows:

// uniform qualified variables are changed at most once per primitive
// by the application, and vec3 declares a vector of three
// floating-point numbers
uniform vec3 CoolestColor;
uniform vec3 HottestColor;

// Temperature contains the now interpolated per-fragment
// value of temperature set by the vertex shader
varying float Temperature;

void main()
    // get a color between coolest and hottest colors, using
    // the mix() built-in function
    vec3 color = mix(CoolestColor, HottestColor, Temperature);

    // make a vector of 4 floating-point numbers by appending an
    // alpha of 1.0, and set this fragment's color
    gl_FragColor = vec4(color, 1.0);

Both shaders receive user-defined state from the application through the declared uniform qualified variables. The vertex shader gets information associated with each vertex through the attribute qualified variable. Information is passed from the vertex shader to the fragment shader through varying qualified variables, whose declarations must match between the vertex and fragment shaders. The fixed functionality located between the vertex and fragment processors will interpolate the per-vertex values written to this varying variable. When the fragment shader reads this same varying variable, it reads the value interpolated for the location of the fragment being processed.

Shaders interact with the fixed functionality OpenGL pipeline by writing built-in variables. OpenGL prefixes built-in variables with "gl_". In the preceding examples, writing to gl_Position tells the OpenGL pipeline where the transformed vertices are located, and writing to gl_FragColor tells the OpenGL pipeline what color to attach to a fragment.

Execution of the preceding shaders occurs multiple times to process a single primitive, once per vertex for the vertex shader and once per fragment for the fragment shader. Many such executions of the same shader can happen in parallel. In general, there is no direct tie or ordering between shader executions. Information can be communicated neither from vertex to vertex nor from fragment to fragment.

Previous Page
Next Page

JavaScript EditorAjax Editor     JavaScript Editor