欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > 从零开始学习three.js(20):three.js实现天气与时间动态效果(白天,黑夜,下雨,下雪)

从零开始学习three.js(20):three.js实现天气与时间动态效果(白天,黑夜,下雨,下雪)

2025/5/17 8:27:35 来源:https://blog.csdn.net/qq_26655893/article/details/148005338  浏览:    关键词:从零开始学习three.js(20):three.js实现天气与时间动态效果(白天,黑夜,下雨,下雪)

基于Three.js的天气与时间动态效果实现

本文将通过代码解析,介绍如何使用Three.js实现动态天气(下雨、下雪)和时间(白天、黑夜)切换效果。完整代码基于一个交互式天气模拟项目,支持粒子密度、速度和环境亮度的实时调整。


一、场景初始化

Three.js场景的基础搭建是项目的核心,包含相机、渲染器、光照和地面模型:

let scene, camera, renderer, controls;function initScene() {// 创建场景scene = new THREE.Scene();// 创建相机(透视相机)camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);camera.position.set(0, 10, 20);// 创建WebGL渲染器const container = document.getElementById("sceneContainer");renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });renderer.setSize(container.clientWidth, container.clientHeight);container.appendChild(renderer.domElement);// 添加轨道控制器controls = new THREE.OrbitControls(camera, renderer.domElement);controls.enableDamping = true;// 添加环境光与平行光ambientLight = new THREE.AmbientLight(0xffffff, 0.5);directionalLight = new THREE.DirectionalLight(0xffffff, 1);scene.add(ambientLight, directionalLight);// 创建地面const ground = new THREE.Mesh(new THREE.PlaneGeometry(100, 100),new THREE.MeshPhongMaterial({ color: 0xcccccc }));ground.rotation.x = -Math.PI / 2;scene.add(ground);// 启动动画循环animate();
}

二、天气效果实现

1. 下雨效果

通过粒子系统模拟雨滴下落:

function setupRain() {// 创建包含位置和速度数据的几何体const rainGeometry = new THREE.BufferGeometry();const positions = new Float32Array(particleCount * 3);const velocities = new Float32Array(particleCount * 3);// 初始化粒子位置和速度for (let i = 0; i < particleCount; i++) {positions[i*3] = (Math.random() - 0.5) * 100;   // X轴随机位置positions[i*3 + 1] = Math.random() * 50;        // Y轴初始高度positions[i*3 + 2] = (Math.random() - 0.5) * 100;// Z轴随机位置velocities[i*3 + 1] = -0.2 * particleSpeed;     // 垂直下落速度}// 创建粒子材质const rainMaterial = new THREE.PointsMaterial({color: 0xadd8e6,size: 0.1,transparent: true});// 生成粒子系统rainMesh = new THREE.Points(rainGeometry, rainMaterial);scene.add(rainMesh);
}

2. 下雪效果

雪花通过随机水平位移和大小变化增强真实感:

function setupSnow() {const snowGeometry = new THREE.BufferGeometry();const sizes = new Float32Array(particleCount);// 为雪花添加水平运动和随机大小for (let i = 0; i < particleCount; i++) {velocities[i*3] = (Math.random() - 0.5) * 0.05 * particleSpeed; // X轴漂移velocities[i*3 + 2] = (Math.random() - 0.5) * 0.05 * particleSpeed; // Z轴漂移sizes[i] = 0.1 + Math.random() * 0.2; // 随机大小}snowGeometry.setAttribute("size", new THREE.BufferAttribute(sizes, 1));// 其余逻辑与下雨类似...
}

三、时间切换实现

1. 白天模式

通过调整背景渐变和光照参数实现:

function setDayTime() {// 修改天空背景sceneContainer.style.background = "linear-gradient(to bottom, #87CEEB, #E0F7FA)";// 设置光照参数ambientLight.color.set(0xffffff);ambientLight.intensity = lightIntensity;directionalLight.color.set(0xffffff);directionalLight.intensity = 1;
}

2. 夜晚模式

降低光照强度并修改光源颜色:

