OCEAN (v2.0), GRDMAP AND GRDWTR MODULES
In General
Click here for a full-screen display that also shows fps.

You can use either the GrdWtr or GrdMap module together with the Ocean module to create a scrolling animated ocean surface that extends to the horizon. The GrdWtr module is a simplified version of the GrdMap module and creates a standard version of the ocean. The GrdMap module is a generalized module that gives the user the ability to define the geometries and textures for each grid. We will be expanding this module to work with land textures.

  Waves from the movie "Dunkirk" compared to waves generated by this module.
The Grid

Both the GrdWtr and GrdMap modules use the same grid map. This grid map has 3 nested grids of squares. Grid0 has 16x16 squares, each of size GrdSiz (e.g. 1 mile, range = 8 miles). Grid1 has 16x16 squares, each of size GrdSiz*4 (e.g. 4 miles, range = 32 miles). Grid2 has 16x16 squares, each of size GrdSiz*16 (e.g. 16 miles, range = 128 miles).

Using the Ocean (v2.0) and GrdWtr Modules in Your Program

The GrdWtr module is a simplified version of the GrdMap module and creates a standard version of the ocean. The ocean is untextured. The color of the ocean is a blend of the ocean color (WtrCol) variable and the SkyBox texture. Thus, merely changing the WtrCol variable may not have the desired effect since you also have to overcome the influence of the SkyBox texture. You can change the size of the smallest grid squares to increase resolution. However, this increases the risk of tiling as the altitude increases.

This references Version 2 of the Ocean program. For Version 1, see here.

Load the Ocean and GrdWtr Modules

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

import {Ocean} from "three/addons/air/Ocean2.js";	// [v2: file name changed]
import {GrdMap} from "three/addons/air/GrdWtr.js";
Define the Variables

You can add these variables to the beginning of your program:

/*= GRID DATA ================================================================*/
let GrdRes = 512;
let GrdSiz = 3200;		// Size of Smallest Grid Square (meters)
let GrdSeg = 256;		// Segments per Plane (256 = OK, 512 = too much)

/*= OCEAN MODULE =============================================================*/
let WndSpd = 20.0;
let WndHdg = 0.0;
let Choppy = 1;
let WavMax = 5;			// Maximum wave height (set height of outer waves)
//
let Ocean1 = 0;
let wav1_ = {
    // Sources
    Res: GrdRes,		// Resolution - segments per square (default = 512)
    Siz: GrdSiz,		// Size of Smallest Square = default = 3200m = 2 miles
    WSp: WndSpd,		// Wind Speed
    WHd: WndHdg,		// Wind Heading
    Chp: Choppy,		// default = 1
    // Results
    Dsp: 0,			// Displacement Map Address
    Nrm: 0,			// Normal Map Address
};
let wavSp1 = 1.0;	// Animation Speed Adjustment [v2: moved from wav_ and renamed]
let wavTm1 = 0;		// Elapsed Time (as Adjusted) [v2: moved from wav_ and renamed]

/*= GRID MODULE ==============================================================*/
let grids = 0;
let grx_ = {
      MSP: new THREE.Vector3 (), // MapSpdX, MapPosY, MapSpdZ (meters)
      // Inputs
      RCs: 16,			// Squares in each of first 2 grids
      Siz: GrdSiz,		// Size of smallest square
      Stp: 4,			// Size multiplier for squares in last 2 grids
      Seg: GrdSeg,		// Segments for smallest square
      WMx: WavMax,		// Max wave height, used to lower outer squares
      Col: WtrCol,		// Color
      Dsp: 0,			// Displacement Map (from Ocean)
      Nrm: 0,			// Normal Map (from Ocean)
      Env: 0,			// EnvMap
      // Results
      Grd: [],			// Index of Grids (0-2)
      Geo: [],			// Master Index of Basic Geometries
      Mat: [],			// Master Index of Basic Materials
   };
Initialization

You can add the following instructions to the Initialization section of your program:

   // Initialize Ocean
   Ocean1 = new Ocean(renderer, wav1_);	// [v2: no longer references camera or scene]
   // Set Aircraft Map Speed and Altitude
   grx_.MSP.z = beg_ZSpeed;	// Z-Speed (N/S) in meters per second
   grx_.MSP.x = beg_XSpeed;	// X-Speed (E/W) in meters per second
   grx_.MSP.y = beg_Altitude;	// Altitude in meters
   // Load GrdMap Variables
   grx_.Dsp = wav_.Dsp;
   grx_.Nrm = wav_.Nrm;
   grx_.Env = envMap;
   // Initialize GridMap
   grids = new GrdMap(grx_, scene);

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

Rendering

You can add the following instructions to the rendering section of your program:

   // Update Grid Data
   grx_.MSP.z = new_ZSpeed;
   grx_.MSP.x = new_XSpeed;
   grx_.MSP.y = new_Altitude;
   // Render Ocean
   wavTm1 = difTim*wavSp1 || 0.0;	[v2: variable moved from wav_ and renamed]
   Ocean1.render(wavTm1);	[v2: only reference only time variable
   // Render Grids
   grids.update(grx_);

In order to compute wavTm1, your main program needs to include a timer. A simple example is shown below.

Using the Ocean (v2.0) and GrdMap Modules in Your Program

The GrdMap module is a more generalized module which allows you to define the Geometries and Materials for each set of Grids. For example, you can add a texture to the ocean and a static normal map to the farthest grids.

This references Version 2 of the Ocean program. For Version 1, see here.

Load the Modules

You can add the instructions to the beginning of your program to load the modules:

import {Ocean} from "three/addons/air/Ocean2.js";	// [v2: file name changed]
import {GrdMap} from "three/addons/air/GrdWtr.js";
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
Variables

You can add these variables to your program:

/*= GRID DATA ================================================================*/
let GrdRes = 512;
let GrdSiz = 3200;		// Size of Smallest Grid Square (meters)
let GrdSeg = 256;		// Segments per Plane (256 = OK, 512 = too much)
let GeoAll = [];		// Array used to assemble Grids

/*= OCEAN MODULE =============================================================*/
let WndSpd = 20.0;
let WndHdg = 0.0;
let Choppy = 1;
let WavMax = 5;			// Maximum wave height (set height of outer waves)
//
let Ocean1 = 0;
let wav1_ = {
    // Sources
    Res: GrdRes,		// Resolution - segments per square (default = 512)
    Siz: GrdSiz,		// Size of Smallest Square = default = 3200m = 2 miles
    WSp: WndSpd,		// Wind Speed
    WHd: WndHdg,		// Wind Heading
    Chp: Choppy,		// default = 1
    // Results
    Dsp: 0,			// Displacement Map Address
    Nrm: 0,			// Normal Map Address
};
let wavSp1 = 1.0;	// Animation Speed Adjustment [v2: moved from wav_ and renamed]
let wavTm1 = 0;		// Elapsed Time (as Adjusted) [v2: moved from wav_ and renamed]

/*= GRID TEXTURES ============================================================*/
//  Textures (these need to be loaded in Main Program)
//  Diffuse Map (Optional - All Grids)
let WtrSrc = "https://threejs.org/examples/textures/waternormals.jpg";	// 0 = Don't load
let WtrTxt = 0;
//  Normal Map (Grid2 Only)
let NrmSrc = "https://threejs.org/examples/textures/waternormals.jpg";
let WtrNrm = 0;

/*= GRID MODULE ==============================================================*/
let grids = 0;
let grx_ = {
      MSP: new THREE.Vector3 (), // MSX, MPY, MSZ (meters)
      RCs: 16,			// Squares in each of first 2 grids
      Siz: GrdSiz,		// Size of smallest square
      Stp: 4,			// Size multiplier for squares in last 2 grids
      Seg: GrdSeg,		// Segments for smallest square
      Grd: [],			// Index of Grids (0-2)
      Geo: [],			// Master Index of Basic Geometries
      Mat: [],			// Master Index of Basic Materials
      WMx: WavMax,		// Max wave height, used to lower outer squares
   };
The GeoMap Functions

The GrdMap module allows you to create custom textures and geometries which are then loaded into the GridMap. You can add the GeoMap Functions below to your main program, which will allow you to add custom textures to the ocean and to customize your materials:

/*= GEOMAT ===================================================================*/

function loadGeoMat() {
   // Load Diffuse Map (Optional - All Grids)
   if (WtrSrc) {		// Load the texture only if there is an address
      txtrLoader.load(WtrSrc, function(texture) {
         texture.format = THREE.RGBAFormat;
         texture.magFilter = THREE.LinearFilter;
         texture.minFilter = THREE.LinearMipMapLinearFilter;
         texture.generateMipmaps = true;
         texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
         texture.offset.set(0,0);
         texture.needsUpdate = true;
         WtrTxt = texture;
      });
   }
   // Normal Map (Grid2 Only)
   txtrLoader.load(NrmSrc, function(texture) {
      texture.format = THREE.RGBAFormat;
      texture.magFilter = THREE.LinearFilter;
      texture.minFilter = THREE.LinearMipMapLinearFilter;
      texture.generateMipmaps = true;
      texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
      texture.offset.set(0,0);
      texture.repeat.set(16,16);
      texture.needsUpdate = true
      WtrNrm = texture;
   });
}

function initGeoMat() {
// Define the Materials and Geometries Referenced in grx_.Geo and grx_.Mat
   let siz = grx_.Siz;
   let stp = grx_.Stp;
   /*- Grid0 ----------------------------------------------------------------*/
   let n = 0;
   let seg = grx_.Seg;
   grx_.Mat[n] = new THREE.MeshPhysicalMaterial({
         color: WtrCol,
         map: WtrTxt,		// 0 if no map loaded
         displacementMap: wav_.Dsp, // Animated displacementMap
         normalMap: wav_.Nrm,	// Animated normalMap
         envMap: envMap,
         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,
      });
   grx_.Geo[n] = new THREE.PlaneGeometry(siz,siz,seg,seg);
         grx_.Geo[0].rotateX(-Math.PI * 0.5);
   /*- Grid1 ----------------------------------------------------------------*/
   // This far out, you cannot see the Animated displacementMap, 
   // but you can still see the Animated normalMap
   n = 1;
   grx_.Mat[n] = new THREE.MeshPhysicalMaterial({
         color: WtrCol,
         map: WtrTxt,		// 0 if no map loaded
         normalMap: wav_.Nrm,	// Animated normalMap
         envMap: envMap,
         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,
      });
   //  This allows you to repeat the Animated normalMap in this 4x4 Square
   let idx = 0;
   let ctr = (0.5*stp-0.5)*siz;	// 2 = 0.5; 3 = 1.0; 4 = 1.5
   for (let z = 0; z < stp; z++) {
      for (let x = 0; x < stp; x++) {
         GeoAll[idx] = new THREE.PlaneGeometry(siz,siz);
         GeoAll[idx].rotateX(-Math.PI * 0.5);
         GeoAll[idx].translate(x*siz-ctr, 0, z*siz-ctr);
         idx++;
      }
   }
   let max = stp*stp;
   for (let i = 1; i < max; i++) {
      GeoAll[0] = BufferGeometryUtils.mergeGeometries([GeoAll[0], GeoAll[i]], false);
   }
   grx_.Geo[n] = GeoAll[0];
   /*- Grid2 ----------------------------------------------------------------*/
   //  This far out, you cannot see the animation, so you can use a static normalMap
   n = 2;
   siz = grx_.Siz*grx_.Stp*grx_.Stp;
   grx_.Mat[n] = new THREE.MeshPhysicalMaterial({
         color: WtrCol,
         map: WtrTxt,		// 0 if no map loaded
         normalMap: WtrNrm,	// Static normalMap
         envMap: envMap,
         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,
      });
   grx_.Geo[n] = new THREE.PlaneGeometry(siz,siz);
   grx_.Geo[2].rotateX(-Math.PI * 0.5);
}
Loading

You can add the following instruction to the loading section of your program:

   loadGeoMat();
Initialization

You can add the following instructions to the Initialization section of your program:

   // Initialize Ocean
   Ocean1 = new Ocean(renderer, wav1_);
   // Define GridMap Materials and Geometries
   initGeoMat();		// Initialize Grid Materials
   // Set Aircraft Map Speed and Altitude
   grx_.MSP.z = beg_ZSpeed;	// Z-Speed (N/S) in meters per second
   grx_.MSP.x = beg_XSpeed;	// X-Speed (E/W) in meters per second
   grx_.MSP.y = beg_Altitude;	// Altitude in meters
   // Initialize GridMap
   grids = new GrdMap(grx_, scene);

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

Rendering

You can add the following instructions to the rendering section of your program:

   // Update Grid Data
   grx_.MSP.z = new_ZSpeed;
   grx_.MSP.x = new_XSpeed;
   grx_.MSP.y = new_Altitude;
   // Render Ocean
   wavTm1 = difTim * wav_.Spd || 0.0;
   Ocean1.render(wavTm1);
   // Render Grids
   grids.update(grx_);

In order to compute wav._DTm, your main program needs to include a timer. A simple example is shown below.

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;
   wavTm1 = difTim * wav_.Spd || 0.0;
Limitations

As altitude increases, you might notice a "tiling" problem. You can address this problem by increasing the size of the smallest grid. However, this will result in a decrease in resolution at lower altitudes.

The Zip File
Here is a zip file that includes the following:

  • Ocean2.js
  •       the Ocean class module.
  • GrdMap.js
  •       a GrdMap module in which water textures are imported.
  • GrdWtr.js
  •       a GrdMap module that automatically creates water textures.
  • SMD_X3W2_mod.html
  •       a program that uses the Ocean and GrdMap (GrdMap.js) modules.
  • SMD_X3W2.html
  •       a version of the above that you can run locally.
  • SMD_X3X2_mod.html
  •       a program that uses the Ocean and GrdMap (GrdWtr.js) modules.
  • SMD_X3X2.html
  •       a version of the above that you can run locally.