Water |
This topic describes how to define bodies of water, e.g. rivers, ponds, lakes, ocean.
This topic contains the following sections:
The following video shows various features of the water rendering in DigitalRune Graphics.
DigitalRune Graphics uses following types to define bodies of water:
A WaterNode is a SceneNode that defines a body of water. WaterNodes are rendered by the WaterRenderer.
The property Volume is a Shape which defines the water surface and the underwater volume where an underwater effect is rendered. If Volume is set to a valid shape, the shape defines where the water will be rendered. The shape can be a "flat" shape, e.g. a horizontal rectangle. It only has to be a non-flat shape, e.g. a box, when the camera can enter the water volume and an underwater effect should be rendered. If Volume is null, a horizontal infinite ocean plane is rendered and everything under the plane counts as underwater. The Volume should be positioned in the local scene node space, such that the water surface goes through the origin of the scene node (y = 0). And the water surface should be roughly horizontal. The WaterRenderer is optimized for this configuration.
The water surface can reflect a static skybox (property SkyboxReflection) or a planar reflection (property PlanarReflection). If the water surface is displaced by high waves, then planar reflections might lead to some visual artifacts. For small waves a planar reflection is usually an adequate approximation of the real reflection. Planar reflections are expensive in most scenes and in most games a static skybox should be used.
The Volume is also used as the bounding shape for frustum culling. If the water is displaced by waves, then Volume is not a valid shape because some water vertices can be moved outside this bounding shape. For this case, an ExtraHeight has to be set which is at least as large as the highest wave. Internally, ExtraHeight is used to increase the bounding shape to get a shape which will contain all waves.
Tip |
---|
An infinite ocean can be rendered by setting Volume to null. When the ocean is not visible, e.g. because the player is inside a house or a cave, it is recommended to disable the ocean, e.g. by setting SceneNodeIsEnabled to false. If the camera cannot enter the water, it is also recommended to set EnableUnderwaterEffect to false. |
The class Water defines the visual appearance of the water.
The water is rendered using reflections, refractions and a specular highlight from the dominant directional light. Water ripples are created using two normal maps which are moved over the water surface. Foam can be rendered where the water intersects geometry and for high water waves. The water also supports real-time underwater caustics (at the moment only for WaterWaves and not for water ripples from the normal maps).
The water color is defined by WaterColor and UnderwaterFogDensity. By tweaking this properties, you can create uniform blue water, brown, muddy water or a Caribbean water which is turquoise near the shore and dark blue in the distance.
The class WaterFlow can be used to define the direction in which the water is flowing.
Note |
---|
Currently, WaterFlow is not rendered if the water is displaced by WaterWaves. |
The water flow can be defined using:
See WaterFlow for more details.
WaterWaves can be used to displace the water surface (using vertex shader displacement mapping).
Using UserDefinedWaves the wave displacement and normal maps can be defined by the user. The displacement and normal maps must be updated by the user each frame. For example, you could run a custom CPU or GPU ripple simulation in small radius around the player position.
OceanWaves use Fast Fourier Transform (FFT) on the GPU with a stastical model of ocean waves to compute complex, periodic waves. You can also query the displacement of a point on the water surface on the CPU. (This requires that a smaller scale FFT is executed on the CPU and should only be enable when needed. See EnableCpuQueries.)
This section explains how to render WaterNodes.
WaterRenderer is a SceneNodeRenderer which handles WaterNodes.
The water is usually rendered after all opaque objects:
// Render opaque objects... // Render water. waterRenderer.Render(waterNodes, context);
The WaterRenderer renders refractions and for this it needs a texture which contains an image of the current scene. It expects this texture in RenderContextSourceTexture. If RenderContextSourceTexture is null, the WaterRenderer automatically resolves the current render target (which must be an off-screen render target), replaces it with a new render target, like this:
if (context.SourceTexture == null && context.RenderTarget != null) { context.SourceTexture = context.RenderTarget; context.RenderTarget = renderTargetPool.Obtain2D(new RenderTargetFormat(context.RenderTarget)); graphicsDevice.SetRenderTarget(context.RenderTarget); graphicsDevice.Viewport = context.Viewport; _rebuildZBufferRenderer.Render(context, context.SourceTexture); }
That means, it is not necessary to set RenderContextSourceTexture manually before calling the WaterRenderer. However, it is sometimes more efficient if RenderContextSourceTexture is set explicitly.
Water is similar to an alpha-blended object. The render order is relevant. Especially, if transparent objects intersect the water surface, some rendering artifacts will be visible.
In many games, alpha-blended objects and the player camera will not enter the water. In this case, water should be rendered before all alpha-blended objects.
If transparent objects and the player camera can enter the water, following render order is better:
The WaterWavesRenderer is a SceneNodeRenderer which handles WaterNodes. If a WaterNode references an OceanWaves instance, the renderer performs the ocean simulation, creates the wave displacement texture and the normal texture and stores the result in the OceanWaves instance.
Currently, the WaterWavesRenderer supports only OceanWaves. It does not support any other wave type derived from WaterWaves.
Wave textures are usually generated at the start of the Draw-method, before the scene is rendered:
// Compute ocean waves. waterWavesRenderer.Render(waterNodes, context); // Render scene...
To summarize: The WaterWavesRenderer creates the wave displacement and normal textures of OceanWaves.