CS 455 Lecture 23 – Water
Agenda
- what ?s
- homework 4
- water
- forward differencing
TODO
As a lab exercise:
- 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. - 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. - In the fragment shader, add a
uniform
for the camera’s world position. Upload it inOnDraw
. Compute the incident vector. - 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.
- Add a
uniform
for the skybox texture in the fragment shader and inOnDraw
. A skybox texture has already been made; it’s calledskybox_texture
. - 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? - 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 andOnDraw
. A texture object has already been made; it’s callednoise_texture
. - 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 atime
uniform in the fragment shader and inOnDraw
. - 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
- 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
vec3
s. Compute their cross product to producenormal_world
. - Alter the reflection vector calculate to use
normal_world
instead of the fixed y-axis. - How does it look? Try some different heightmap series:
wkcamp
,cupertin-lake
,midnight-silence
. - Send me a screenshot.