15.4. A Simple Noise Shader
Now we put all these ideas into some OpenGL shaders that do some interesting rendering for us. The first shader we look at uses noise in a simple way to produce a cloud effect.
15.4.1. Application Setup
Very little needs to be passed to the noise shaders discussed in this section and in Section 15.5 and Section 15.6. The vertex position must be passed in as always, and the surface normal is needed for performing lighting computations. Colors and scale factors are parameterized as uniform variables for the various shaders.
15.4.2. Vertex Shader
The code shown in Listing 15.3 is the vertex shader that we use for the four noise fragment shaders that follow. It is fairly simple because it really only needs to accomplish three things.
No matter how the object is drawn, fragments always produce the same position values (or very close to them); therefore, the noise value obtained for each point on the surface is also the same (or very close to it). The application can set a uniform variable called Scale to optimally scale the object in relationship to the size of the noise texture.
Listing 15.3. Cloud vertex shader
15.4.3. Fragment Shader
After we've computed a noise texture and used OpenGL calls to download it to the graphics card, we can use a fairly simple fragment shader together with the vertex shader described in the previous section to make an interesting "cloudy sky" effect (see Listing 15.4). This shader results in something that looks like the sky on a mostly cloudy day. You can experiment with the color values to get a result that is visually pleasing.
This fragment shader receives as input the two varying variablesLightIntensity and MCpositionthat were computed by the vertex shader shown in the previous section. These values were computed at each vertex by the vertex shader and then interpolated across the primitive by the rasterization hardware. Here, in our fragment shader, we have access to the interpolated value of each of these variables at every fragment.
The first line of code in the shader performs a 3D texture lookup on our 3D noise texture to produce a four-component result. We compute the value of intensity by summing the four components of our noise texture. This value is then scaled by 1.5 and used to perform a linear blend between two colors: white and sky blue. The four channels in our noise texture have mean values of 0.25, 0.125, 0.0625, and 0.03125. An additional 0.03125 term is added to account for the average values of all the octaves at higher frequencies. You can think of this as fading to the average values of all the higher frequency octaves that aren't being included in the calculation, as described earlier in Section 15.1. Scaling the sum by 1.5 stretches the resulting value to use up more of the range from [0,1].
The computed color is then scaled by LightIntensity value to simulate a diffuse surface lit by a single light source. The result is assigned to the built-in variable gl_FragColor with an alpha value of 1.0 to produce the color value that is used by the remainder of the OpenGL pipeline. An object rendered with this shader is shown in Color Plate 24. Notice that the texture on the teapot looks a lot like the final image in Figure 15.6.
Listing 15.4. Fragment shader for cloudy sky effect