Structure of a shader's code

The shader's text is a C++ file and contains the void main() function. This function works on GPU and is called once for processing every object (vertex, primitive, or pixel, depending on the shader's type). The main() function has no parameters. All the necessary parameters such as coordinates, colors, and textures are held by built-in GLSL variables such as gl_Color and gl_Position. Also, you can use your own custom parameters passed from your CPU code, such as float time (see the Passing a float parameter to a shader section).

The simplest code for the fragment shader will look as follows:

#version 120
void main() {
  gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
}

This shader will render everything in the red color. Namely, if you enable the shader and then draw lines, images, and any other objects, then OpenGL will call the shader's main() function for each drawn pixel, and the shader will set the pixel color to red. Let's study the shader's code in detail.

The first line #version 120 is a compiler directive, which means that we want to use the GLSL Version 1.2 in the shader. Though this is quite an old version of the language, it is currently used in all the openFrameworks examples and in all our examples in the chapter. The reason is that Version 1.2 is the last version that contains many built-in variables, which simplify shader interfacing. (Most of these variables were removed in the latest GLSL versions. Though this makes shaders more flexible, it seems a little hard to begin the study of shaders with a very high level of flexibility.)

The rest of the code is the main() function. The body of the function consists of a single line which sets the gl_FragColor variable to value vec4( 1.0, 0.0, 0.0, 1.0 ). Actually, gl_FragColor is a built-in variable, which holds an output color of the pixel processed by a fragment shader. The mission of any fragment shader is to set a value to this gl_FragColor variable inside its main() function. In this example, we set it to a constant value. For more realistic shaders, the output color depends on the pixel position gl_FragCoord, current drawing color, textures, normals, lighting information and other parameters.

Note

The list of built-in GLSL variables can be found at:

http://www.opengl.org/sdk/docs/manglsl/xhtml/index.html#Built-in%20Variables

Notation vec4( 1.0, 0.0, 0.0, 1.0 ) returns the object of type vec4. This is a vector with four float components. In our example, vec4 holds the color and its components are red, green, blue, and alpha respectively. In GLSL, color components have a meaningful range from 0.0 to 1.0, so vec4( 1.0, 0.0, 0.0, 1.0 ) means the opaque red color.

Besides vec4, there are types vec2 and vec3, which hold the float values with two and three components respectively. All these types are implemented in hardware and work very fast.

There are several ways for accessing the vector components in GLSL, similar to working with union in C++. Namely, if you have a vec4 v object, you can access its four components in the following ways:

  • As an ordinal array: v[0], v[1], v[2], and v[3]
  • As a color: v.r, v.g, v.b, and v.a
  • As coordinates: v.x, v.y, v.z, and v.w

    Note

    The fourth coordinate v.w comes from projective coordinates' notation and most often is set to 1.0.

  • As texture coordinates: v.s, v.t, v.p, and v.q

Tip

In GLSL, you can use a notation called swizzle. This technique allows the usage of any combination of letters of the same type to access several vector components at once:

  • v.xyz means the vec3 vector (v.x, v.y, v.z)
  • v.bg means the vec2 vector ( v.b, v.g )
  • v.xy = vec2( 0.0, 100.0 ); and vec3 u = v.xxx; are correct GLSL operations

See details on GLSL types and swizzle at http://www.opengl.org/wiki/GLSL_Type.

There is an exhaustive list of the built-in mathematical functions, which work with numbers and vectors, such as sin( x ), distance( v, u ), and dot( u, v ). You can find the list of functions in the full language specification at http://www.opengl.org/documentation/glsl/.

In order to make the fragment shader work, you need to enable a vertex shader too. The simplest vertex shader just transforms the input vertex position gl_Vertex to output the vertex position gl_Position using a built-in matrix gl_ModelViewProjectionMatrix. This matrix translates the internal coordinate system of an object into screen coordinates. The simplest vertex shader's code is as follows:

#version 120
void main() {
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

The preceding shaders' examples only illustrate the shaders' structure and are too trivial to be useful. Now, we will consider a really useful example of using the shaders in an openFrameworks project.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset