/*------------------------------
Imports
------------------------------*/
import { OrbitControls } from "https://cdn.jsdelivr.net/npm/three@0.112.1/examples/jsm/controls/OrbitControls.js";
/*------------------------------
Initial Variables
------------------------------*/
let cloudMesh;
let water;
let camera, scene, renderer, renderTarget, depthMaterial, clock;
let canvas = document.querySelector(".webgl-canvas");
let params = {
foamColor: 0xf0faff,
waterColor: 0x1d38,
threshold: 0.1,
fogNear: 0,
fogFar: 55,
fogColour: 0x0
};
/*------------------------------
Run App
------------------------------*/
init();
animate();
/*------------------------------
App
------------------------------*/
function init() {
/*------------------------------
Clock & Camera
------------------------------*/
clock = new THREE.Clock();
camera = new THREE.PerspectiveCamera(
70,
window.innerWidth / window.innerHeight,
0.1,
100
);
camera.position.set(0, 7, 10);
/*------------------------------
Scene
------------------------------*/
scene = new THREE.Scene();
scene.background = new THREE.Color(0x1e485e);
/*--------------
Renderer
--------------*/
renderer = new THREE.WebGLRenderer({ antialias: true, canvas, alpha: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// renderer.gammaOutput = true;
/*------------------------------
Orbit Controls
------------------------------*/
let controls = new OrbitControls(camera, renderer.domElement);
controls.minDistance = 1;
controls.maxDistance = 50;
/*------------------------------
Fog
------------------------------*/
// let fog = new THREE.Fog(params.fogColour, params.fogNear, params.fogFar);
// scene.fog = fog;
/*------------------------------
Lights
------------------------------*/
let ambientLight = new THREE.AmbientLight(0xcccccc, 0.4);
scene.add(ambientLight);
let dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(0, 5, 5);
scene.add(dirLight);
/*------------------------------
Models
------------------------------*/
let boxGeometry = new THREE.BoxGeometry(10, 1, 1);
let boxMaterial = new THREE.MeshLambertMaterial({ color: 0xea4d10 });
/*--------------
Box 1
--------------*/
let box1 = new THREE.Mesh(boxGeometry, boxMaterial);
box1.position.z = 4.5;
scene.add(box1);
/*--------------
Box 2
--------------*/
let box2 = new THREE.Mesh(boxGeometry, boxMaterial);
box2.position.z = -4.5;
scene.add(box2);
/*--------------
Box 3
--------------*/
let box3 = new THREE.Mesh(boxGeometry, boxMaterial);
box3.position.x = -5;
box3.rotation.y = Math.PI * 0.5;
scene.add(box3);
/*--------------
Box 4
--------------*/
let box4 = new THREE.Mesh(boxGeometry, boxMaterial);
box4.position.x = 5;
box4.rotation.y = Math.PI * 0.5;
scene.add(box4);
/*--------------
Box 5
--------------*/
let box5 = new THREE.Mesh(new THREE.BoxGeometry(), boxMaterial);
box5.rotation.y = Math.PI * 0.1;
box5.rotation.x = Math.PI * 0.05;
scene.add(box5);
/*--------------
Box 6
--------------*/
let box6 = new THREE.Mesh(new THREE.BoxGeometry(), boxMaterial);
box6.position.y = 0;
box6.position.x = 3;
box6.position.z = 3;
scene.add(box6);
/*--------------
Box 7
--------------*/
let box7 = new THREE.Mesh(new THREE.BoxGeometry(), boxMaterial);
box7.position.y = 0;
box7.position.x = -3;
box7.position.z = -2;
scene.add(box7);
/*--------------
Cloud
--------------*/
let cloudSrc =
"https://dubsy-world.fra1.cdn.digitaloceanspaces.com/Images/cl-001.png";
let cloudTexture = new THREE.TextureLoader().load(cloudSrc);
let cloudMaterial = new THREE.SpriteMaterial({
map: cloudTexture,
transparent: true,
opacity: 1
});
cloudMesh = new THREE.Sprite(cloudMaterial);
cloudMesh.scale.set(8.95, 4.66, 1.38);
cloudMesh.position.set(0, 4.1, 0);
cloudMesh.castShadow = true;
scene.add(cloudMesh);
/*------------------------------
Depth Texture & Pixel Ratio
------------------------------*/
let supportsDepthTextureExtension = !!renderer.extensions.get(
"WEBGL_depth_texture"
);
let pixelRatio = renderer.getPixelRatio();
/*------------------------------
Render Target
------------------------------*/
renderTarget = new THREE.WebGLRenderTarget(
window.innerWidth * pixelRatio,
window.innerHeight * pixelRatio
);
renderTarget.texture.minFilter = THREE.NearestFilter;
renderTarget.texture.magFilter = THREE.NearestFilter;
renderTarget.texture.generateMipmaps = false;
renderTarget.stencilBuffer = false;
/*------------------------------
Set Depth Texture
------------------------------*/
if (supportsDepthTextureExtension === true) {
renderTarget.depthTexture = new THREE.DepthTexture();
renderTarget.depthTexture.type = THREE.UnsignedShortType;
renderTarget.depthTexture.minFilter = THREE.NearestFilter;
renderTarget.depthTexture.maxFilter = THREE.NearestFilter;
}
/*------------------------------
Set Depth Material
------------------------------*/
depthMaterial = new THREE.MeshDepthMaterial();
depthMaterial.depthPacking = THREE.RGBADepthPacking;
depthMaterial.blending = THREE.NoBlending;
/*------------------------------
Displacemet Map
------------------------------*/
let dudvMap = new THREE.TextureLoader().load(
"https://i.imgur.com/uVQJZFn.png"
);
dudvMap.wrapS = dudvMap.wrapT = THREE.RepeatWrapping;
/*------------------------------
Water Geometry & Material
------------------------------*/
let waterGeometry = new THREE.PlaneGeometry(10, 10);
let waterMaterial = new THREE.ShaderMaterial({
/*--------------
Defines
--------------*/
defines: {
DEPTH_PACKING: supportsDepthTextureExtension === true ? 0 : 1,
ORTHOGRAPHIC_CAMERA: 0
},
/*--------------
Uniforms
--------------*/
uniforms: {
time: { value: 0 },
threshold: { value: 0.1 },
tDudv: { value: null },
tDepth: { value: null },
cameraNear: { value: 0 },
cameraFar: { value: 0 },
resolution: { value: new THREE.Vector2() },
foamColor: { value: new THREE.Color() },
waterColor: { value: new THREE.Color() },
...THREE.UniformsLib["fog"]
},
/*--------------
Others
--------------*/
fog: true,
vertexShader: vertexShader(),
fragmentShader: fragmentShader(),
transparent: true
// blending: THREE.AdditiveBlending,
});
/*------------------------------
Assign Unifoms Values
------------------------------*/
waterMaterial.uniforms.cameraNear.value = camera.near;
waterMaterial.uniforms.cameraFar.value = camera.far;
waterMaterial.uniforms.resolution.value.set(
window.innerWidth * pixelRatio,
window.innerHeight * pixelRatio
);
waterMaterial.uniforms.tDudv.value = dudvMap;
waterMaterial.uniforms.tDepth.value =
supportsDepthTextureExtension === true
? renderTarget.depthTexture
: renderTarget.texture;
/*------------------------------
Water Mesh
------------------------------*/
water = new THREE.Mesh(waterGeometry, waterMaterial);
water.rotation.x = -Math.PI * 0.5;
water.scale.set(500, 500, 500);
scene.add(water);
/*------------------------------
Resize
------------------------------*/
window.addEventListener("resize", onWindowResize, false);
}
/*------------------------------
Set Window Resize
------------------------------*/
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
let pixelRatio = renderer.getPixelRatio();
renderTarget.setSize(
window.innerWidth * pixelRatio,
window.innerHeight * pixelRatio
);
water.material.uniforms.resolution.value.set(
window.innerWidth * pixelRatio,
window.innerHeight * pixelRatio
);
}
/*------------------------------
Update Every Frame
------------------------------*/
function animate() {
requestAnimationFrame(animate);
/*------------------------------
Depth Pass
------------------------------*/
water.visible = false; // we don't want the depth of the water
scene.overrideMaterial = depthMaterial;
/*------------------------------
Render & Render Target
------------------------------*/
renderer.setRenderTarget(renderTarget);
cloudMesh.visible=false;
renderer.render(scene, camera);
cloudMesh.visible=true;
renderer.setRenderTarget(null);
scene.overrideMaterial = null;
water.visible = true;
/*------------------------------
Final Pass
------------------------------*/
let time = clock.getElapsedTime();
water.material.uniforms.threshold.value = params.threshold;
water.material.uniforms.time.value = time;
water.material.uniforms.foamColor.value.set(params.foamColor);
water.material.uniforms.waterColor.value.set(params.waterColor);
renderer.render(scene, camera);
}
/*------------------------------
Vertex Shader
------------------------------*/
function vertexShader() {
return /*glsl*/ `
#include <fog_pars_vertex>
varying vec2 vUv;
void main() {
#include <begin_vertex>
#include <project_vertex>
#include <fog_vertex>
vUv = uv;
}
`;
}
/*------------------------------
Fragment Shader
------------------------------*/
function fragmentShader() {
return /*glsl*/ `
#include <common>
#include <packing>
#include <fog_pars_fragment>
varying vec2 vUv;
uniform sampler2D tDepth;
uniform sampler2D tDudv;
uniform vec3 waterColor;
uniform vec3 foamColor;
uniform float cameraNear;
uniform float cameraFar;
uniform float time;
uniform float threshold;
uniform vec2 resolution;
float getDepth( const in vec2 screenPosition ) {
#if DEPTH_PACKING == 1
return unpackRGBAToDepth( texture2D( tDepth, screenPosition ) );
#else
return texture2D( tDepth, screenPosition ).x;
#endif
}
float getViewZ( const in float depth ) {
#if ORTHOGRAPHIC_CAMERA == 1
return orthographicDepthToViewZ( depth, cameraNear, cameraFar );
#else
return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );
#endif
}
void main() {
vec2 screenUV = gl_FragCoord.xy / resolution;
float fragmentLinearEyeDepth = getViewZ( gl_FragCoord.z );
float linearEyeDepth = getViewZ( getDepth( screenUV ) );
float diff = saturate( fragmentLinearEyeDepth - linearEyeDepth );
float foamForce = 0.05;
float thickness = 0.01;
float foamScale = 10.0; // Adjust the foam scale value to control the fineness of the foam
vec2 displacement = texture2D( tDudv, ( vUv * foamScale ) - time * 0.05 ).rg;
displacement = ( ( displacement * 2.0 ) - 1.0 ) * 1.0;
float waveAmount = sin((vUv.x + vUv.y) * 10.0 + time * 5.0) * foamForce; // Adjust the parameters to control the wave effect
displacement.x += waveAmount;
displacement.y += waveAmount;
diff += displacement.x;
gl_FragColor.rgb = mix( foamColor, waterColor, step( threshold / (0.1 / thickness), diff ) );
gl_FragColor.a = 1.0;
#include <tonemapping_fragment>
#include <encodings_fragment>
#include <fog_fragment>
}
`;
}