# Water

## Realistic water with POV-Ray - surface geometry

This part describes different methods to create realistic water surface geometry, for the other parts of the tutorial go to the tutorial starting page.

Although we are not dealing with moving water here, water surfaces are seldom completely flat. Even if you pour a glass of water and put it on a table, the free surface is curved to some extend, wind and other disturbances lead to much more complicated shapes. This chapter first describes several ways to model such irregularities in general and then will also show different methods to create realistic looking water waves

### modelling water geometry in general

If we have a large mainly flat water surface like a lake or ocean it is usually sufficient to approximate the geometry with a totally plane object and only perturb the surface normal. This is done in all the previous samples and it works fairly well since the appearance of the surface including reflection and refraction is mainly steered by the surface normal and the absolute position of the surface has only minor importance.

The best shape to choose for the water depends on the situation. In most cases a plane, box or cylinder is the best choice.

The normal statement is part of the texture, the patterns that can be used will be explained later on.

normal {
bozo [Value]
scale 0.15
}

bozo 0.3 bozo 0.8 bozo 1.5

You can see that the water surface itself looks fairly good, but where the water surface intersects with the rim we get a straight line since the geometry is flat.

If we want to actually model the geometry of the waves, POV-Ray offers a very elegant but slow method to do this: the isosurface object.

Isosurfaces are an extremely versatile concept. For detailed information on the diverse possibilities see the official POV-Ray documentation. I just want to mention that although they are not really difficult to handle in fairly simple situations like this it is important to know about the different parameters to be able to get good looking and fast results.

We will use a pattern like in the normal statement to displace a planar surface in vertical direction:

#declare fn_pattern=
function {
pattern { bozo scale 0.15 }
}

#declare fn_water=
function {
z - fn_pattern(xy0)*Value
}

Value marks where you can change the height of the waves.

The actual isosurface definition requires a container object. This means that the water surface can't be infinitely large, but you can have arbitrarily sized containers, just it will possibly become very slow.

isosurface {
function { fn_water(xyz}
contained_by { box { ... } }
}

For the function you have to follow the same rule like for isosurface functions in general: it has to be continuous. In most cases you will have to adapt the accuracy and max_gradient values for optimal results.

The material used of course no more contains a normal statement.

Value=0.02 Value=0.04 Value=0.08

As you can see the water surface itself looks very much like with the normal statement, but the interaction with the walls is much more realistic.

The speed of this solution much depends on the function used, in this case it is still reasonably fast since the pattern used is fairly simple, but this can change with more complicated functions.

A different approach is to precalculate the surface geometry in the classical form of computer graphics: a triangle mesh. In most situations the best method to do this in POV-Ray is the heightfield object.

A heightfield can take a separate image file or an internally generated matrix to define the positions of the triangles which are placed in a rectangular grid. We use a pattern function like in the isosurface version.

height_field {
function 400400 { fn_pattern(xy0)*Value + 0.5 }
smooth
}

Value=0.025 Value=0.05 Value=0.1

A heightfield is always 1 unit large so you have to scale and translate it as appropriate. The 400, 400 specify the resolution of the mesh.

You can imagine that the mesh data requires quite a lot of memory and this is also the main disadvantage of this method. A large water surface reaching from foreground to horizon would require enormous amounts of memory. A possible solution would be not to use a heightfield and make a mesh of variable resolution.

### realistic water structures

In the previous general samples i used a fairly simple pattern as an example. In reality the possible structures differ quite a lot. Sometimes we have very shallow parallel waves, sometimes very turbulent and splashy structures. Important factors that influence the style of the waves are - apart from the wind - the water depth, the rims and moving objects in the water like ships etc.

In the finish and interior samples i used a ridged multifractal pattern. This is well suited for quite strong ripples that are just before starting to splash. This pattern is not directional but linear structures can be obtained by overlaying parallel waves:

#include functions.inc

normal {
function {
f_ridged_mf(xyz0.13.070.70.72)
+ sin(x*2.5)*0.4
} 0.8
scale 0.13
}

or asymmetric scaling:

#include functions.inc

normal {
function {
f_ridged_mf(xyz0.13.070.70.72)
} 0.8
scale <0.130.40.13>
}

plain overlaid sine asymmetric scaling

There are quite a lot of POV-Ray Patterns that are suited for water structures in some situation - either on it's own or in combination with others.

Here are some example of basic patterns:

bumps 0.3 scale 0.1 wrinkles 0.2 scale 0.2 granite 0.15 scale 1.2
waves 0.3 scale 0.1 ripples 0.4 scale 0.4 leopard 0.6 scale 0.04

Finally you can of course also try an accurate physical simulation. With taking into account all possible influences including interaction with the environment this can get extremely complicated. See the links section for some approaches in that direction.

If we restrict the situation to deep ocean without shore a simplified model is possible. It basically superposes a lot of sine waves with different frequencies and directions to a wave pattern. Distribution of frequencies and directions depends on the wind speed.

The description of a practicable model for those distributions can be found in a paper on visualizing water surfaces (German).

I wrote an implementation in POV, it generates a function that can be used in normals, heightfields and isosurfaces. The following samples show the results with different wind speed settings.

function {
Waves(pi/2, WindSpeed, 0650)
}

WindSpeed=4.1 WindSpeed=4.4 WindSpeed=4.6

Since the function generated by the Waves() macro is fairly long and slow, this method is less suited for isosurfaces.