Spaces:
Running
Running
File size: 10,458 Bytes
88fac8b 7bacca7 b623540 88fac8b 7bacca7 b623540 3f03a92 88fac8b b623540 7bacca7 b623540 7bacca7 b623540 7bacca7 88fac8b 7bacca7 88fac8b 7bacca7 b623540 7bacca7 88fac8b b623540 7bacca7 88fac8b b623540 7bacca7 88fac8b 7bacca7 88fac8b b623540 7bacca7 b623540 7bacca7 21984c5 7bacca7 b623540 7bacca7 b623540 7bacca7 b623540 7bacca7 b623540 7bacca7 b623540 7bacca7 b623540 5d17858 bd068fd e79f077 b623540 7bacca7 b623540 7bacca7 b623540 7bacca7 b623540 7bacca7 b623540 7bacca7 88fac8b 7bacca7 88fac8b b623540 7bacca7 b623540 7bacca7 b623540 7bacca7 dad985a b623540 88fac8b 7bacca7 b623540 7bacca7 b623540 7bacca7 b623540 7bacca7 b623540 542c40e b623540 88fac8b 5d17858 88fac8b 7bacca7 88fac8b 7bacca7 88fac8b 7bacca7 88fac8b b623540 88fac8b b623540 7bacca7 b623540 7bacca7 b623540 7bacca7 88fac8b b623540 88fac8b b623540 88fac8b b623540 7bacca7 b623540 7bacca7 b623540 7bacca7 88fac8b 7bacca7 b623540 88fac8b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
import * as THREE from 'three';
// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); // Orthographic for 2D
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Simulation parameters
const resolution = 512; // Texture resolution
const damping = 0.995; // Slightly stronger damping for smoother waves
const waveSpeed = 0.55; // Faster waves for dynamic feel
// Frame buffers for height and normal maps
const heightTarget1 = new THREE.WebGLRenderTarget(resolution, resolution, {
type: THREE.FloatType,
minFilter: THREE.NearestFilter,
magFilter: THREE.NearestFilter,
});
const heightTarget2 = new THREE.WebGLRenderTarget(resolution, resolution, {
type: THREE.FloatType,
minFilter: THREE.NearestFilter,
magFilter: THREE.NearestFilter,
});
const normalTarget = new THREE.WebGLRenderTarget(resolution, resolution, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
});
// Simulation uniforms
const simUniforms = {
u_time: { value: 0.0 },
u_resolution: { value: new THREE.Vector2(resolution, resolution) },
u_mouse: { value: new THREE.Vector2(-1, -1) },
u_mouseForce: { value: 0.0 },
u_prevHeight: { value: heightTarget1.texture },
u_damping: { value: damping },
u_waveSpeed: { value: waveSpeed },
};
// Simulation vertex shader
const simVertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
// Simulation fragment shader (wave equation with background motion)
const simFragmentShader = `
uniform float u_time;
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_mouseForce;
uniform sampler2D u_prevHeight;
uniform float u_damping;
uniform float u_waveSpeed;
varying vec2 vUv;
// Noise for randomness and background motion
float random(vec2 st) {
return fract(sin(dot(st, vec2(127.1, 311.7))) * 43758.5453123);
}
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
void main() {
vec2 texel = 1.0 / u_resolution;
float h = texture2D(u_prevHeight, vUv).r; // Current height
float h_prev = texture2D(u_prevHeight, vUv).g; // Previous height
// Sample neighbors
float h_n = texture2D(u_prevHeight, vUv + vec2(0.0, texel.y)).r;
float h_s = texture2D(u_prevHeight, vUv - vec2(0.0, texel.y)).r;
float h_e = texture2D(u_prevHeight, vUv + vec2(texel.x, 0.0)).r;
float h_w = texture2D(u_prevHeight, vUv - vec2(texel.x, 0.0)).r;
// Wave equation
float c2 = u_waveSpeed * u_waveSpeed;
float newHeight = 2.0 * h - h_prev + c2 * (h_n + h_s + h_e + h_w - 4.0 * h);
newHeight *= u_damping;
// Mouse force with randomness
float dist = length(vUv - u_mouse);
if (dist < 0.05 && u_mouseForce > 0.0) {
float rand = noise(vUv + u_time) * 0.1 + 0.9; // Random strength variation
newHeight += 0.15 * rand * u_mouseForce * exp(-dist * 100.0);
}
// Background motion (subtle waves)
float t = u_time * 2.0;
float background = noise(vUv * 2.0 + t) * 0.00002;
background += sin(u_time * 0.5 + vUv.x * 5.0) * 0.0000002; // Periodic pulse
newHeight += background;
gl_FragColor = vec4(newHeight, h, 0.0, 1.0);
}
`;
// Simulation material
const simMaterial = new THREE.ShaderMaterial({
uniforms: simUniforms,
vertexShader: simVertexShader,
fragmentShader: simFragmentShader,
});
// Normal map shader (computes normals from height field)
const normalVertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const normalFragmentShader = `
uniform sampler2D u_heightMap;
uniform vec2 u_resolution;
varying vec2 vUv;
void main() {
vec2 texel = 1.0 / u_resolution;
float h = texture2D(u_heightMap, vUv).r;
float h_n = texture2D(u_heightMap, vUv + vec2(0.0, texel.y)).r;
float h_s = texture2D(u_heightMap, vUv - vec2(0.0, texel.y)).r;
float h_e = texture2D(u_heightMap, vUv + vec2(texel.x, 0.0)).r;
float h_w = texture2D(u_heightMap, vUv - vec2(texel.x, 0.0)).r;
// Compute normal using height gradients
vec3 normal = normalize(vec3(h_w - h_e, h_s - h_n, 0.05));
gl_FragColor = vec4(normal * 0.5 + 0.5, 1.0); // Pack to [0,1]
}
`;
const normalMaterial = new THREE.ShaderMaterial({
uniforms: {
u_heightMap: { value: heightTarget1.texture },
u_resolution: { value: new THREE.Vector2(resolution, resolution) },
},
vertexShader: normalVertexShader,
fragmentShader: normalFragmentShader,
});
// Simulation and normal scenes
const simGeometry = new THREE.PlaneGeometry(2, 2);
const simMesh = new THREE.Mesh(simGeometry, simMaterial);
const simScene = new THREE.Scene();
simScene.add(simMesh);
const normalMesh = new THREE.Mesh(simGeometry, normalMaterial);
const normalScene = new THREE.Scene();
normalScene.add(normalMesh);
// Rendering uniforms
const renderUniforms = {
u_heightMap: { value: heightTarget1.texture },
u_normalMap: { value: normalTarget.texture },
u_resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
u_time: { value: 0.0 },
u_lightPos: { value: new THREE.Vector2(0.5, 0.5) }, // Dynamic light position
};
// Rendering vertex shader
const renderVertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
// Rendering fragment shader (with lighting and glowing effects)
const renderFragmentShader = `
uniform sampler2D u_heightMap;
uniform sampler2D u_normalMap;
uniform vec2 u_resolution;
uniform float u_time;
uniform vec2 u_lightPos;
varying vec2 vUv;
// Noise for granularity
float random(vec2 st) {
return fract(sin(dot(st, vec2(127.1, 311.7))) * 43758.5453123);
}
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
void main() {
float height = texture2D(u_heightMap, vUv).r;
vec3 normal = texture2D(u_normalMap, vUv).xyz * 2.0 - 1.0;
vec2 p = vUv * u_resolution.xy / min(u_resolution.x, u_resolution.y);
// Dynamic lighting
vec3 lightDir = normalize(vec3(u_lightPos - vUv, 0.5));
float diffuse = max(dot(normal, lightDir), 0.0);
float specular = pow(diffuse, 32.0) * 0.5;
// Base color with gradient
vec3 baseColor = mix(vec3(0.0, 0.2, 0.5), vec3(0.1, 0.7, 1.0), 0.5 + height * 3.0);
// Add granularity
float n = noise(p * 15.0 + u_time * 0.2) * 0.1;
baseColor += vec3(n);
// Glowing wave crests
float glow = abs(height) > 0.02 ? sin(height * 100.0 + u_time * 2.0) * 0.3 : 0.0;
vec3 glowColor = vec3(0.2, 0.8, 1.0) * glow;
vec3 glowColorB = vec3(0.2, 0.8, 1.0);
// Combine lighting and effects
vec3 color = baseColor * (0.5 + diffuse) + vec3(specular) + glowColor;
color = clamp(color, 0.0, 1.0);
gl_FragColor = vec4(color, 1);
}
`;
// Rendering material
const renderMaterial = new THREE.ShaderMaterial({
uniforms: renderUniforms,
vertexShader: renderVertexShader,
fragmentShader: renderFragmentShader,
});
// Rendering plane
const renderGeometry = new THREE.PlaneGeometry(2, 2);
const renderMesh = new THREE.Mesh(renderGeometry, renderMaterial);
scene.add(renderMesh);
// Handle window resize
window.addEventListener('resize', () => {
renderer.setSize(window.innerWidth, window.innerHeight);
renderUniforms.u_resolution.value.set(window.innerWidth, window.innerHeight);
});
// Mouse interaction (click and drag)
const mouse = new THREE.Vector2();
let isMouseDown = false;
window.addEventListener('mousedown', (event) => {
isMouseDown = true;
updateMouse(event);
});
window.addEventListener('mousemove', (event) => {
if (isMouseDown) updateMouse(event);
});
window.addEventListener('mouseup', () => {
isMouseDown = false;
simUniforms.u_mouseForce.value = 0.0;
});
function updateMouse(event) {
mouse.x = event.clientX / window.innerWidth;
mouse.y = 1.0 - event.clientY / window.innerHeight;
simUniforms.u_mouse.value.set(mouse.x, mouse.y);
simUniforms.u_mouseForce.value = 1.0;
}
// Animation loop
const clock = new THREE.Clock();
let currentHeightTarget = heightTarget1;
let nextHeightTarget = heightTarget2;
function animate() {
requestAnimationFrame(animate);
const t = clock.getElapsedTime();
simUniforms.u_time.value = t;
renderUniforms.u_time.value = t;
// Update light position (follows mouse or oscillates)
renderUniforms.u_lightPos.value.set(
isMouseDown ? mouse.x : 0.5 + Math.sin(t * 0.5) * 0.3,
isMouseDown ? mouse.y : 0.5 + Math.cos(t * 0.5) * 0.3
);
// Update simulation
simUniforms.u_prevHeight.value = currentHeightTarget.texture;
renderer.setRenderTarget(nextHeightTarget);
renderer.render(simScene, camera);
// Update normal map
normalMaterial.uniforms.u_heightMap.value = nextHeightTarget.texture;
renderer.setRenderTarget(normalTarget);
renderer.render(normalScene, camera);
// Render to screen
renderUniforms.u_heightMap.value = nextHeightTarget.texture;
renderer.setRenderTarget(null);
renderer.render(scene, camera);
// Swap buffers
const temp = currentHeightTarget;
currentHeightTarget = nextHeightTarget;
nextHeightTarget = temp;
}
animate(); |