GLSL Programming/Blender/RGB Cube

This tutorial introduces varying variables. It builds on the tutorial about a minimal shader.

In this tutorial we will write a shader to render an RGB cube similar to the one shown to the left. The color of each point on the surface is determined by its coordinates; i.e., a point at position $$(x,y,z)$$ has the color $$(\text{red}, \text{green}, \text{blue}) = (x,y,z)$$. For example, the point $$(x,y,z)=(0,0,1)$$ is mapped to the color $$(\text{red}, \text{green}, \text{blue}) = (0,0,1)$$, i.e. pure blue. (This is the blue corner in the lower right of the figure to the left.)

Preparations
Since we want to create an RGB cube, you first have to create a cube mesh as described in the tutorial about a minimal shader. Continue with creating a Python script and the game logic as described in the tutorial about a minimal shader.

The Shader Code
Here is the source code of the vertex shader, which you should copy & paste into the assignment to  in your Python script: And here is the source code of the fragment shader which should be assigned to : If your cube is not colored correctly, check the console for error messages.

Varying Variables
The main task of our shader is to set the output fragment color in the fragment shader to the position  that is available in the vertex shader. Actually, this is not quite true: the coordinates in  for Blenders's default cube are between -1.0 and +1.0 while we would like to have color components between 0.0 and 1.0; thus, we need to add 1.0 to the x, y, and z component and multiply the result with 0.5, which is done by this expression:.

The main problem, however, is: how do we get any value from the vertex shader to the fragment shader? It turns out that the only way to do this is to use varying variables (or varyings for short). Output of the vertex shader can be written to a varying variable and then it can be read as input by the fragment shader. This is exactly what we need.

To specify a varying variable, it has to be defined with the modifier  (before the type) in the vertex and the fragment shader outside of any function; in our example:. And here comes the most important rule about varying variables:

This is required to avoid ambiguous cases where the GLSL compiler cannot figure out which varying variable of the vertex shader should be matched to which varying variable of the fragment shader.

Variations of this Shader
The RGB cube represents the set of available colors (i.e. the gamut of the display). Thus, it can also be used show the effect of a color transformation. For example, a color to gray transformation would compute either the mean of the red, green, and blue color components, i.e. $$(\text{red}+\text{green}+\text{blue}) / 3$$, and then put this value in all three color components of the fragment color to obtain a gray value of the same intensity. Instead of the mean, the relative luminance could also be used, which is $$0.21 \text{ red} + 0.72 \text{ green} + 0.07 \text{ blue}$$. Of course, any other color transformation (changing saturation, contrast, hue, etc.) is also applicable.

Another variation of this shader could compute a CMY (cyan, magenta, yellow) cube: for position $$(x, y, z)$$ you could subtract from a pure white an amount of red that is proportional to $$x$$ in order to produce cyan. Furthermore, you would subtract an amount of green in proportion to the $$y$$ component to produce magenta and also an amount of blue in proportion to $$z$$ to produce yellow.

If you really want to get fancy, you could compute an HSV (hue, saturation, value) cylinder. For $$x$$ and $$z$$ coordinates between -0.5 and +0.5, you can get an angle $$H$$ between 0 and 360° with  in GLSL and a distance $$S$$ between 0 and 1 from the $$y$$ axis with. The $$y$$ coordinate for Blender's built-in cylinder is between -1 and 1 which can be translated to a value $$V$$ between 0 and 1 by $$(y + 1.0) / 2.0$$. The computation of RGB colors from HSV coordinates is described in the article on HSV in Wikipedia.

Interpolation of Varying Variables
The story about varying variables is not quite over yet. If you select the cube object in the 3D View and switch to Edit Mode, you will see that that it includes only 8 vertices. Thus, the vertex shader might be called only eight times and only eight different outputs are written to the varying variable. However, there are many more colors on the cube. How did that happen?

The answer is implied by the name varying variables. They are called this way because they vary across a triangle. (Note that all polygons are decomposed into triangles before they are rendered in OpenGL.) In fact, the vertex shader is only called for each vertex of each triangle. If the vertex shader writes different values to a varying variable for different vertices, the values are interpolated across the triangle. The fragment shader is then called for each pixel that is covered by the triangle and receives interpolated values of the varying variables. The details of this interpolation are described in “Rasterization”.

If you want to make sure that a fragment shader receives one exact, non-interpolated value by a vertex shader, you have to make sure that the vertex shader writes the same value to the varying variable for all vertices of a triangle.

Summary
And this is the end of this tutorial. Congratulations! Among other things, you have seen:
 * What an RGB cube is.
 * What varying variables are good for and how to define them.
 * How the values written to a varying variable by the vertex shader are interpolated across a triangle before they are received by the fragment shader.