Good water simulation should have at least the following features:

- True reflection (with correct parallax)
- Clipping of underwater objects on reflection image
- View angle dependent transparency/reflectivity of water
- Ripples and/or waves
- Water scattering (i.e. water becoming gradually opaque as depth increases)

- Refraction
- Caustics - i.e. light spots at the bottom of shallow water
- Reflected light - i.e. light spots reflected to objects near water

Here I will describe the mathematics behind the scenes and give step-by-step guide to writing your own water system/object/rendering pass.

1.Rendering reflection texture

Water without reflection looks totally uninteresting - just like any other semitransparent surface. Thus we start from implementing reflection and later go on to other effects.

**1.1. Parallax**

Even if you have until now managed to render you scene in single pass, from this point on you need at least two passes (actually at least

**N+1**, where

**N**is the number of visible reflective surfaces).

The reason is, that unfortunately we cannot recycle our main scene image for reflections. First because it could make view frustum insanely large (for example - if viewing the water surface from high angle we see only ground and water in our main view, but mostly sky in reflection). And second because of parallax. The reflection is unfortunately not the perfect copy of reflected scene, but copy of the view of the same scene from different viewpoint. The following image illustrates this.

A diagram explaining the parallax effect on reflected image |

It means that you need to have rendering to texture set up and working. We will render reflection to texture and later use this texture while rendering the water surface in main scene.

Thus, to get reflection texture we first have to render our scene from the reflected camera viewpoint

**P'**to texture. First we have to find the reflected camera position - or more precisely the reflected view matrix (because we need camera orientation too in addition to the position).

This can be done with the following formula:

**M'camera = Mreflection * Mcamera**Where

*is the reflection matrix of mirror surface. It can trivially be calculated from the position of reflection plane:*

**Mreflection**| 1-2Nx2 -2NxNy -2NxNz -2NxD | Mreflection = | -2NxNy 1-2Ny2 -2NyNz -2NyD | | -2NxNz -2NyNz 1-2Nz2 -2NzD | | 0 0 0 1 |

Where (

*) are the coefficients of plane equation (*

**Nx,Ny,Nz,D***). Notice, that (*

**xNx + yNy + zNz + D = 0***) is also the normal vector of given plane.*

**Nx,Ny,Nz***is the transformation of camera as if it would be "normal" object in scene. To get ModelView matrix you will need the inverse of it*

**Mcamera**

**.****1.2. Mirrored geometry**

Actually we cheated a little in the previous image. We rotated the mirrored image 180ยบ to make it more similar to the original image, so the effect of parallax can be seen. The actual mirrored image looks like this:

Different winding order on mirrored image |

This may or may not be problem for you. If all your materials are double sided (i.e. you do not do back face culling) or if you can set up rendering pipeline in such a way, that you can change culling direction it is OK. In my case though, I prefer to keep culling always on and have forward-facing always defined as CCW. So something has to be done with the reflected image - or otherwise geometry will not render properly.

We will exploit the feature that camera is always (at least in most applications) rectangular and centered around view direction. Thus we can just flip camera in

**Y**direction and the winding order will be correct again (it flips reflected image so it looks like (3) on the first picture).

This can be done with one more reflection matrix:

**M''camera = Mreflection * Mcamera**

*** Mflip**Where

*is simply another reflection matrix that does reflection over*

**Mflip****XZ**plane.

Now if we render mirrored image using

*as camera matrix, pipeline can be left intact. We, of course, have to save this matrix for later reference, because it is needed to properly map our texture to water object in main render stage.*

**M''camera****1.3. Underwater clipping**

Take a look at the following picture:

A reflection with underwater object |

We have added an underwater object

**Q**to our scene. Now it should not appear on reflection, because it does not block the actual reflection rays

**PB'B**and

**PA'A**. But we are not doing ray-tracing. We are instead moving camera to mirrored viewpoint

**P'**and rendering reflection like normal image. But as you can see, the object

**Q**blocks ray

**P'A'A**and thus would show up in our reflection.

Thus we have to make sure, that nothing that is under the reflection plane (water surface) will show up in mirror rendering. This can be achieved in three different ways:

- Use additional clipping plane on GPU. It can be very fast or very slow - depending on card and driver used.
- Use oblique projection matrix during reflection rendering. You can read more about it here. This is cool technique, but personally I have never got it to work well enough because it messes up camera far plane.
- Clip manually in pixel shaders. It wastes some GPU cycles, but is otherwise easy and foolproof.

uniform vec4 clip_plane; varying vec3 interpolatedVertexEye; void main() { float clipPos = dot (interpolatedVertexEye, clip_plane.xyz) + clip_plane.w; if (clipPos < 0.0) { discard; } ... }

