DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>沉浸式波浪动画title>
<style>
body { margin: ; }
#container { width: vw; height: vh; }
style>
head>
<body>
<div id="container">div>
<script type="module">
// 代码将在这里编写
script>
body>
html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r175/three.min.js">script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(, window.innerWidth / window.innerHeight, 0.1, );
camera.position.z = ; // 相机位置
const renderer = new THREE.WebGLRenderer({ antialias: true }); // 开启抗锯齿
renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染区域大小
document.getElementById('container').appendChild(renderer.domElement); // 将渲染器添加到页面
const planeGeometry = new THREE.PlaneGeometry(, , , ); // 平面尺寸和细分
const planeMaterial = new THREE.MeshStandardMaterial({
color: 0x00ffff, // 水的颜色
roughness: 0.2, // 粗糙度
metalness: 0.3, // 金属度
envMap: envMapTexture, // 环境贴图(可选)
});
const wavePlane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(wavePlane);
// 定义波浪参数
let time = ;
const waveSpeed = 0.05; // 波浪速度
const waveAmplitude = 0.5; // 波浪振幅
function animate() {
requestAnimationFrame(animate);
time += waveSpeed;
// 获取平面顶点数据
const vertices = wavePlane.geometry.attributes.position.array;
// 遍历每个顶点,计算位移
for (let i = ; i < vertices.length; i += ) {
const x = vertices[i];
const z = vertices[i + ];
// 波浪公式:y = amplitude * sin(x * frequency + time * speed) + cos(z * frequency + time * speed)
const waveY = waveAmplitude * (Math.sin(x * + time) + Math.cos(z * + time));
vertices[i + ] = waveY; // 更新 y 坐标
}
// 通知 Three.js 顶点数据已更新
wavePlane.geometry.attributes.position.needsUpdate = true;
wavePlane.geometry.computeVertexNormals(); // 重新计算法线,确保光照正确
renderer.render(scene, camera);
}
animate();
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; // 引入控件
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 启用阻尼,使动画更平滑
controls.dampingFactor = 0.05; // 阻尼系数
controls.autoRotate = false; // 是否自动旋转
controls.autoRotateSpeed = 2.0; // 自动旋转速度
// 在动画循环中更新控件
function animate() {
requestAnimationFrame(animate);
controls.update(); // 更新控件状态
// ...其他动画逻辑
renderer.render(scene, camera);
}
window.addEventListener('wheel', (event) => {
event.preventDefault();
waveSpeed += event.deltaY * 0.001; // 根据滚轮方向调整速度
waveSpeed = Math.max(0.01, Math.min(0.2, waveSpeed)); // 限制速度范围
});
renderer.domElement.addEventListener('click', (event) => {
// 将鼠标坐标转换为归一化设备坐标(NDC)
const mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * - ;
mouse.y = -(event.clientY / window.innerHeight) * + ;
// 创建射线投射器
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
// 检测与波浪平面的交点
const intersects = raycaster.intersectObjects([wavePlane]);
if (intersects.length > ) {
// 生成随机颜色
const randomColor = new THREE.Color(Math.random(), Math.random(), Math.random());
wavePlane.material.color.set(randomColor);
}
});
renderer.domElement.addEventListener('touchstart', (event) => {
event.preventDefault(); // 阻止默认触摸行为
if (event.touches.length === ) {
// 单指旋转
const touch = event.touches[];
controls.handleTouchStartRotate(touch);
} else if (event.touches.length === ) {
// 双指缩放和平移
const touch0 = event.touches[];
const touch1 = event.touches[];
controls.handleTouchStartDollyPan(touch0, touch1);
}
});
renderer.domElement.addEventListener('touchmove', (event) => {
event.preventDefault();
if (event.touches.length === ) {
controls.handleTouchMoveRotate(event.touches[]);
} else if (event.touches.length === ) {
controls.handleTouchMoveDollyPan(event.touches[], event.touches[]);
}
});
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // 颜色和强度
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, );
directionalLight.position.set(, , ); // 光源位置
directionalLight.castShadow = true; // 开启阴影
directionalLight.shadow.mapSize.set(, ); // 阴影贴图分辨率
scene.add(directionalLight);
wavePlane.castShadow = true; // 波浪平面投射阴影
wavePlane.receiveShadow = true; // 波浪平面接收阴影
renderer.shadowMap.enabled = true; // 渲染器启用阴影
const normalTexture = new THREE.TextureLoader().load('water_normal.jpg');
planeMaterial.normalMap = normalTexture;
planeMaterial.normalScale.set(0.1, 0.1); // 法线强度
const displacementTexture = new THREE.TextureLoader().load('water_displacement.jpg');
planeMaterial.displacementMap = displacementTexture;
planeMaterial.displacementScale = 0.2; // 置换强度
第五 AI 生成论文靠谱吗?手机用其一键生成论文教程 2025 在学术写作领域,AI 工具的应用愈发广泛,第五 AI 作为其中一员,其生成论文的可靠性和操作便捷性成为用户关注焦点。那么,第五 AI 生
在 2025 年的内容创作领域,AI 生成内容的普及带来了效率的飞跃,但也引发了信任危机。腾讯朱雀检测系统的出现,如同悬在创作者头上的达摩克利斯之剑,让每一篇 AI 生成的内容都面临被识破的风险。不过
有一云 AI 的 AI 写作功能可不止是简单的 “写”,它还能 “聪明地写”。只要输入关键词,AI 瞬间就能输出原创内容框架。更厉害的是它的全网热点追踪引擎,能实时抓取热词,自动把爆款基因融入文章,让
? 快速上手 Waverly AI 健康管理核心操作 刚开始用 Waverly AI 时,第一步得先把 APP 下载到手机里。不管你用的是苹果还是安卓手机,直接去应用商店搜 “Waverly AI”
? 支付方式全解析:Trendyol 如何让购物更便捷? Trendyol 作为土耳其领先的电商平台,支付方式丰富多样,满足不同用户的需求。** 现金支付(COD)** 是当地最受欢迎的方式,占比高达
? 小荷作文网竞赛活动 + 投稿指南,中小学生作文范文 + 写作技巧免费获取! 最近发现一个超实用的中小学生作文学习平台 —— 小荷作文网,这里不仅有丰富的竞赛活动和投稿指南,还能免费获取作文范文和写
? 平台背景:当 AI 遇上千年古籍的数字化革命 你有没有想过,那些沉睡在图书馆角落里的古籍,如何跨越千年与现代人对话?现在有这样一个平台,把 AI 技术和学术资源玩出了新花样 —— 专门做古籍整理发
? SVG.IO:免费无水印 SVG 编辑器的优选 在众多免费无水印的 SVG 编辑器中,SVG.IO 凭借其独特的优势脱颖而出。它是一款完全免费且无水印的在线 SVG 编辑器,支持多种功能设计,操作