Gerstner Water with Sky Box
Threejs Boilerplate : https://github.com/Sean-Bradley/Three.js-TypeScript-Boilerplate Threejs Course : https://sbcode.net/threejs/ Discount Coupons : https://sbcode.net/coupons#threejs
by seanwasere
HTML
<!--
Threejs Boilerplate : https://github.com/Sean-Bradley/Three.js-TypeScript-Boilerplate
Threejs Course : https://sbcode.net/threejs/
Discount Coupons : https://sbcode.net/coupons#threejs
-->
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "https://cdn.skypack.dev/three@0.144.0/build/three.module",
"three/": "https://cdn.skypack.dev/three@0.144.0/",
"dat.gui": "https://cdn.skypack.dev/dat.gui"
}
}
</script>
<script id="vertexShader" type="x-shader/x-vertex">
uniform mat4 textureMatrix;
uniform float time;
varying vec4 mirrorCoord;
varying vec4 worldPosition;
#include <common>
#include <fog_pars_vertex>
#include <shadowmap_pars_vertex>
#include <logdepthbuf_pars_vertex>
uniform vec4 waveA;
uniform vec4 waveB;
uniform vec4 waveC;
vec3 GerstnerWave (vec4 wave, vec3 p) {
float steepness = wave.z;
float wavelength = wave.w;
float k = 2.0 * PI / wavelength;
float c = sqrt(9.8 / k);
vec2 d = normalize(wave.xy);
float f = k * (dot(d, p.xy) - c * time);
float a = steepness / k;
return vec3(
d.x * (a * cos(f)),
d.y * (a * cos(f)),
a * sin(f)
);
}
void main() {
mirrorCoord = modelMatrix * vec4( position, 1.0 );
worldPosition = mirrorCoord.xyzw;
mirrorCoord = textureMatrix * mirrorCoord;
vec3 p = position.xyz;
p += GerstnerWave(waveA, position.xyz);
p += GerstnerWave(waveB, position.xyz);
p += GerstnerWave(waveC, position.xyz);
...
CSS
body {
overflow: hidden;
margin: 0px;
}
JavaScript
import * as THREE from 'three';
import Stats from 'three/examples/jsm/libs/stats.module.js';
import {
GUI
} from 'dat.gui';
import {
OrbitControls
} from 'three/examples/jsm/controls/OrbitControls';
import {
Water
} from 'three/examples/jsm/objects/Water';
let container, stats;
let camera, scene, renderer;
let controls, water;
let clock, delta, boxes, numBoxes;
const waves = {
A: {
direction: 0,
steepness: 0.4,
wavelength: 60
},
B: {
direction: 30,
steepness: 0.4,
wavelength: 30
},
C: {
direction: 60,
steepness: 0.4,
wavelength: 15
},
};
function getWaveInfo(x, z, time) {
const pos = new THREE.Vector3();
const tangent = new THREE.Vector3(1, 0, 0);
const binormal = new THREE.Vector3(0, 0, 1);
Object.keys(waves).forEach((wave) => {
const w = waves[wave];
const k = (Math.PI * 2) / w.wavelength;
const c = Math.sqrt(9.8 / k);
const d = new THREE.Vector2(
Math.sin((w.direction * Math.PI) / 180),
-Math.cos((w.direction * Math.PI) / 180)
);
const f = k * (d.dot(new THREE.Vector2(x, z)) - c * time);
const a = w.steepness / k;
pos.x += d.y * (a * Math.cos(f));
pos.y += a * Math.sin(f);
pos.z += d.x * (a * Math.cos(f));
tangent.x += -d.x * d.x * (w.steepness * Math.sin(f));
tangent.y += d.x * (w.steepness * Math.cos(f));
tangent.z += -d.x * d.y * (w.steepness * Math.sin(f));
binormal.x += -d.x * d.y * (w.steepness * Math.sin(f));
binormal.y += d.y * (w.steepness * Math.cos(f));
binormal.z += -d.y * d.y * (w.steepness * Math.sin(f));
});
const normal = binormal.cross(tangent).normalize();
return {
position: pos,
normal: normal
};
}
function updateBoxes(delta) {
const t = water.material.uniforms['time'].value;
boxes.forEach((b) => {
const waveInfo = getWaveInfo(b.position.x, b.position.z, t);
b.position.y = waveInfo.position.y;
const quat = new THREE.Quaternion().setFromEuler(
new...