Triton
Visual simulation library for ocean rendering.
Integrating Triton with terrain and shallow water

If your application includes terrain as well as open ocean, there are several ways to achieve good results at the shoreline.

The best approach is to provide Triton with a height map of your terrain, that includes bathymetry data. If a height map is provided using Triton::Environment::SetHeightMap(), the water will automatically become more transparent near the shore, and waves will be dampened to minimize any depth precision artifacts. Take care to only call SetHeightMap when your height map changes; Triton needs to copy the texture into system memory, and you don't want to do that every frame. Be sure to create your Ocean object with breaking waves flag enabled to take full advantage of the height map's data.

If you have provided a valid height map, you may also use Triton::Environment::SetBreakingWavesParameters() to simulate waves breaking at the shoreline. This will produce high-wavelength waves that slow down as they approach the coastline with foam and displacement effects. They look great from the air, but for performance reasons the waves don't actually curl over themselves and generate spray particles when breaking. You'll need to specify the world direction toward the local coastline so the waves travel in the correct direction; generally you'll be fine leaving the other parameters set to their defaults. It's important that your bathymetry data in the height map is realistic; the waves will start to appear as the water becomes more shallow, and realistic depth information will result in waves appearing at the proper distance from shore and following the contour of the coastline.

In addition to a floating-point height map texture, Triton::Environment::SetHeightMap() also requires a matrix to transform world coordinates into texture coordinates. Here's an example of computing this matrix and creating a height map texture of the proper format using OpenGL; it creates an orthogonal projection with a view looking down at the entire terrain from an altitude of 10000m, in a coordinate system where Y is "up" and the terrain's extents range from (minX, minZ) to (maxX, maxZ).

glEnable(GL_TEXTURE_2D);
glGenTextures(1, &heightMap);
glBindTexture(GL_TEXTURE_2D, heightMap);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE32F_ARB, HEIGHT_MAP_DIM, HEIGHT_MAP_DIM, 0, GL_LUMINANCE, GL_FLOAT, (void*)heightData);
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glLoadIdentity();
const GLdouble bias[16] = {
0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0};
glLoadMatrixd(bias);
double xrange = maxX - minX;
double zrange = maxZ - minZ;
glOrtho(-(xrange * 0.5), xrange * 0.5, -(zrange * 0.5), zrange * 0.5, -1.0, 1.0);
double eyex = minX + xrange * 0.5;
double eyey = 10000.0;
double eyez = minZ + zrange * 0.5;
double centerx = eyex;
double centery = 0;
double centerz = eyez;
double upx = 0;
double upz = -1.0;
double upy = 0;
gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz);
glGetDoublev(GL_TEXTURE_MATRIX, heightMapMatrix);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);

You would then pass the resulting height map texture and matrix to Triton like this:

tritonEnvironment->SetHeightMap((Triton::TextureHandle)heightMap, Triton::Matrix4(heightMapMatrix));
int TextureHandle
A renderer-agnostic handle for a texture.
Definition: TritonCommon.h:56
An implementation of a 4x4 matrix and some simple operations on it.
Definition: Matrix4.h:30

DirectX9 users should refer to the documentation for Triton::Envrionment::SetHeightMap() for important information on the type of texture Triton expects.

OpenSceneGraph users may refer to the OSGDynamicHeightMap sample included with the SDK. This sample generates a height map from the scene surrounding the camera automatically, and feeds it to Triton for smooth coastline blending. This sample also includes code for a vertex program that can compute accurate height map data for geocentric or ECEF terrains.

If you don't have a height map available for your terrain, be sure to call Triton::Ocean::SetDepth() if your camera is near the shoreline. This method allows you to specify the depth and surface normal of the seafloor at the camera position. At shallow depths, this information is used to make the waves more pronounced near the shore, and to make the water more transparent near the shoreline. If your terrain includes textured geometry extending under the water for some distance offshore, your ocean floor will influence the color of the ocean near the shore. The Triton demo application available from our website illustrates this effect when the "Beach" checkbox is enabled.

For handling the water/land boundary without height maps, the simplest approach is to rely on the depth buffer. You'll need to ensure your depth buffer has adequate precision for this to work well. Use a 32 bit depth buffer if your system supports it, and ensure your near and far clip planes are set as close together as is practical. If depth buffer precision issues are a problem in the distance, you may also use the method Triton::Ocean::SetDepthOffset() to force the water's depth values to be closer to the camera. A value of 0.01 generally seems to clear things up.

If you have a copy of your depth buffer available in a texture that includes terrain but does not include Triton's water, you might also have a look at the Triton::Environment::SetDepthMap() method as an alternative to SetHeightMap(). This method comes with several caveats however, and use of SetHeightMap() is preferred.

If your existing terrain database includes geometry for the ocean surface, you may be better off using Triton::Ocean::SetPatchShader() to apply Triton's shaders to your existing water geometry. This same technique may be used to render smaller bodies of water, instead of the infinite oceans rendered by Triton::Ocean::Draw(). See Rendering User-Defined Patches of Geometry for more information.

If your terrain includes areas of land that are below the simulated sea level, you'll need to prevent Triton from rendering waves in these areas. A simple solution is to render your terrain while writing to the stencil buffer, and then enabling the stencil test surrounding the call to Triton::Ocean::Draw(). Or, use Triton::Ocean::SetPatchShader()with your own water grids to be more selective about where water is rendered in your scene. Another solution is a technique called "depth masking," where a transparent polygon is drawn over the below-sea-level area that you want to remain free of water, but with depth writes on, prior to drawing the ocean. Depth masking is a good way to prevent water from appearing inside ship hulls, for example.

You may adjust the sea level of Triton using Triton::Environment::SetSeaLevel().