Realistic Natural Effect Rendering: Water I
Clipping planesWhen creating the reflection texture as described in the first step of the planar mirror algorithm, it was assumed that all reflected objects are initially above the water surface. This rarely is the case under real conditions, except perhaps for simple test scenes. There will almost always be geometry and objects below the water surface, often they will even intersect it. When reflecting the environment around the water plane, geometry that was below the surface will suddenly end up above it. These parts need to be clipped away, otherwise visual artifacts in the form of ghost reflections can appear. Many approaches to geometric clipping exist, and all of them will achieve the desired results. But depending on the choice of target hardware and the complexity of the reflected geometry, some may be better suited than others, especially from performance considerations and feature requirements. Discussing the details of geometric clipping would be out of scope for this article, but a short overview over the most common techniques and their benefits and drawbacks when combined with water reflections is shown below: User clip plane:
User clip planes are provided as a built-in feature of many 3D APIs. They are easy to use and portable. Unfortunately, hardware support varies, and user clip planes are not guaranteed to be hardware accelerated on many current 3D chipsets. Information about user clip planes can be found in the specifications of the selected target API.
Alpha testing: Culling away fragments using the alpha test has long been the method of choice for user plane clipping. The signed distance from a vertex to the water plane is computed and passed as 1D texture coordinate into a clamped alpha texture, after appropriate remapping. The result of the texture lookup is passed on to the alpha test, which culls away all fragments on the wrong side of the plane. This technique is hardware accelerated even on older 3D chipsets. But it burns up a texture stage, and uses the output alpha, which is often unacceptable.
Pixel shader based culling: As with alpha test culling, the signed distance from the vertex to the plane is computed, and passed to a pixel shader. The shader culls away all fragments with a negative (or positive) distance using a function such as texkill. Pixel shader based culling is efficient, but requires shader capable hardware. Also, the culling functionality has to be explicitly added to every single shader that might be used by reflective geometry.
Oblique frustum clipping: A potentially optimal culling technique developed by Eric Lengyel. It maps the standard frustum near plane onto an arbitrary user defined clipping plane (the water surface in this case) by modifying the camera projection matrix. The technique is hardware accelerated by all 3D chipsets, since it operates on functionality inherently available on all 3D architectures (standard view frustum clipping). Oblique frustum culling doesn't require any additional GPU resources. The drawback is a reduced z-buffer precision on the reflected render pass. More information about the technqiue can be found under [5].
Disturbing the reflectionUntil now, the reflections are completely flat and clear, a direct consequence of the planar water surface approximation. But the water surface is often not flat at all. In fact the wave model might have given the renderer a very choppy surface, with many interfering waves and turbulences. Fortunately, the projective texture pass allows full control over the way the reflection texture is applied to the water: by modifying the projective coordinates, distortions are introduced. The perfect mirror generated by undisturbed projective coordinates assumes all water grid normals to point straight upward, parallel to the normal of the idealized flat water surface. This assumption is correct, as long as the water surface stands perfectly still, without the slightest fluctuation. As soon as the surface starts to move, the water grid normals deviate from their ideal direction. Since the mesh generated from the water heightfield cannot represent slopes larger than 90°, the normals are also bound to this limit. In the worst-case scenario, a grid normal will be parallel to the water surface, lying on the XY plane. Along with the grid position, the grid vertex normal deviation can serve as a measure of how much directional distortion is required within the reflection. Many different mathematical approaches can be used to derive the precise distortion vectors from this information. As so often in computer graphics, choosing an appropriate technique is again a tradeoff between accuracy and speed. While not physically accurate, a simple 2D displacement along the normal can approximate the amount and direction of the reflective distortion. This technique is very efficient, and generates good-looking results on a typical water surface. Alternative and more precise methods will be discussed later in this series. In order to compute the distortion, the vertex grid positions are displaced along their respective normal, just before being fed into the projective texture matrix Mprojtex. The displacement is only carried out on the [x, y] components of the point, while the z component is kept unmodified. This displacement will adjust the generated projective texture coordinates, shifting them into the appropriate direction proportionally to the deviation of the vertex normals. No displacement takes place on an undisturbed upwards-pointing normal. The equation used is as follows: P.x += d * N.x where P is the vertex position, N the vertex normal, and d is a user adjustable factor controlling the strength of the distortions. This factor has no specific physical meaning, the optimal value depends on the geometric scale, the wave model, and personal preference. Various values for d should be tried out, until the visual result is found to be satisfactory. The distortion pass can be conveniently inserted into the projective vertex shader, displacing the vertex coordinates just before they are multiplied with the projective texture matrix. The modified part of the vertex shader is given in source 3:
float d = 4.0; // the adjustable displacement factor
float4 dPos; // temporary variable to hold the displaced vertex position
dPos.xy = inPos.xy + d * inNormal.xy; // displace the xy components of the vertex position
dPos.z = inPos.z; // the original z component is kept
dPos.w = 1.0; // the w component is always one for a point
outTexProj = mul(Mprojtex, dPos); // transform the displaced vertex position by
// the projectivetexture matrix and copy the
// result into homogeneoustexture coordinate set 0
Source 3: projective reflection coordinates displacement
|
|