Implement Water (Placement and Rendering)

Infantry Online 2 should support water rendering for the sake of having interesting environments. Note that we will not support water collision or physics simulation, and we will not create wakes and other effects that displace the water geometry.

The "waves" of the water are provided by two normal maps that blend together and move in a user-specified manner. See more details below.

Map Editor

We should leverage the existing volume functionality to specify a set of points that represents the water boundary. This means that all bodies of water will be on the same vertical plane.

Within the editor we should have a "Water Bodies..." menu item that brings up a popup that has the list of bodies and their associated volumes. Note that we should always have a 1:1 water body:volume association.

Within the popup the user should be able to modify:

  1. Color and Opacity.
  2. Wave 1 and Wave 2 normal maps. (image files, explained further below in Rendering).
  3. Wave 1 and Wave 2 speed values (world units/sec).
  4. Wave 1 and Wave 2 rotation values. (theta angles relative to world space).
  5. Distortion amount (explained further below in Rendering).
  6. Specularity amount.
  7. Skybox (optional - image file).
  8. Flag: Show Cast Shadows.

Rendering

As previously mentioned the two wave normal maps are used to distort the water so it does not look like a perfect mirror reflection. We should allow for users to specify the normal maps to use. We should provide a few standard ones for them to use.

When the game loads the map up, all bodies of water should be triangulated and stored as triangle sets. We can also store all the boundary edges in case we decide to create small shore sprite animations later on.

With that in mind, here is the rendering pipeline for water:

Note that we render bodies of water after the scene has already been rendered and we have the shadow map ready to use.

  1. Render the body of water to a stencil mask.

  2. Given any hull point, and having the up vector, we now have the mirroring and clipping plane so we can re-render the scene upside down (full screen quad). Set the stencil mask so that it will only render the scene if the stencil passes. We now have a texture of the scene, upside down, for that body of water. (optionally, we can also render the geometry that is underneath the water to another texture, for the sake of refraction, but this will not be implemented yet)

  3. We can now render the body of water. Transform the UVs of the normal maps using the rotation angles and the current time value, and blend the two normal maps, averaging and normalizing their direction vectors. Using the distortion amount and the average direction vector, we can offset our pixel coordinates when we are sampling the upside-down scene texture. Note that we should use the same offset for the shadow map as well as the skybox so that the distortion looks consistent across between the three textures.

  4. Lastly, apply directional lighting and specular highlights as needed. The parts of the normal map that face away from the directional light should be slightly darker than those that face towards the light.

Future Improvements

  1. Because we have the depth map from the mesh rendering stage, we can get world-space positions of each fragment under the water plane and calculate the distance to the water plane, giving us the water depth at that location. If needed, we can also get the length of the line segment from camera's location as it intersects the plane and travels to this fragment, giving us that distance as well.

  2. Because we have the edges of the water, we can create shoreline foam and wave crashes (can be configurable in the editor) as sprites that are periodically faded in and out similar to particles. Their movement would be perpendicular to the edge from which it emanates.