Gamasutra is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.

Gamasutra: The Art & Business of Making Gamesspacer
Refractive Texture Mapping, Part Two
View All     RSS
September 26, 2020
arrowPress Releases
September 26, 2020
Games Press
View All     RSS

If you enjoy reading this site, you might also want to check out these UBM Tech sites:


Refractive Texture Mapping, Part Two

November 17, 2000 Article Start Previous Page 2 of 2


Second Simulation Using Refractive Texture Mapping

The implementation of the simulation with refractive texture mapping is straightforward, but in this case the render function needs to call the refraction function (Listing 4) instead of the reflection function . There is one more detail needs to be observed, the V coordinate calculations are flipped in Listing 4, because the UV conventions that DirectX 7 (and most libraries) uses do not match the way the Z coordinate is used in the interpolation. This is not a big problem, but if the V coordinate is computed exactly as in Equation 13, the texture will look inverted. Figure 14 shows the final mesh rendered with a few perturbations and refractive texture mapping (note the clamping problem on the lower border of the mesh), and Figure 15 shows the texture used.

Figure 14 (top): Refractive texture mapping
Figure 15 (bottom): Texture used in Figure 14

Listing 4. Refractive texture mapping and perturbations

void water_compute_refraction (WATER *water)

static long



//update camera
camera = TransformD3DCamera;

// x_length is equal xe-xi
interpolation_factor_x= 1.0f/(water->initial_parameters.x_length);
xi = water->initial_parameters.x0;

// z length is equal ze-zi
// to make the z convention match with the v coordinates we need to flip it
interpolation_factor_z = -1.0f/(water->initial_parameters.z_length);
ze = water->initial_parameters.z0+water->initial_parameters.z_length;

//loop through the vertex list
vertex_current = (VERTEX_TEXTURE_LIGHT *)water->mesh_info.vertex_list;
for (t0=0; t0mesh_info.vertex_list_count; t0++, vertex_current++)

//get incident ray
camera_ray.x = camera.camera_pos.x - vertex_current->x;
camera_ray.y = camera.camera_pos.y - vertex_current->y;
camera_ray.z = camera.camera_pos.z - vertex_current->z;

//avoid round off errors
math2_normalize_vector (&camera_ray);

vertex_normal.x = vertex_current->nx;
vertex_normal.y = vertex_current->ny;
vertex_normal.z = vertex_current->nz;

//compute the approx refracted ray
refracted_ray.x = -(vertex_normal.x*water->refraction_coeff + camera_ray.x);
refracted_ray.y = -(vertex_normal.y*water->refraction_coeff + camera_ray.y);
refracted_ray.z = -(vertex_normal.z*water->refraction_coeff + camera_ray.z);

math2_normalize_vector (&refracted_ray);

//let's compute the intersection with the planar map
final_depth = water->depth+(vertex_current->y-water->initial_parameters.y0);
t = final_depth/refracted_ray.y;

//figure out the hitting region
map_x = vertex_current->x + refracted_ray.x*t;
map_z = vertex_current->z + refracted_ray.z*t;

new_u = (map_x-xi)*interpolation_factor_x;

//because of variable conventions the z/v is flipped
new_v = (map_z-ze)*interpolation_factor_z;

//clamp if overflow
if (new_u < water->refraction_uv_min) new_u = water->refraction_uv_min;
if (new_u > water->refraction_uv_max) new_u = water->refraction_uv_max;

if (new_v < water->refraction_uv_min) new_v = water->refraction_uv_min;
if (new_v > water->refraction_uv_max) new_v = water->refraction_uv_max;

vertex_current->u = new_u;
vertex_current->v = new_v;


} //end main loop


Some optimizations can be done to the techniques described in this article to speed up the calculations. Nevertheless, I have to admit that water simulation done by these techniques can be intense if the mesh is composed of a lot of polygons, especially because the vertex data changes every frame. Your best bet is not to use too many polygons, but I'll go over some of the bottlenecks here.

In the mapping techniques, most of the calculations rely on vector normalization. I did not address this problem too much because most console platforms perform vector normalization in hardware, and on PCs the new Pentiums can handle square roots reasonably well.

