JavaScript EditorFree JavaScript Editor     Ajax Editor 

Main Page
Previous Page
Next Page

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.

  1. As in all vertex shaders, our vertex shader transforms the incoming vertex value and stores it in the built-in varying variable gl_Position.

  2. Using the incoming normal and the uniform variable LightPos, the vertex shader computes the light intensity from a single white light source and applies a scale factor of 1.5 to increase the amount of illumination.

  3. The vertex shader scales the incoming vertex value and stores it in the varying variable MCposition. This value is available to us in our fragment shader as the modeling coordinate position of the object at every fragment. It is an ideal value to use as the input for our 3D texture lookup.

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

varying float LightIntensity;
varying vec3  MCposition;

uniform vec3  LightPos;
uniform float Scale;

void main()
    vec3 ECposition = vec3(gl_ModelViewMatrix * gl_Vertex);
    MCposition      = vec3(gl_Vertex) * Scale;
    vec3 tnorm      = normalize(vec3(gl_NormalMatrix * gl_Normal));
    LightIntensity  = dot(normalize(LightPos - ECposition), tnorm);
    LightIntensity *= 1.5;
    gl_Position     = ftransform();

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

varying float LightIntensity;
varying vec3  MCposition;

uniform sampler3D Noise;
uniform vec3 SkyColor;     // (0.0, 0.0, 0.8)
uniform vec3 CloudColor;   // (0.8, 0.8, 0.8)

void main()
    vec4  noisevec  = texture3D(Noise, MCposition);

    float intensity = (noisevec[0] + noisevec[1] +
                       noisevec[2] + noisevec[3] + 0.03125) * 1.5;

    vec3 color   = mix(SkyColor, CloudColor, intensity) * LightIntensity;
    gl_FragColor = vec4(color, 1.0);

Previous Page
Next Page

JavaScript EditorAjax Editor     JavaScript Editor