Of course you have to supply your shader with

**clip_plane**and calculate

**interpolatedVertexEye**in vertex shader (it is simply vertex coordinate in view/eye space:

**VertexEye = Mmodelview * Vertex**). If you do not need clipping, simply set

**clip_plane**normal (

**xyz**) to zero and all pixels will be rendered.

**1.4. Putting it all together**

Before starting the main render pass (being it forward or deferred) do the following:

- Create list of all objects that need reflections (and the parameters of all reflection planes). Then for each reflection plane:
- Calculate the reflected camera matrix

M''camera = Mreflection * Mcamera*** Mflip** - Set up camera matrices (you can optimize rendering by using clipped projection matrix, but this will not be discussed here).
- Set clipping plane to reflection plane
- Render full scene
- Save the rendered image as texture to be used with reflective object

This is actually quite easy - provided that you have at hand all necessary parameters. You have still to decide at which render stage to do this. I use transparent stage, as water is basically just one semi-transparent surface in scene, but you can add another pass before or after transparency as well.

You will need at hand:

- Reflected camera matrix
**M''camera** - Projection matrix you used to render reflection
(normally this is the same projection that you use for main camera)*Mprojectionreflection* - Reflection texture

**2.1. Vertex shader**

attribute vec3 vertex; uniform mat4 o2v_projection; varying vec3 interpolatedVertexObject; void main() { gl_Position = o2v_projection * vec4(vertex.xy, 0.0, 1.0); interpolatedVertexObject = vertex; }

We add another constraint here - water surface will be at

**XY**plane of the object local coordinate system. It is strictly not necessary if you have the proper reflection plane, but I found it easier that way. Just use

**XY**plane as reflection plane and place your object (water body) appropriately.

Actually this allows us to do another cool trick. We can use the bottom of water body (i.e. river, lake..) as our water object. It will be flattened in shader, but we can use the

**Z**data to determine the depth of water at given point. But more about this in next part.

**o2v_projection**is simply my name for composite matrix

**Projection * ModelView**. I prefer to name matrices with mnemonic names, describing the coordinate system transformations they do - in given case it is

**Object To View**, multiplied with

**Projection**.

**interpolatedVertexObject**is simply vertex coordinate in object local coordinate system - we will need it to do lookup onto reflection texture.

**2.2. Fragment shader**

uniform mat4 o2v_projection_reflection; uniform sampler2D reflection_sampler; varying vec3 interpolatedVertexObject; void main() { vec4 vClipReflection = o2v_projection_reflection * vec4(interpolatedVertexObject.xy, 0.0 , 1.0); vec2 vDeviceReflection = vClipReflection.st / vClipReflection.q; vec2 vTextureReflection = vec2(0.5, 0.5) + 0.5 * vDeviceReflection; vec4 reflectionTextureColor = texture2D (reflection_sampler, vTextureReflection); // Framebuffer reflection can have alpha > 1 reflectionTextureColor.a = 1.0; gl_FragColor = reflectionTextureColor; }

**o2v_projection_reflection**is the composite matrix

**Projection * ModelView**as it was used during reflection rendering. I.e:

*Mprojectionreflection **

**(M''camera)-1 * Mobject**Like the name implies, it transforms from the object coordinate system to the clip coordinate system of reflection camera.

In fragment shader we simply repeat the full transform pipeline during reflection rendering and use final 2D coordinates for texture lookup. For this we need initial, untransformed object vertices - thus they are interpolated from vertex shader (

**interpolatedVertexObject**).

I'll set reflection

**alpha**to

**1.0**because I use HDR buffers and due to additive blending the final alpha can have some very weird values there.

And the rendered image:

Simple scene from Shinya showing water as perfect mirror |

Not very realistic?

Up to now we have implemented water as perfect mirror. This is very far from reality (look at the feature list in the first section).

In the next parts I will show how to add viewing angle based transparency, water color and depth-dependent ripples to your water.

Have fun!

Hello, thanks a lot for this tutorial ! It was clear and it's the most up-to-date I could find :)

ReplyDeleteHowever I've got a remark about this part for the reflected matrix :

"M'camera = Mreflection * Mcamera"

Using the GLM lib to handle the matrices, I got more luck using :

"mat4 reflectedView = camera->viewMatrix * reflectionMat;"

instead. Also, what about this line :

"To get ModelView matrix you will need the inverse of it." ? I don't think any inverse was needed.

Anyway I got my reflection working and I am heading to part ii :)

A big thank you from me as well. This explanation is the most comprehensive I found yet and it's helping me a lot on my own water rendering project.