Perhaps where a lot of experimentation can be done is in the perturbations. As I mentioned earlier, instead of use the ripple equation, you can try to replace this part by the array algorithm (see References) or a spring model. However; it's important to know that these algorithms do not use sine waves and therefore the ripples may lose their round shape (in side view). I haven't tried this yet, but it will definitely speed up the code. If you plan to use the ripple equation anyway, another thing that can be done is to create a big square root lookup table. That is, if the ripples are always generated certain positions, the radius

can be precomputed and stored in an array. Even though the values under the square root are floats, the mesh is composed of discrete points. Using this fact, you can use the vertices' indices to index the square root array.

Another big bottleneck in the code is the normal calculation. Here more than anywhere else I welcome reader suggestions, because I could not find a very efficient way of computing the vertex normals for a mesh that's constantly being morphed. They way I've implemented it is to create lookup tables of neighboring triangles. When I loop through the mesh vertices I can quickly look up its neighboring faces and average these face normals. However, this is still slow, and even console hardware won't be able to help me much, except for the vector normalization.

Sample Program

When you look at my implementation, you'll notice that the code is slightly different from the code presented in this article. There is a lot of debugging code, editor interface variables, as well as some experiments I was doing. In this article, I excluded this part for sake of clarity. Also, you won't be able to download the whole application source files to compile in your computer. I just put the water part for you to look at as a reference. The main reason for this is because the water implementation is part of an editor I've been working on for the past two years or so. The whole editor has thousands of lines of code, and it performs several different algorithms, experiments, and so on. So, I decided to not make the entire editor public. If you run the editor, you can control several parameters of the mesh, mapping techniques, and perturbations. You can play around with those parameters to see how they affect the final simulation. Also, you can fly with the camera by using arrows, A, Z, Q, W, and Shift and Control plus these keys will make the camera move slower or strafe.

Final Words

I hope this information has helped you to understand and implement some interesting techniques for simple water simulation using refractive texture mapping. Please e-mail me at [email protected] or [email protected] with suggestions, questions, or comments.

To download the code and run the sample application, go to my web site at, and you should find everything you need under "Downloads."


Möller, Tomas, and Eric Haines. Real-Time Rendering. A.K. Peteres, 1999. pp 131-133.

Direct X 7 SDK Help (Microsoft Corp.)

Serway, R. Physics for Scientists and Engineers with Modern Phyisics, 4th ed. HBJ, 1996. pp. 1023-1036.

Ts'o, Pauline, and Brian Barsky. "Modeling and Rendering Waves: Wave-Tracing Using Beta-Splines and Reflective and Refractive Texture Mapping." SIGGRAPH 1987. pp. 191-214.

Watt, Alan, and Fabio Policarpo. The Computer Image. Addison-Wesley, 1997. pp. 447-448.

Watt, Alan, and Mark Watt. Advanced Animation and Rendering Techniques: Theory and Practice. Addison-Wesley, 1992. pp. 189-190

Harris, John W., and Horst Stocker. Handbook of Mathematics and Computational Science. Springer Verlag, 1998.

PlayStation Technical Reference Release 2.2, August 1998 (Sony Computer Entertainment)

Elias, Hugo.

Lander, Jeff. "A Clean Start: Washing Away the New Millennium." Game Developer (December 1999) pp. 23-28.

Haberman, Richard. Elementary Applied Partial Differential Equations, 3rd ed. Prentice Hall, 1997. pp. 130-150.

Graff, Karl F. Wave Motion In Elastic Solids. Dover Publications, 1991. pp. 213-272.

Article Start Previous Page 2 of 2

Related Jobs

Deep Silver Volition
Deep Silver Volition — Champaign, Illinois, United States

Senior Engine Programmer
Deep Silver Volition
Deep Silver Volition — Champaign, Illinois, United States

Senior Technical Designer
Random42 — London, England, United Kingdom

UE4 Technical Artist
Evil Empire
Evil Empire — Bordeaux, France

Senior Technical Developer

Loading Comments

loader image