Cg Programming/Unity/RGB Cube

This tutorial discusses vertex output parameters and fragment input parameters. It is assumed that you are familiar with.

In this tutorial we will write a shader to render an RGB cube similar to the one shown. 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 .)

Preparations
Since we want to create an RGB cube, you first have to create a cube game object. As described in for a sphere, you can create a cube game object by selecting GameObject > 3D Object > Cube from the main menu. Continue with creating a material and a shader object and attaching the shader to the material and the material to the cube as described in.

The Shader Code
Here is the shader code, which you should copy & paste into your shader object:

If your cube is not colored correctly, check the console for error messages (by selecting Window > General > Console from the main menu), make sure you have saved the shader code, and check whether you have attached the shader object to the material object and the material object to the game object.

Communication between Vertex and Fragment Shaders
The main task of our shader is to set the fragment output color (i.e. the fragment output parameter with semantic ) in the fragment shader to the vertex position that is available in the vertex shader. Actually, this is not quite true: the coordinates in the vertex input parameter with semantic  for Unity's default cube are between -0.5 and +0.5 while we would like to have color components between 0.0 and 1.0; thus, we need to add 0.5 to the x, y, and z component, 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 pairs of vertex output parameters and fragment input parameters with the same semantics ( in this case). In fact, it is only the semantics that are used to determine which vertex output parameters correspond to which fragment input parameters. Instead of the semantic  we could also use another semantic, e.g. , it doesn't really matter here, except that parameters with the semantic   are often clamped to values between 0 and 1 (which would be OK in this case). It is, however, common to use the semantics,  ,  , etc. for all kinds of parameters.

The next problem is to specify multiple vertex output parameters. Since the return instruction can only return one value, it is common to define a structure for all the required vertex output parameters. Here, this structure is called : By using this structure as an argument of the fragment shader function, we make sure that the semantics match. Note that in Cg (in contrast to C), we don't have to write  when defining variables of this type but we can just use the name   (without  ) for the same type.

The out Qualifier
An alternative to the use of an output structure would be to use arguments of the vertex shader function with the  qualifier, e.g.: However, the use of an output structure is more common in practice and it makes sure that vertex output parameters and fragment input parameters have matching semantics.

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 could compute 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 brightness. 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 Cg and a distance $$S$$ between 0 and 1 from the $$y$$ axis with. The $$y$$ coordinate for Unity'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 Vertex Output Parameters
The story about vertex output parameters and fragment input parameters is not quite over yet. If you select the cube game object, you will see in the Scene View that it consists of only 12 triangles and 8 vertices. Thus, the vertex shader might be called only eight times and only eight different outputs are written to the vertex output parameters. However, there are many more colors on the cube. How did that happen?

'''In fact, the vertex shader is only called for each vertex of each triangle. However, the different values of the vertex output parameters for the different vertices 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 vertex output parameters as fragment input parameters.''' The details of this interpolation are described in.

If you want to make sure that a fragment shader receives one exact, non-interpolated value by a vertex shader (so-called flat shading), you can either make sure that the vertex shader writes the same value to the vertex output parameters for all vertices of a triangle, or you can use the HLSL storage-class modifier. In our example, you could use this struct:

Summary
And this is the end of this tutorial. Congratulations! Among other things, you have seen:
 * What an RGB cube is.
 * What an output structure is and how to define it.
 * How output structures are used to make sure that vertex output parameters have the same semantics as fragment input parameters.
 * How the values written to vertex output parameters are interpolated across a triangle before they are received as input parameters by the fragment shader.