一、前言:当数学遇上艺术 🎨
Three.js 是 WebGL 的魔术师,它能在浏览器里召唤出立方体、星系、时间感与情绪。而当它遇上"噪声粒子(Noise Particles)",我们就能做出一种仿佛会呼吸的几何动画。
这不仅仅是动画,而是一种"数据哲学" ------ 在浮点与顶点之间,每一个粒子都在询问存在的意义。
二、底层原理🌊:几何变化与噪声的"粒子哲学"
1. 几何变化(Geometry Morphing)
- 几何的顶点(vertex)是动态的向量集合,你可以理解为一群情绪不稳定的小点。
- 改变它们的坐标,就是让它们"跳舞"。
2. 噪声(Noise)
- 普通的随机数像是喝多了的鹦鹉,杂乱无章。
- 而噪声(如 Perlin Noise 或 Simplex Noise)是"带旋律的随机"------一种自然界常见的渐变模式。
通过给每个顶点一个"噪声驱动的偏移",我们就能得到有机的变化感。想象一下海浪、呼吸、风中的草。
三、技术结构:怎么让坐标"呼吸"🌬️
我们利用以下逻辑:
- 创建基础几何体,例如球体或平面;
- 在每一帧中,为每个顶点叠加一层噪声偏移;
- 通过时间参数让噪声随帧变化,从而产生动态效果;
- 同时结合粒子(PointMaterial)渲染,让几何看起来像在散发能量。
四、核心代码讲解:JS 实战 💻
ini
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { SimplexNoise } from "three/examples/jsm/math/SimplexNoise.js"; // 引入噪声
// 🌍 初始化场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x080808);
// 📷 相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 🎞️ 渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 🧭 控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 🌐 创建几何体点云
const geometry = new THREE.SphereGeometry(1, 128, 128);
const material = new THREE.PointsMaterial({
color: 0x66ccff,
size: 0.02,
});
const points = new THREE.Points(geometry, material);
scene.add(points);
// 🌪 生成噪声对象
const noise = new SimplexNoise();
// 🔄 动画逻辑
function animate() {
requestAnimationFrame(animate);
const time = performance.now() * 0.001; // 时间参数
geometry.attributes.position.needsUpdate = true; // 告诉Three.js刷新顶点
const positions = geometry.attributes.position.array;
// 🌀 每个顶点加上噪声偏移
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i];
const y = positions[i + 1];
const z = positions[i + 2];
const n = noise.noise3d(x * 0.7, y * 0.7, time * 0.3);
positions[i] = x + n * 0.05;
positions[i + 1] = y + n * 0.05;
positions[i + 2] = z + n * 0.05;
}
points.rotation.y += 0.002;
renderer.render(scene, camera);
}
animate();
// 📱 自适应
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
💡 核心思想
每一帧我们重新计算顶点位置 + 噪声干扰,让几何体轻微抖动,形似"呼吸球体"。通过 PointsMaterial 将物体转化为粒子可视化。
五、为什么噪声让动画看起来"自然"🌾
大自然不喜欢陡峭的转折。
风不会突然变向,波浪不会瞬间下沉。
噪声用于建立"连续的不确定性":
- 每个点都有随机偏移;
- 但这种随机变化是逐渐的,而非突兀;
- 结果就是------流畅、有机、具有生命感。
🎭 一句话概括:
噪声就是"让随机数装得像命运"的艺术。
六、小技巧 🌈
- 做光影交互:可以让粒子颜色随噪声变化,像极了能量在流动。
- 混合多层噪声:叠加快慢两层噪声,获得更复杂的形态。
- 用 GUI 调参:让用户通过滑杆调节噪声强度与速度,实验不同"生命频率"。
七、附录:响应式可视化布局图🌐
下面是一个 HTML 可运行版本(自动适配屏幕),直接复制运行👇
ini
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>Three.js 噪声粒子呼吸球</title>
<script src="https://cdn.jsdelivr.net/npm/three@0.161.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.161.0/examples/js/controls/OrbitControls.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.161.0/examples/js/math/SimplexNoise.js"></script>
<style>
body { margin: 0; background: #000; overflow: hidden; }
canvas { display: block; width: 100%; height: 100%; }
</style>
</head>
<body>
<script>
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000010);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
const geo = new THREE.SphereGeometry(1, 128, 128);
const mat = new THREE.PointsMaterial({ color: 0x44ccff, size: 0.02 });
const mesh = new THREE.Points(geo, mat);
scene.add(mesh);
const noise = new SimplexNoise();
function animate() {
requestAnimationFrame(animate);
const t = Date.now() * 0.001;
const pos = geo.attributes.position.array;
for (let i = 0; i < pos.length; i += 3) {
const x = pos[i], y = pos[i+1], z = pos[i+2];
const n = noise.noise3d(x*0.7, y*0.7, t*0.3);
pos[i] = x + n * 0.05;
pos[i+1] = y + n * 0.05;
pos[i+2] = z + n * 0.05;
}
geo.attributes.position.needsUpdate = true;
mesh.rotation.y += 0.002;
renderer.render(scene, camera);
}
animate();
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
八、结语:代码的呼吸 🌬️
当你看到屏幕上那一片不断律动的粒子球,请记得:
每一个顶点,都在计算与随机之间寻找平衡;
每一阵噪声,都是动态世界的轻叹。
于是,当数字开始"呼吸",我们也许就在靠近计算的艺术。
🌟 致程序员的浪漫宣言:
"我在浏览器里造了一个星球,它并不完美,但它会呼吸。" 🌐💙