ReplyDeleteWhile true reflections can only be done with ray-tracing, one can achieve surprisingly nice approximations by using quite simple scene setup and some GPU programming. Plumbing Judge

ReplyDeletepackers and movers lucknow to Kanpur cost

ReplyDeletepackers and movers Chennai to hydrabad cost

packers and movers Noida to lucknow cost

packers and movers Ghaziabad to Raipur cost

packers and movers indore to delhi cost

packers and movers Gurgaon to Kanpur cost

packers and movers Kolkata to patna cost

packers and movers Mumbai to Nagpur cost

packers and movers Pune to Bangalore cost

packers and movers Hyderabad to Chennai cost

packers and movers Bangalore to Hyderabad cost

packers and movers Faridabad to navi Mumbai cost

packers and movers Delhi to pune cost

movers and packers Kolkata to Bangalore cost

ReplyDeletemovers and packers delhi to patna cost

movers and packers Mumbai to pune cost

movers and packers pune to Nagpur cost

movers and packers Faridabad to agar cost

movers and packers indore to Bhopal cost

movers and packers chandigarh to Mohali cost

movers and packers Chennai to trichy cost

movers and packers Bangalore to mangalore cost

packers and movers lucknow to Varanasi cost

movers and packers Noida to Allahabad cost

movers and packers Ghaziabad to Bhubaneswar cost

movers and packers Gurgaon to chandigarh cost

movers and packers movers thane to Gurgaon cost

movers and packers navi Mumbai to Allahabad cost

movers and packers Hyderabad to navi Mumbai cost

movers and packers Bhubaneswar to Raipur cost

movers and packers Raipur to patna cost

movers and packers patna to lucknow cost

movers and packers Ahmedabad to surat cost

ReplyDeletemovers and packers surat to Ahmedabad cost

movers and packers rajkot to vadodara cost

movers and packers Vadodara to Rajkot cost

movers and packers Lucknow to delhi cost

movers and packers chandigarh to Amritsar cost

movers and packers Amritsar to chandigarh cost

movers and packers Ludhiana to panchkula cost

movers and packers panchkula to Ludhiana cost

movers and packers mohali to Jalandhar cost

movers and packers Jalandhar to Mohali cost

movers and packers varanasi to agra cost

movers and packers allahabad to lucknow cost

movers and packers Kanpur to Bhopal cost

movers and packers agra to Kanpur cost

packers and movers in Bangalore to Kanpur cost

packers and movers chandigarh cost

packers and movers lucknow cost

packers and movers Vadodara cost

packers and movers Ahmedabad cost

packers and movers surat cost

packers and movers Rajkot cost

car transport in chandigarh to bangalore

ReplyDeletecar transport in lucknow to delhi

car transport in Kolkata to mumbai

car transport in Hyderabad to lucknow

car transport in Bangalore to raipur

car transport in pune to chennai

car transport in Mumbai to kanpur

car transport in Chennai to patna

car transport in delhi to mumbai

car transport in Gurgaon to hyderabad

car transport in Noida to agar

car transport in Faridabad to pune

car transport in Ahmedabad to bangalore

car transport in navi Mumbai to kolkata

car transport in thane to pune

car transport in jammu to surat

car transport in surat to vadodara

car transport in Vadodara to nellore

car transport in Nellore to panchkula

car transport in panchkula to jammu

car transport in amritsar to bhavnagar

car transport in Bhavnagar to nagpur

car transport in rajkot to mohali

car transport in mohali to nagpur

car transport in nagpur to bhopal

car transport in nashik to bhopal

car transport in Kalyan dombivali to kanpur

car transport in vasai virar to kanpur

car transport in bhopal to kanpur

ReplyDeletecar transport in indore to bhavnagar

car transport in kanpur to rajkot

car transport in varanasi to amritsar

car transport in allahabad to coimbatore

car transport in Coimbatore to trivandrum

car transport in Trivandrum to nashik

car transport in cochin to mysore

car transport in mysore to kolkata

car transport in kochi to visakhapatnam

car transport in visakhapatnam to guwahati

car transport in vijayawada to jaipur

car transport in trichy to guwahati

car transport in goa to jaipur

car transport in jaipur to patna

car transport in patna to guwahati

car transport in ranchi to patna

car transport in raipur to jalandhar

car transport in guwahati to jalandhar

car transport in jalandhar to jamshedpur

car transport in jamshedpur to dehradun

car transport in dehradun to jamnagar

car transport in jamnagar to shimla

car transport in gandhinagar to ludhiana

car transport in ludhiana to kolkata

car transport in shimla to raipur

car transport in bhubaneswar to trichy