This is an old revision of the document!


Water

A water surface is not seldomly a popular game component. We do not provide directly a water surface component, but it is quite easy to setup one.

Water rendering is based on:

  • a plane
  • refraction computation
  • reflection computation
  • and the water shader itself

Components

Plane

The plane is an opaque instance of TGorillaPlane or TPlane. It will be used as basis mesh to render water onto.

Refraction

Refraction is the image data simulating underwater 3D objects modified by some visual effects (Fresnel Effect). We'll need a separated RenderPass for this, to pre-compute the information. That's the reason why there is no all-in-one component for water, because we want users to reuse their renderpasses instead of instanciating multiple renderpasses for the same image data. This would lead to horrible performance issues. This means you could use the refraction renderpass for water and at the same time f.e. for some transparency effect.

Create an instance of TGorillaRenderPassRefraction and apply the viewport and camera to it.

Because water surface getting rendered in main pass, we should ignore it while refraction computation. This can be done very easily, by:

FRefraction.IgnoreControl(FWaterPlane);

Reflection

Reflection is the image data containing a mirrored view on the scene depending on the current camera position and direction. As the name already says, it will be displayed as 3D object reflection on the water surface.

Create an instance of TGorillaRenderPassReflection and apply the viewport and camera to it.

And again we'll need to ignore the water plane while computation, by:

FReflection.IgnoreControl(FWaterPlane);

Because the reflection pass always computes the mirrored camera view, we should tell him, how the virtual mirror surface looks like:

  // for plane vector computation we need to know the size of this mirror plane
  FReflection.MirrorSize := FWaterPlane.Width;
 
  // set the current position of the water plane as mirror plane
  // this needs to be updated, if water plane moves
  FReflection.MirrorPosition := TPoint3D(FWaterPlane.AbsolutePosition);

In case plane size or position changes, you have to update those values in reflection pass.

Water Material

Finally of course we need a material shader (TGorillaWaterMaterialSource), which is able to merge all components together.

For computing waves, riffles and foam the shader needs a few textures:

  • NormalTexture
  • DUDVTexture
  • DisplacementTexture
  • SpeculareTexture
  • FoamTexture

The different texture may look like these:

Water normals map Water DUDV map Water displacement map Water specular map Water foam texture

Attention: The image source is unknown! It is not advisable to use these images and, if necessary, to violate the license agreement.

After loading all relevant textures to material source, we only have to apply refraction and reflection to it:

  FWaterMaterial.ReflectionPass := FReflection;
  FWaterMaterial.RefractionPass := FRefraction;

Remarks: normal map texture will also be used as displacement map for simulating real mesh waves, in case no explicit displacement map was applied to the material.

In the end do not forget to link water plane with water material source.

Example

Take a look at this example code for a complex water surface setup.

procedure TForm1.CreateWater(const AAssetsPath : String);
var LTexPath : String;
begin
  LTexPath := IncludeTrailingPathDelimiter(AAssetsPath + 'water');
 
  /// create the water plane
  FWaterPlane := TGorillaPlane.Create(FViewport);
  FWaterPlane.Name := 'WaterPlane1';
  FWaterPlane.BeginUpdate();
  try
    FWaterPlane.Parent := FViewport;
    FWaterPlane.HitTest := false;
    FWaterPlane.RotationAngle.X := -90;
    FWaterPlane.Width  := MAP_SIZE;
    FWaterPlane.Height := MAP_SIZE;
    FWaterPlane.Depth  := 1;
    FWaterPlane.SubdivisionsHeight := 64;
    FWaterPlane.SubdivisionsWidth  := 64;
    FWaterPlane.Position.Y := -25;
  finally
    FWaterPlane.EndUpdate();
  end;
 
  /// create reflection render pass for water material
  FReflection := TGorillaRenderPassReflection.Create(FViewport);
  FReflection.Viewport := FViewport;
  FReflection.Camera := FCamera;
  FReflection.IgnoreControl(FWaterPlane);
  FReflection.MirrorSize := FWaterPlane.Width;
 
  // set the current position of the water plane as mirror plane
  // this needs to be updated, if water plane moves
  FReflection.MirrorPosition := TPoint3D(FWaterPlane.AbsolutePosition);
  FReflection.Enabled := true;
 
  /// create refraction render pass for water material
  FRefraction := TGorillaRenderPassRefraction.Create(FViewport);
  FRefraction.Viewport := FViewport;
  FRefraction.IgnoreControl(FWaterPlane);
  FRefraction.Enabled := true;
 
  /// create material source
  FWaterMaterial := TGorillaWaterMaterialSource.Create(FWaterPlane);
  FWaterMaterial.Parent := FWaterPlane;
  FWaterMaterial.NormalTexture.LoadFromFile(LTexPath + 'water_normal.png');
  FWaterMaterial.DUDVTexture.LoadFromFile(LTexPath + 'water3-dudv.jpg');
  FWaterMaterial.DisplacementTexture.LoadFromFile(LTexPath + 'water_height.png');
  FWaterMaterial.SpecularTexture.LoadFromFile(LTexPath + 'water_height.png');
  FWaterMaterial.FoamTexture.LoadFromFile(LTexPath + 'foam.png');
 
  /// link reflection and refraction render pass to water material
  FWaterMaterial.ReflectionPass := FReflection;
  FWaterMaterial.RefractionPass := FRefraction;
 
  FWaterPlane.MaterialSource := FWaterMaterial;
end;