teaching machines

CS 455 Lecture 23 – Water

May 5, 2015 by . Filed under cs455, lectures, spring 2015.

Agenda

TODO

As a lab exercise:

  1. Sync with the class repository to get the water starter project. Run it to find some very disappointing water. We’re going to do a couple of things to make it look better: 1) add environment mapping to make it reflect the skybox and 2) perturb its normals using a 3D noise texture.
  2. Let’s start with environment mapping. Recall the concepts behind this technique: in world space, we shoot a vector from the eye to the vertex, and reflect that vector around the vertex’s normal. That reflected vector serves as a texture coordinate into a cube map. Last time, we implemented the reflection in the vertex shader. This time, our normals are going to be retrieved from a noise texture in the fragment shader. In the vertex, however, we’ll need to output the vertex’s world position. Add a varying for fposition_world. Compute its value in the vertex shader.
  3. In the fragment shader, add a uniform for the camera’s world position. Upload it in OnDraw. Compute the incident vector.
  4. Compute the reflection vector by reflecting the incident vector about the water’s normal. For now, assume the normal is the y-axis. This will change soon.
  5. Add a uniform for the skybox texture in the fragment shader and in OnDraw. A skybox texture has already been made; it’s called skybox_texture.
  6. Look up the color in the skybox using the reflection vector and the textureCube function. Assign this color to the fragment. How do things look?
  7. Now let’s perturb the surface. We’re not going to actually move any geometry. We’re simply going to derive the normals across the water’s surface from a slice of a 3D noise texture. As time passes, we’ll read different slices of the texture, producing a rippling effect. Start by adding a uniform for the noise texture to the fragment shader and OnDraw. A texture object has already been made; it’s called noise_texture.
  8. For looking up noise intensities, we’ll need a 3D texture coordinate. Two of these we can get from the 2D texture coordinate vertex attribute. Add a varying to get these values in the fragment shader. The third we can get from a clock reading. Add a time uniform in the fragment shader and in OnDraw.
  9. We are not using the noise texture for color. We are using it as a heightmap; specifically, we are using it to calculate normals. How do we determine the normal at a given location? Find two tangent vectors and then take their cross product. How do we find two tangent vectors? Recall that a tangent vector lies across a surface, running parallel to the surface’s general orientation. We can find these parallel vectors by a technique called forward differencing. First, take three intensity readings:
    here = lookup height at texture coordinates
    right = lookup height at here + nudge right
    above = lookup height at here + nudge up
  10. With these three values in hand, we can determine the tangent vectors by computing their differences on all three dimensions:
    tangentRight = [1, right - here, 0]
    tangentAbove = [0, above - here, 1]

    Both are vec3s. Compute their cross product to produce normal_world.

  11. Alter the reflection vector calculate to use normal_world instead of the fixed y-axis.
  12. How does it look? Try some different heightmap series: wkcamp, cupertin-lake, midnight-silence.
  13. Send me a screenshot.