This sample demonstrates dynamic, interactive water surface animation using compute shaders


APIs Used

  • glDispatchCompute
  • glMemoryBarrier
  • glGenFramebuffers
  • glBindFramebuffer

Shared User Interface

The Graphics samples all share a common app framework and certain user interface elements, centered around the "Tweakbar" panel on the left side of the screen which lets you interactively control certain variables in each sample.

To show and hide the Tweakbar, simply click or touch the triangular button positioned in the top-left of the view.

Technical Details


This sample shows how compute shaders can be used to implement height field-based water simulation techniques on the GPU, and use the results for rendering.

In this technique, the water surface is represented as regular grid with height and velocity values for each point.

Compute shader is used for surface transformation and computation of normals, which are required for realistic rendering.

Rendering Procedure:

  1. In the first pass, the compute shader calculates updated velocity vectors per water height map sample. New height values are computed for each point via integration, and the results written into a temporary buffer.

  2. In the second pass, the compute shader calculates water surface normals at each point via gradients, and updates the resulting data.

  3. The vertex shader accesses the resulting height and normal data to compute fresnel and reflection vectors.

  4. The fragment shader uses the fresnel and reflection vectors to draw, shade, and reflect the surface.

UI options:

  • Toggle between CPU and GPU simulation

  • Enable/Disable rain

  • Select grid size

  • Select number of surfaces

  • Adjust wave size

  • Adjust wave damping

  • Show fresnel/normals


Compute shader:

Height and velocity data is stored in regular buffers, which are used as input attributes to the vertex shader.

  1. Height Computation Pass

    1. Update the velocities using a nearest-neighbor distribution formula.

    2. Apply a damping factor to the current velocity.

    3. Update height value into temporary compute buffer.

    4. Apply a "disturbance" function to the temporary height value.

  2. Normal Computation Pass

    1. Compute surface normals and write into the gradients buffer.

    2. Copy temporary height data into output height field buffer.

Vertex shader:

The two result buffers from the compute shader stage (heights and normals) are bound as regular vertex attributes in the vertex shader stage.

The vertex shader does the following:

  1. Fetches gradients from the compute buffer and computes the normal for the vertex.

  2. Fetches the height value from the compute buffer and calculates the position of the vertex.

  3. Transforms and projects the vertex.

  4. Computes the fresnel term, reflection and refraction vectors and passes them to the fragment shader.

Fragment Shader:

The fragment shader fetches a texel from the environment map (a cube map texture) using the reflection and refraction vectors, and blends between them using a fresnel term.