JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/misc/GPUComputationRenderer.js"></script>
<script id="heightmapFragmentShader" type="x-shader/x-fragment">
#include <common>
uniform vec2 mousePos;
uniform float mouseSize;
uniform float viscosityConstant;
uniform float heightCompensation;
void main() {
vec2 cellSize = 1.0 / resolution.xy;
vec2 uv = gl_FragCoord.xy * cellSize;
// heightmapValue.x == height from previous frame
// heightmapValue.y == height from penultimate frame
// heightmapValue.z, heightmapValue.w not used
vec4 heightmapValue = texture2D( heightmap, uv );
// Get neighbours
vec4 north = texture2D( heightmap, uv + vec2( 0.0, cellSize.y ) );
vec4 south = texture2D( heightmap, uv + vec2( 0.0, - cellSize.y ) );
vec4 east = texture2D( heightmap, uv + vec2( cellSize.x, 0.0 ) );
vec4 west = texture2D( heightmap, uv + vec2( - cellSize.x, 0.0 ) );
// https://web.archive.org/web/20080618181901/http://freespace.virgin.net/hugo.elias/graphics/x_water.htm
float newHeight = ( ( north.x + south.x + east.x + west.x ) * 0.5 - heightmapValue.y ) * viscosityConstant;
// Mouse influence
float mousePhase = clamp( length( ( uv - vec2( 0.5 ) ) * BOUNDS - vec2( mousePos.x, - mousePos.y ) ) * PI / mouseSize, 0.0, PI );
newHeight += ( cos( mousePhase ) + 1.0 ) * 0.28;
heightmapValue.y = heightmapValue.x;
heightmapValue.x = newHeight;
gl_FragColor = heightmapValue;
}
</script>
<!-- This is just a smoothing 'compute shader' for using manually: -->
<script id="smoothFragmentShader" type="x-shader/x-fragment">
uniform sampler2D smoothTexture;
void main() {
vec2 cellSize = 1.0 / resolution.xy;
vec2 uv =...
CSS
body{
overflow: hidden;
margin: 0;
}
JavaScript
// Texture width for simulation
// Texture width for simulation
const WIDTH = 128;
// Water size in system units
const BOUNDS = 512;
const BOUNDS_HALF = BOUNDS * 0.5;
let container, stats;
let camera, scene, renderer;
let mouseMoved = false;
const mouseCoords = new THREE.Vector2();
const raycaster = new THREE.Raycaster();
let waterMesh;
let meshRay;
let gpuCompute;
let heightmapVariable;
let waterUniforms;
let smoothShader;
let readWaterLevelShader;
let readWaterLevelRenderTarget;
let readWaterLevelImage;
const waterNormal = new THREE.Vector3();
const NUM_SPHERES = 5;
const spheres = [];
let spheresEnabled = true;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 3000);
camera.position.set(0, 200, 350);
camera.lookAt(0, 0, 0);
scene = new THREE.Scene();
const sun = new THREE.DirectionalLight(0xFFFFFF, 1.0);
sun.position.set(300, 400, 175);
scene.add(sun);
const sun2 = new THREE.DirectionalLight(0x40A040, 0.6);
sun2.position.set(- 100, 350, - 200);
scene.add(sun2);
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
container.style.touchAction = 'none';
container.addEventListener('pointermove', onPointerMove);
document.addEventListener('keydown', function (event) {
// W Pressed: Toggle wireframe
if (event.keyCode === 87) {
waterMesh.material.wireframe = !waterMesh.material.wireframe;
waterMesh.material.needsUpdate = true;
}
});
window.addEventListener('resize', onWindowResize);
const text = new THREE.TextureLoader().load("./baked.jpg")
const effectController = {
...