OCEAN MODULE v1.0
In General
Four adjacent squares of waves. Click here for a display that also shows fps and displacement and normal maps.

The simplest way to create an single ocean wave is to use a variation of the sine formula. However, creating a realistic display requires a lot of waves. The iFFT (or Tessendorf) wave generator uses an inverse of the FFT (Fast Fourier Transform) method to quickly generate a lot of waves.

Unfortunately, we were unable to find a current example of a three.js iFFT wave generator. The best example we found was a three.js wave generator created in 2015 by Jérémy Bouny, based on a 2014 js version created by David Li which was adapted to three.js by Aleksandr Albert.

We have updated and revised that wave generator to create this module. We combined all of the programs and shaders into a single program. We eliminated the portion of the program and the shaders used to create the final display. Instead, the program simply creates displacement and normal maps which you can use with standard three.js materials. We eliminated the program dealing with reflections since you can create reflections using three.js materials.

  Waves from the movie "Dunkirk" compared to waves generated by this module.
Using this Module in Your Program
Load Module into Program

You can insert these kinds of instructions at the beginning of your program to load the module:

   <script type="importmap">
      {
         "imports": {
            "three": "../3js/r154/build/three.module.js",
            "three/addons/": "../3js/r154/examples/jsm/"
         }
      }
   </script>

   <script type="module">
      import * as THREE from "three";
      import {Ocean} from "three/addons/air/Oceans.js";
      [etc]

The "3js/r154 directory" is the place on our webpage where we store the three.js programs for revision 154. To insure that the three.js programs load properly, we use the same directory structure that three.js uses with their examples. The "build" directory contains the standard three.module.js program. The "examples/jsm" directory contains various standard three.js modules. Within "examples/jsm" we created a custom directory "air" which holds Ocean.js and our other custom modules.

Variables

The Ocean module uses the following variable to store data.

   let ocean = 0;    // Address of module function
   let wav_ = {
      // Inputs
      Res: 512,      // Resolution - segments per square (default = 512)
      Siz: 1609,     // Size of Square (meters) 1609 meters = 1 mile
      WSp: 20,       // Wind Speed
      WHd: 0,        // Wind Heading
      Chp: Choppy,   // Choppiness of waves - default = 1
      DTm: 0.0,      // Delta time (program must provide this)
      // Results
      Dsp: 0,        // The Displacement Map
      Nrm: 0,        // The Normal Map
      // Other Variables
      Spd: 1.0,      // Animation speed - use in main program (bigger is faster)
   };

You can use whatever name you like for ocean and wav_, as long as you correctly reference the variables when calling Ocean.

Initialization

The Ocean module is initialized using this command:

    ocean = new Ocean(renderer, camera, scene, wav_);

where renderer, camera and scene are the names of the variables for your renderer, camera and scene.

When you later run ocean.render, the resulting displacement and normal maps will be saved in wav_.Dsp and wav_.Nrm. You can link these to your material, using something like this:

   material = new THREE.MeshPhysicalMaterial({
      color: 0x102080,	// Water color
      normalMap: wav_.Nrm,	// Normal Map
      displacementMap: wav_.Dsp,	// Displacement Map
      envMap: envMap,	// (Optional)
      normalScale: new THREE.Vector2(2.5,2.5),
      metalness: 1.0,		// 1 for max reflection
      roughness: 0.7,		// 0 for max reflection
      reflectivity: 0.5,	// 1 for max reflection
      envMapIntensity: 5,	// max reflection suggested = 5
      premultipliedAlpha: true,
   });	

You can use any three.js material that accepts displacement and normal maps.

If, like us, you display your waves using a field of adjacent grids, you can create a second texture for the more distant grids that includes only the normal map. You will want to displace those outer grids downard enough to avoid apparent gaps. Here is a program that uses that method. As you can see, the ocean surface appears seamless.

Animating the Waves

To animate the waves you need to update Ocean module within your render loop, using this command:

   if (ocean) ocean.render(wav_);

As part of your render loop, you will have to compute the difference in time since your last animation loop. You can then load that difference into wav_ using this command:

   wav_.DTm = difTim * wav_.Spd || 0.0;
A Simple Timer

Here is a simple timer you can use to update wav_.DTm.

Initialize the clock in the variables section with this command and general variables:

   let clock = new THREE.Clock();
   let oldTim, nowTim, difTim = 0;

In your render loop, insert the following:

   nowTim = clock.getElapsedTime();
   difTim = nowTim-oldTim;
   oldTim = nowTim;
   wav_.DTm = difTim * wav_.Spd || 0.0;
Limitations

The good news about sine wave generators is that you can tile the results seamlessly. The bad news is that this tiling starts looking bad as tile size decreases (or as altitude increases). Thus, this wave generator will generate great results, but only within a certain range.

The standard method of solving this problem is to create "cascading" maps. Since the iFFT method creates waves using a specified range of frequencies, you can easily split the wave frequencies into higher and lower frequencies. You then use only the higher (shorter) frequencies to create the standard displacement and normal maps. And you use the lower (longer) frequency waves to create larger displacement and normal maps. And then you combine the two. We have not made this improvement to the module.

A final limitation is that, although Oceans.js generates a 3-way displacement map, Three.js apparently can only handle vertical displacements. This limitation may be a problem only in certain conditions, such as those involving close-up views with high windspeed and choppiness settings. You should be able to determine the impact by comparing the your results to the waves in David Li's model, which uses 3-way displacement.

The Zip File

Here is a zip file that includes the following:
* Oceans.js - the Ocean module.
* iFFT_LAB_mod.html - a sample program that externally references the Ocean module.
* iFFT_LAB_local.html - a sample program that includes the Ocean module that you can run locally.