)
Three.js TSL实战5分钟打造酷炫粒子鼠标跟随效果附完整代码在Web 3D开发领域Three.js一直是前端工程师的首选工具库。而随着WebGPU技术的普及Three.js团队推出的TSLThree.js Shading Language为开发者提供了更高效的着色器编写方式。本文将带你快速实现一个令人惊艳的粒子鼠标跟随效果不仅视觉效果出众代码也极具复用价值。1. 环境准备与基础配置首先确保你的开发环境已经准备好。我们需要使用Three.js的最新版本r176它原生支持TSL语法。创建一个基础的HTML文件并添加以下依赖!DOCTYPE html html head meta charsetUTF-8 titleTSL粒子效果/title style body { margin: 0; overflow: hidden; } canvas { display: block; } /style /head body script typeimportmap { imports: { three: https://cdn.jsdelivr.net/npm/three0.176.0/build/three.module.js, three/tsl: https://cdn.jsdelivr.net/npm/three0.176.0/build/three.tsl.js, three/addons/: https://cdn.jsdelivr.net/npm/three0.176.0/examples/jsm/ } } /script script typemodule srcapp.js/script /body /html提示使用importmap可以避免路径问题确保所有依赖都能正确加载。接下来创建app.js文件初始化基础场景import * as THREE from three; import { Fn, If, uniform, float, vec3, instancedArray, instanceIndex } from three/tsl; import { OrbitControls } from three/addons/controls/OrbitControls.js; // 初始化场景、相机和渲染器 const scene new THREE.Scene(); const camera new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); 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); camera.position.set(0, 0, 50); controls.update();2. 创建粒子系统核心逻辑TSL的强大之处在于可以用JavaScript风格的语法编写着色器代码。我们将创建50万个粒子它们会对鼠标移动做出反应。// 粒子数量 - 50万 const PARTICLE_COUNT 500000; // 创建粒子位置和速度的实例化数组 const positions instancedArray(PARTICLE_COUNT, vec3); const velocities instancedArray(PARTICLE_COUNT, vec3); const colors instancedArray(PARTICLE_COUNT, vec3); // 初始化粒子位置 const initParticles Fn(() { const position positions.element(instanceIndex); const color colors.element(instanceIndex); // 在球面上随机分布粒子 const radius float(20).mul(hash(instanceIndex.add(1))); const theta hash(instanceIndex).mul(Math.PI * 2); const phi hash(instanceIndex.add(2)).mul(Math.PI); position.x radius.mul(theta.sin()).mul(phi.cos()); position.y radius.mul(phi.sin()); position.z radius.mul(theta.cos()).mul(phi.cos()); // 随机颜色 color.assign(vec3( hash(instanceIndex), hash(instanceIndex.add(3)), hash(instanceIndex.add(5)) )); })().compute(PARTICLE_COUNT);粒子物理更新逻辑是效果的核心我们使用TSL编写// 物理参数 const gravity uniform(-0.0005); const bounce uniform(0.7); const friction uniform(0.98); const mouseForce uniform(5); const mousePosition uniform(new THREE.Vector3(0, 0, 0)); // 粒子更新函数 const updateParticles Fn(() { const position positions.element(instanceIndex); const velocity velocities.element(instanceIndex); // 应用重力 velocity.y velocity.y.add(gravity); // 鼠标吸引力 const direction position.sub(mousePosition).normalize(); const distance position.distance(mousePosition); const force float(1).div(distance.add(0.1)).mul(mouseForce); velocity.x velocity.x.add(direction.x.mul(force)); velocity.y velocity.y.add(direction.y.mul(force)); velocity.z velocity.z.add(direction.z.mul(force)); // 更新位置 position.addAssign(velocity); velocity.mulAssign(friction); // 地面碰撞检测 If(position.y.lessThan(-20), () { position.y -20; velocity.y velocity.y.negate().mul(bounce); velocity.x velocity.x.mul(0.9); velocity.z velocity.z.mul(0.9); }); })().compute(PARTICLE_COUNT);3. 渲染粒子与鼠标交互现在我们需要将计算好的粒子渲染到屏幕上并添加鼠标交互// 创建粒子材质 const particleMaterial new THREE.PointsMaterial({ size: 0.1, vertexColors: true, transparent: true, opacity: 0.8, blending: THREE.AdditiveBlending }); // 创建粒子几何体 const particleGeometry new THREE.BufferGeometry(); particleGeometry.setAttribute(position, positions.toAttribute()); particleGeometry.setAttribute(color, colors.toAttribute()); // 创建粒子系统 const particles new THREE.Points(particleGeometry, particleMaterial); scene.add(particles); // 鼠标移动交互 const raycaster new THREE.Raycaster(); const mouse new THREE.Vector2(); function onMouseMove(event) { // 将鼠标坐标归一化为-1到1的范围 mouse.x (event.clientX / window.innerWidth) * 2 - 1; mouse.y -(event.clientY / window.innerHeight) * 2 1; // 更新射线投射器 raycaster.setFromCamera(mouse, camera); // 计算鼠标在3D空间中的位置 const intersects raycaster.intersectObjects([new THREE.Mesh( new THREE.PlaneGeometry(1000, 1000), new THREE.MeshBasicMaterial({ visible: false }) )]); if (intersects.length 0) { mousePosition.value.copy(intersects[0].point); mousePosition.value.z 0; // 限制在XY平面 } } window.addEventListener(mousemove, onMouseMove);4. 动画循环与性能优化最后设置动画循环确保粒子系统流畅运行function animate() { requestAnimationFrame(animate); // 更新粒子位置 renderer.compute(updateParticles); // 更新几何体属性 particleGeometry.attributes.position.needsUpdate true; // 渲染场景 renderer.render(scene, camera); controls.update(); } // 处理窗口大小变化 function onWindowResize() { camera.aspect window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } window.addEventListener(resize, onWindowResize); animate();注意对于大量粒子建议使用WebGPU渲染器以获得更好性能。只需将WebGLRenderer替换为WebGPURenderer即可。5. 进阶效果与自定义基础效果实现后你可以通过调整参数获得不同视觉效果参数调优表参数默认值效果说明gravity-0.0005重力大小负值表示向下bounce0.7碰撞反弹系数friction0.98摩擦力值越小减速越快mouseForce5鼠标吸引力强度particleSize0.1粒子显示大小尝试修改这些值观察效果变化。例如增加mouseForce会让粒子对鼠标移动更敏感// 更强的鼠标交互 mouseForce.value 10;你还可以添加更多视觉效果如// 添加辉光效果 import { UnrealBloomPass } from three/addons/postprocessing/UnrealBloomPass.js; import { EffectComposer } from three/addons/postprocessing/EffectComposer.js; const composer new EffectComposer(renderer); composer.addPass(new UnrealBloomPass( new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85 )); // 在animate()中替换renderer.render为composer.render通过组合不同的参数和后期处理效果你可以创造出独一无二的粒子动画。在实际项目中这种技术可应用于数据可视化、游戏特效或网页背景等多个领域。