function setNightTime() {sceneContainer.style.background = "linear-gradient(to bottom, #0A192F, #112240)";ambientLight.color.set(0x404040);ambientLight.intensity = lightIntensity * 0.3;directionalLight.color.set(0x8080ff); // 添加冷色调月光directionalLight.intensity = 0.5;
}

3. 多云模型

降低光照强度模拟多云天气

function setupCloudy() {// 降低光照强度模拟多云天气ambientLight.intensity = lightIntensity * 0.7;directionalLight.intensity = 0.7;
}

四、动态效果更新

在动画循环中持续更新粒子位置:

function updateParticles() {if (isRaining) {const positions = rainMesh.geometry.attributes.position.array;for (let i = 0; i < positions.length; i += 3) {positions[i + 1] += velocities[i + 1]; // Y轴位置更新if (positions[i + 1] < 0) resetParticlePosition(i); // 重置超出范围的粒子}rainMesh.geometry.attributes.position.needsUpdate = true;}// 雪花更新逻辑类似...
}

五、用户交互实现

通过事件监听实现参数调整:

// 密度滑块控制
densitySlider.addEventListener("input", (e) => {particleCount = parseInt(e.target.value);updateParticleDensity(); // 重新生成粒子系统
});// 重置按钮
resetBtn.addEventListener("click", () => {particleCount = 5000;setupRain(); // 重置为初始状态setDayTime();
});

六、整体代码,可直接复制运行,修改orbitControl.js地址即可运行

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Three.js天气效果演示</title><script src="https://cdn.tailwindcss.com"></script><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet" /><!--可用的three.js地址--><script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/three.js/110/three.js">  </script><!--可用的orbitContorl.js地址--><script src="./orbitControl.js"></script><script>tailwind.config = {theme: {extend: {colors: {primary: "#165DFF",secondary: "#6B7280",accent: "#3B82F6",dark: "#1F2937",light: "#F9FAFB",},fontFamily: {inter: ["Inter", "sans-serif"],},},},};</script><style type="text/tailwindcss">@layer utilities {.content-auto {content-visibility: auto;}.text-shadow {text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);}.bg-blur {backdrop-filter: blur(8px);}.transition-all-300 {transition: all 300ms ease-in-out;}.weather-card-hover {@apply hover:shadow-lg hover:-translate-y-1 transition-all duration-300;}}</style>
</head><body class="font-inter bg-gradient-to-br from-slate-50 to-slate-100 min-h-screen flex flex-col overflow-x-hidden"><!-- 顶部导航 --><header class="bg-white/80 bg-blur shadow-sm fixed w-full z-50 transition-all duration-300"><div class="container mx-auto px-4 py-3 flex justify-between items-center"><div class="flex items-center space-x-2"><i class="fa-solid fa-cloud-sun-rain text-primary text-2xl"></i><h1 class="text-xl font-bold text-dark">天气模拟器</h1></div><button class="md:hidden text-dark text-xl"><i class="fa-solid fa-bars"></i></button></div></header><!-- 主要内容 --><main class="flex-grow pt-16 flex flex-col md:flex-row"><!-- 控制面板 --><asideclass="w-full md:w-80 bg-white/80 bg-blur shadow-md p-4 md:sticky md:top-20 md:h-[calc(100vh-5rem)] overflow-y-auto"><h2 class="text-xl font-semibold mb-4 text-dark border-b pb-2">场景控制</h2><div class="space-y-6"><!-- 天气选择 --><div><h3 class="font-medium text-dark/90 mb-3">天气效果</h3><div class="grid grid-cols-2 gap-3"><button id="rainBtn"class="weather-card-hover bg-white border border-gray-200 rounded-lg p-3 flex flex-col items-center"><i class="fa-solid fa-cloud-rain text-primary text-2xl mb-2"></i><span class="text-sm">下雨</span></button><button id="snowBtn"class="weather-card-hover bg-white border border-gray-200 rounded-lg p-3 flex flex-col items-center"><i class="fa-solid fa-snowflake text-accent text-2xl mb-2"></i><span class="text-sm">下雪</span></button><button id="clearBtn"class="weather-card-hover bg-white border border-gray-200 rounded-lg p-3 flex flex-col items-center"><i class="fa-solid fa-sun text-yellow-500 text-2xl mb-2"></i><span class="text-sm">晴天</span></button><button id="cloudyBtn"class="weather-card-hover bg-white border border-gray-200 rounded-lg p-3 flex flex-col items-center"><i class="fa-solid fa-cloud text-gray-400 text-2xl mb-2"></i><span class="text-sm">多云</span></button></div></div><!-- 时间选择 --><div><h3 class="font-medium text-dark/90 mb-3">时间</h3><div class="grid grid-cols-2 gap-3"><button id="dayBtn"class="weather-card-hover bg-white border border-gray-200 rounded-lg p-3 flex flex-col items-center"><i class="fa-solid fa-sun text-yellow-500 text-2xl mb-2"></i><span class="text-sm">白天</span></button><button id="nightBtn"class="weather-card-hover bg-white border border-gray-200 rounded-lg p-3 flex flex-col items-center"><i class="fa-solid fa-moon text-blue-700 text-2xl mb-2"></i><span class="text-sm">夜晚</span></button></div></div><!-- 粒子密度控制 --><div><h3 class="font-medium text-dark/90 mb-2">粒子密度</h3><div class="flex items-center space-x-3"><i class="fa-solid fa-minus text-gray-400"></i><input type="range" id="densitySlider" min="1000" max="20000" value="5000"class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary" /><i class="fa-solid fa-plus text-gray-400"></i></div><p class="text-xs text-gray-500 mt-1">当前: <span id="densityValue">5000</span></p></div><!-- 粒子速度控制 --><div><h3 class="font-medium text-dark/90 mb-2">粒子速度</h3><div class="flex items-center space-x-3"><i class="fa-solid fa-turtle text-gray-400"></i><input type="range" id="speedSlider" min="1" max="20" value="10"class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary" /><i class="fa-solid fa-rabbit text-gray-400"></i></div><p class="text-xs text-gray-500 mt-1">当前: <span id="speedValue">10</span></p></div><!-- 环境光强度 --><div><h3 class="font-medium text-dark/90 mb-2">环境亮度</h3><div class="flex items-center space-x-3"><i class="fa-solid fa-moon text-gray-400"></i><input type="range" id="lightSlider" min="0" max="1" step="0.01" value="0.5"class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-primary" /><i class="fa-solid fa-sun text-gray-400"></i></div><p class="text-xs text-gray-500 mt-1">当前: <span id="lightValue">0.5</span></p></div><!-- 重置按钮 --><button id="resetBtn"class="w-full bg-primary hover:bg-primary/90 text-white font-medium py-2 px-4 rounded-lg transition-all duration-300 flex items-center justify-center space-x-2"><i class="fa-solid fa-refresh"></i><span>重置场景</span></button></div></aside><!-- 3D场景容器 --><div class="flex-grow relative"><div id="sceneContainer" class="w-full h-full bg-gradient-to-b from-blue-400 to-blue-600"></div><!-- 信息卡片 --><div class="absolute top-4 left-4 bg-white/80 bg-blur rounded-lg shadow-md p-4 max-w-md"><h3 class="text-lg font-semibold text-dark mb-2">Three.js天气模拟</h3><p class="text-sm text-dark/80">使用Three.js实现的交互式天气模拟系统,支持下雨、下雪等天气效果,以及白天黑夜的时间变化。</p><div class="mt-3 flex items-center text-xs text-dark/60"><i class="fa-solid fa-info-circle mr-1"></i><span>拖动鼠标旋转视角,滚轮缩放场景</span></div></div><!-- 天气状态显示 --><div id="weatherStatus"class="absolute top-4 right-4 bg-white/80 bg-blur rounded-lg shadow-md p-4 flex items-center"><i id="weatherIcon" class="fa-solid fa-cloud-sun-rain text-primary text-3xl mr-3"></i><div><h3 id="weatherType" class="text-lg font-semibold text-dark">下雨</h3><p id="timeOfDay" class="text-sm text-dark/80">白天</p></div></div></div></main><!-- 页脚 --><footer class="bg-dark text-white/80 py-4"><div class="container mx-auto px-4 text-center text-sm"><p>© 2025 天气模拟器 | 使用 Three.js 构建</p><div class="mt-2 flex justify-center space-x-4"><a href="#" class="hover:text-white transition-colors"><i class="fa-brands fa-github"></i></a><a href="#" class="hover:text-white transition-colors"><i class="fa-brands fa-twitter"></i></a><a href="#" class="hover:text-white transition-colors"><i class="fa-brands fa-linkedin"></i></a></div></div></footer><script>// 场景初始化let scene, camera, renderer, controls;let rainMesh, snowMesh, ambientLight, directionalLight;let isRaining = false,isSnowing = false;let currentTime = "day";let particleCount = 5000;let particleSpeed = 10;let lightIntensity = 0.5;// 初始化Three.js场景function initScene() {// 创建场景scene = new THREE.Scene();// 创建相机camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000);camera.position.set(0, 10, 20);// 创建渲染器const container = document.getElementById("sceneContainer");renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });renderer.setSize(container.clientWidth, container.clientHeight);renderer.setPixelRatio(window.devicePixelRatio);container.appendChild(renderer.domElement);// 添加轨道控制器controls = new THREE.OrbitControls(camera, renderer.domElement);controls.enableDamping = true;controls.dampingFactor = 0.1;controls.rotateSpeed = 0.5;controls.zoomSpeed = 0.8;// 添加环境光ambientLight = new THREE.AmbientLight(0xffffff, lightIntensity);scene.add(ambientLight);// 添加平行光(太阳光/月光)directionalLight = new THREE.DirectionalLight(0xffffff, 1);directionalLight.position.set(1, 2, 1);directionalLight.castShadow = true;scene.add(directionalLight);// 添加地面const groundGeometry = new THREE.PlaneGeometry(100, 100);const groundMaterial = new THREE.MeshPhongMaterial({ color: 0xcccccc });const ground = new THREE.Mesh(groundGeometry, groundMaterial);ground.rotation.x = -Math.PI / 2;ground.receiveShadow = true;scene.add(ground);// 添加一些简单的建筑模型// addBuildings();// 初始设置为下雨和白天setupRain();setDayTime();// 渲染循环function animate() {requestAnimationFrame(animate);// 更新控制器controls.update();// 更新粒子系统updateParticles();// 渲染场景renderer.render(scene, camera);}animate();// 监听窗口大小变化window.addEventListener("resize", onWindowResize);}// 窗口大小变化处理function onWindowResize() {const container = document.getElementById("sceneContainer");camera.aspect = container.clientWidth / container.clientHeight;camera.updateProjectionMatrix();renderer.setSize(container.clientWidth, container.clientHeight);}// 添加简单的建筑模型function addBuildings() {const buildingPositions = [[-15, 0, -15],[0, 0, -15],[15, 0, -15],[-15, 0, 0],[0, 0, 0],[15, 0, 0],[-15, 0, 15],[0, 0, 15],[15, 0, 15],];const buildingHeights = [5, 7, 4, 6, 10, 8, 4, 9, 6];const buildingColors = [0x8b4513, 0xa0522d, 0xd2691e, 0xcd853f, 0xf4a460, 0xdaa520, 0xb8860b,0xbc8f8f, 0xf5deb3,];buildingPositions.forEach((pos, index) => {const geometry = new THREE.BoxGeometry(3, buildingHeights[index], 3);const material = new THREE.MeshPhongMaterial({color: buildingColors[index],});const building = new THREE.Mesh(geometry, material);building.position.set(pos[0], buildingHeights[index] / 2, pos[1]);building.castShadow = true;building.receiveShadow = true;scene.add(building);// 添加屋顶const roofGeometry = new THREE.ConeGeometry(2.5, 3, 4);const roofMaterial = new THREE.MeshPhongMaterial({ color: 0x8b0000 });const roof = new THREE.Mesh(roofGeometry, roofMaterial);roof.position.set(pos[0], buildingHeights[index] + 1.5, pos[1]);roof.castShadow = true;scene.add(roof);});}// 设置下雨效果function setupRain() {// 移除现有的雨雪效果if (rainMesh) scene.remove(rainMesh);if (snowMesh) scene.remove(snowMesh);isRaining = true;isSnowing = false;// 更新UIdocument.getElementById("weatherType").textContent = "下雨";document.getElementById("weatherIcon").className ="fa-solid fa-cloud-rain text-primary text-3xl mr-3";// 创建雨滴几何体const rainGeometry = new THREE.BufferGeometry();const rainCount = particleCount;// 随机位置和速度const positions = new Float32Array(rainCount * 3);const velocities = new Float32Array(rainCount * 3);for (let i = 0; i < rainCount; i++) {const i3 = i * 3;// 随机位置positions[i3] = (Math.random() - 0.5) * 100;positions[i3 + 1] = Math.random() * 50;positions[i3 + 2] = (Math.random() - 0.5) * 100;// 随机速度velocities[i3] = 0;velocities[i3 + 1] = -(0.1 + Math.random() * 0.2) * particleSpeed;velocities[i3 + 2] = 0;}rainGeometry.setAttribute("position",new THREE.BufferAttribute(positions, 3));rainGeometry.setAttribute("velocity",new THREE.BufferAttribute(velocities, 3));// 创建雨滴材质const rainMaterial = new THREE.PointsMaterial({color: 0xadd8e6,size: 0.1,transparent: true,opacity: 0.8,});// 创建雨滴粒子系统rainMesh = new THREE.Points(rainGeometry, rainMaterial);scene.add(rainMesh);}// 设置下雪效果function setupSnow() {// 移除现有的雨雪效果if (rainMesh) scene.remove(rainMesh);if (snowMesh) scene.remove(snowMesh);isRaining = false;isSnowing = true;// 更新UIdocument.getElementById("weatherType").textContent = "下雪";document.getElementById("weatherIcon").className ="fa-solid fa-snowflake text-accent text-3xl mr-3";// 创建雪花几何体const snowGeometry = new THREE.BufferGeometry();const snowCount = particleCount;const positions = new Float32Array(snowCount * 3);const velocities = new Float32Array(snowCount * 3);const sizes = new Float32Array(snowCount);for (let i = 0; i < snowCount; i++) {const i3 = i * 3;// 随机位置positions[i3] = (Math.random() - 0.5) * 100;positions[i3 + 1] = Math.random() * 50;positions[i3 + 2] = (Math.random() - 0.5) * 100;// 随机速度velocities[i3] = (Math.random() - 0.5) * 0.05 * particleSpeed;velocities[i3 + 1] = -(0.05 + Math.random() * 0.1) * particleSpeed;velocities[i3 + 2] = (Math.random() - 0.5) * 0.05 * particleSpeed;// 随机大小sizes[i] = 0.1 + Math.random() * 0.2;}snowGeometry.setAttribute("position",new THREE.BufferAttribute(positions, 3));snowGeometry.setAttribute("velocity",new THREE.BufferAttribute(velocities, 3));snowGeometry.setAttribute("size", new THREE.BufferAttribute(sizes, 1));// 创建雪花材质const snowMaterial = new THREE.PointsMaterial({color: 0xffffff,size: 0.1,transparent: true,opacity: 0.9,});// 创建雪花粒子系统snowMesh = new THREE.Points(snowGeometry, snowMaterial);scene.add(snowMesh);}// 设置晴天效果function setupClear() {// 移除现有的雨雪效果if (rainMesh) scene.remove(rainMesh);if (snowMesh) scene.remove(snowMesh);isRaining = false;isSnowing = false;// 更新UIdocument.getElementById("weatherType").textContent = "晴天";document.getElementById("weatherIcon").className ="fa-solid fa-sun text-yellow-500 text-3xl mr-3";}// 设置多云效果function setupCloudy() {// 移除现有的雨雪效果if (rainMesh) scene.remove(rainMesh);if (snowMesh) scene.remove(snowMesh);isRaining = false;isSnowing = false;// 更新UIdocument.getElementById("weatherType").textContent = "多云";document.getElementById("weatherIcon").className ="fa-solid fa-cloud text-gray-400 text-3xl mr-3";// 降低光照强度模拟多云天气ambientLight.intensity = lightIntensity * 0.7;directionalLight.intensity = 0.7;}// 设置白天function setDayTime() {currentTime = "day";document.getElementById("timeOfDay").textContent = "白天";// 更新天空颜色const container = document.getElementById("sceneContainer");container.style.background ="linear-gradient(to bottom, #87CEEB, #E0F7FA)";// 更新光照ambientLight.color.set(0xffffff);ambientLight.intensity = lightIntensity;directionalLight.color.set(0xffffff);directionalLight.intensity = 1;directionalLight.position.set(1, 2, 1);}// 设置夜晚function setNightTime() {currentTime = "night";document.getElementById("timeOfDay").textContent = "夜晚";// 更新天空颜色const container = document.getElementById("sceneContainer");container.style.background ="linear-gradient(to bottom, #0A192F, #112240)";// 更新光照ambientLight.color.set(0x404040);ambientLight.intensity = lightIntensity * 0.3;directionalLight.color.set(0x8080ff);directionalLight.intensity = 0.5;directionalLight.position.set(-1, 2, -1);}// 更新粒子系统function updateParticles() {if (isRaining && rainMesh) {const positions = rainMesh.geometry.attributes.position.array;const velocities = rainMesh.geometry.attributes.velocity.array;for (let i = 0; i < positions.length; i += 3) {// 更新位置positions[i + 1] += velocities[i + 1];// 如果雨滴落到地面,重置位置if (positions[i + 1] < 0) {positions[i] = (Math.random() - 0.5) * 100;positions[i + 1] = 50;positions[i + 2] = (Math.random() - 0.5) * 100;}}rainMesh.geometry.attributes.position.needsUpdate = true;}if (isSnowing && snowMesh) {const positions = snowMesh.geometry.attributes.position.array;const velocities = snowMesh.geometry.attributes.velocity.array;for (let i = 0; i < positions.length; i += 3) {// 更新位置positions[i] += velocities[i];positions[i + 1] += velocities[i + 1];positions[i + 2] += velocities[i + 2];// 如果雪花落到地面,重置位置if (positions[i + 1] < 0) {positions[i] = (Math.random() - 0.5) * 100;positions[i + 1] = 50;positions[i + 2] = (Math.random() - 0.5) * 100;}}snowMesh.geometry.attributes.position.needsUpdate = true;}}// 更新粒子密度function updateParticleDensity() {if (isRaining) {setupRain();} else if (isSnowing) {setupSnow();}}// 初始化事件监听function initEventListeners() {// 天气按钮document.getElementById("rainBtn").addEventListener("click", () => {setupRain();document.querySelectorAll("#rainBtn, #snowBtn, #clearBtn, #cloudyBtn").forEach((btn) => {btn.classList.remove("ring-2", "ring-primary");});document.getElementById("rainBtn").classList.add("ring-2", "ring-primary");});document.getElementById("snowBtn").addEventListener("click", () => {setupSnow();document.querySelectorAll("#rainBtn, #snowBtn, #clearBtn, #cloudyBtn").forEach((btn) => {btn.classList.remove("ring-2", "ring-primary");});document.getElementById("snowBtn").classList.add("ring-2", "ring-primary");});document.getElementById("clearBtn").addEventListener("click", () => {setupClear();document.querySelectorAll("#rainBtn, #snowBtn, #clearBtn, #cloudyBtn").forEach((btn) => {btn.classList.remove("ring-2", "ring-primary");});document.getElementById("clearBtn").classList.add("ring-2", "ring-primary");// 恢复光照ambientLight.intensity = lightIntensity;directionalLight.intensity = 1;});document.getElementById("cloudyBtn").addEventListener("click", () => {setupCloudy();document.querySelectorAll("#rainBtn, #snowBtn, #clearBtn, #cloudyBtn").forEach((btn) => {btn.classList.remove("ring-2", "ring-primary");});document.getElementById("cloudyBtn").classList.add("ring-2", "ring-primary");});// 时间按钮document.getElementById("dayBtn").addEventListener("click", () => {setDayTime();document.querySelectorAll("#dayBtn, #nightBtn").forEach((btn) => {btn.classList.remove("ring-2", "ring-primary");});document.getElementById("dayBtn").classList.add("ring-2", "ring-primary");});document.getElementById("nightBtn").addEventListener("click", () => {setNightTime();document.querySelectorAll("#dayBtn, #nightBtn").forEach((btn) => {btn.classList.remove("ring-2", "ring-primary");});document.getElementById("nightBtn").classList.add("ring-2", "ring-primary");});// 滑块控制const densitySlider = document.getElementById("densitySlider");const densityValue = document.getElementById("densityValue");densitySlider.addEventListener("input", (e) => {particleCount = parseInt(e.target.value);densityValue.textContent = particleCount;updateParticleDensity();});const speedSlider = document.getElementById("speedSlider");const speedValue = document.getElementById("speedValue");speedSlider.addEventListener("input", (e) => {particleSpeed = parseInt(e.target.value);speedValue.textContent = particleSpeed;// 更新雨滴速度if (isRaining && rainMesh) {const velocities = rainMesh.geometry.attributes.velocity.array;for (let i = 0; i < velocities.length; i += 3) {velocities[i + 1] = -(0.1 + Math.random() * 0.2) * particleSpeed;}rainMesh.geometry.attributes.velocity.needsUpdate = true;}// 更新雪花速度if (isSnowing && snowMesh) {const velocities = snowMesh.geometry.attributes.velocity.array;for (let i = 0; i < velocities.length; i += 3) {velocities[i] = (Math.random() - 0.5) * 0.05 * particleSpeed;velocities[i + 1] = -(0.05 + Math.random() * 0.1) * particleSpeed;velocities[i + 2] = (Math.random() - 0.5) * 0.05 * particleSpeed;}snowMesh.geometry.attributes.velocity.needsUpdate = true;}});const lightSlider = document.getElementById("lightSlider");const lightValue = document.getElementById("lightValue");lightSlider.addEventListener("input", (e) => {lightIntensity = parseFloat(e.target.value);lightValue.textContent = lightIntensity.toFixed(2);// 更新光照if (currentTime === "day") {ambientLight.intensity = lightIntensity;directionalLight.intensity = 1;} else {ambientLight.intensity = lightIntensity * 0.3;directionalLight.intensity = 0.5;}// 如果是多云天气,调整光照if (document.getElementById("cloudyBtn").classList.contains("ring-2")) {ambientLight.intensity = lightIntensity * 0.7;directionalLight.intensity = 0.7;}});// 重置按钮document.getElementById("resetBtn").addEventListener("click", () => {// 重置所有参数particleCount = 5000;particleSpeed = 10;lightIntensity = 0.5;// 更新滑块densitySlider.value = particleCount;densityValue.textContent = particleCount;speedSlider.value = particleSpeed;speedValue.textContent = particleSpeed;lightSlider.value = lightIntensity;lightValue.textContent = lightIntensity.toFixed(2);// 重置场景setupRain();setDayTime();// 更新按钮状态document.querySelectorAll("#rainBtn, #snowBtn, #clearBtn, #cloudyBtn").forEach((btn) => {btn.classList.remove("ring-2", "ring-primary");});document.getElementById("rainBtn").classList.add("ring-2", "ring-primary");document.querySelectorAll("#dayBtn, #nightBtn").forEach((btn) => {btn.classList.remove("ring-2", "ring-primary");});document.getElementById("dayBtn").classList.add("ring-2", "ring-primary");});// 初始化按钮状态document.getElementById("rainBtn").classList.add("ring-2", "ring-primary");document.getElementById("dayBtn").classList.add("ring-2", "ring-primary");}// 初始化页面window.addEventListener("DOMContentLoaded", () => {initScene();initEventListeners();});</script>
</body></html>

七、总结

本项目通过Three.js实现了以下特性:

  1. 粒子系统动态控制:通过BufferGeometry高效管理大量粒子
  2. 光照体系:环境光与平行光配合实现昼夜变化
  3. 交互设计:参数实时调整带来灵活体验

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词