Meta Description" name="description" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ocean Wave Simulation</title>
<style>
body { margin: 0; background-color: #000; color: #fff; font-family: sans-serif; }
canvas { display: block; }
#info {
position: absolute;
top: 10px;
width: 100%;
text-align: center;
z-index: 100;
display:block;
}
.controls {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.5);
padding: 15px;
border-radius: 10px;
display: flex;
gap: 20px;
align-items: center;
}
.slider-container {
display: flex;
flex-direction: column;
align-items: center;
}
label {
margin-bottom: 5px;
}
input[type="range"] {
width: 150px;
}
</style>
</head>
<body>
<div id="info">
<h1>Ocean Wave Simulation</h1>
</div>
<div class="controls">
<div class="slider-container">
<label for="windSpeed">Wind Speed</label>
<input type="range" id="windSpeed" min="0.1" max="10" value="4" step="0.1">
</div>
<div class="slider-container">
<label for="waveHeight">Wave Height</label>
<input type="range" id="waveHeight" min="0.1" max="5" value="1" step="0.1">
</div>
<div class="slider-container">
<label for="lighting">Lighting</label>
<input type="range" id="lighting" min="-1" max="1" value="0.5" step="0.01">
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
let scene, camera, renderer, water, light;
let windSpeed = 4, waveHeight = 1, lighting = 0.5;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 5, 10);
camera.lookAt(scene.position);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const waterGeometry = new THREE.PlaneGeometry(100, 100, 100, 100);
const waterMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 1.0 },
windSpeed: { value: windSpeed },
waveHeight: { value: waveHeight },
lightDirection: { value: new THREE.Vector3(lighting, 1.0, 0.5) },
waterColor: { value: new THREE.Color(0x001e0f) },
skyColor: { value: new THREE.Color(0x87CEEB) }
},
vertexShader: `
uniform float time;
uniform float windSpeed;
uniform float waveHeight;
varying vec3 vNormal;
varying vec3 vPosition;
void main() {
vNormal = normal;
vec3 pos = position;
float k = 2.0 * 3.14159 / 20.0;
float c = sqrt(9.8 / k);
float f1 = k * (pos.x - c * time * windSpeed);
float f2 = k * (pos.y - c * time * windSpeed * 0.5);
pos.z += waveHeight * sin(f1);
pos.z += waveHeight * cos(f2) * 0.5;
vPosition = pos;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
uniform vec3 lightDirection;
uniform vec3 waterColor;
uniform vec3 skyColor;
varying vec3 vNormal;
varying vec3 vPosition;
void main() {
vec3 normal = normalize(vNormal);
vec3 lightDir = normalize(lightDirection);
float fresnel = dot(cameraPosition - vPosition, normal);
fresnel = pow(1.0 - fresnel, 3.0);
fresnel = clamp(fresnel, 0.0, 1.0);
float diffuse = max(0.0, dot(normal, lightDir));
vec3 color = mix(waterColor, skyColor, fresnel);
gl_FragColor = vec4(color * diffuse, 1.0);
}
`
});
waterMaterial.side = THREE.DoubleSide;
water = new THREE.Mesh(waterGeometry, waterMaterial);
water.rotation.x = -Math.PI / 2;
scene.add(water);
light = new THREE.DirectionalLight(0xffffff, 1);
scene.add(light);
window.addEventListener('resize', onWindowResize, false);
document.getElementById('windSpeed').addEventListener('input', (event) => {
windSpeed = parseFloat(event.target.value);
water.material.uniforms.windSpeed.value = windSpeed;
});
document.getElementById('waveHeight').addEventListener('input', (event) => {
waveHeight = parseFloat(event.target.value);
water.material.uniforms.waveHeight.value = waveHeight;
});
document.getElementById('lighting').addEventListener('input', (event) => {
lighting = parseFloat(event.target.value);
water.material.uniforms.lightDirection.value.x = lighting;
});
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
water.material.uniforms.time.value += 1.0 / 60.0;
renderer.render(scene, camera);
}
</script>
</body>
</html>2
2
125KB
596KB
1,225.0ms
124.0ms
1,225.0ms