如何在React Three Fiber中实现粒子气泡物理效果?是否需自定义顶点着色器?
React Three Fiber粒子鼠标交互实现方案
核心结论
是的,要实现这种鼠标靠近粒子时反向移动、远离时回归的气泡效果,必须用到自定义顶点着色器——因为这种基于粒子与鼠标距离的实时位置偏移计算,没法用Three.js内置材质或组件直接实现,需要在GPU层面逐顶点处理交互逻辑。
实现思路拆解
- 传递鼠标位置到着色器:在React Three Fiber中,通过
useFrame钩子实时获取鼠标的归一化坐标(或世界坐标),并将其作为uniform变量传递给着色器。 - 顶点着色器中计算偏移:
- 保存粒子初始位置:在几何体的
attributes里添加initialPosition属性,初始化时复制顶点原始位置。 - 计算距离与偏移:算出每个粒子到鼠标的距离,距离越近偏移幅度越大,方向与鼠标指向粒子的方向相反。
- 加入缓动效果:用
mix函数或简单弹簧公式,让粒子回归原位时呈现气泡般的平滑运动。
- 保存粒子初始位置:在几何体的
- CPU辅助优化(可选):如果粒子数量极大,可在CPU端先筛选鼠标附近的粒子,只对这些粒子传递偏移数据,减少GPU计算压力,基础实现用纯着色器即可。
简化代码示例(核心部分)
自定义着色器材质
import { useRef, useFrame } from '@react-three/fiber' import { Points, BufferGeometry } from 'three' function BubbleParticles() { const meshRef = useRef() const mouse = useRef([0, 0]) // 实时更新鼠标位置 useFrame((state) => { mouse.current = [state.mouse.x, state.mouse.y] if (meshRef.current) { meshRef.current.material.uniforms.uMouse.value.set(mouse.current[0], mouse.current[1], 0) meshRef.current.material.uniforms.uTime.value = state.clock.getElapsedTime() } }) // 创建几何体并添加初始位置属性 const positions = [] for (let i = 0; i < 1000; i++) { positions.push(Math.random() * 10 - 5, Math.random() * 10 - 5, Math.random() * 10 - 5) } const geometry = new BufferGeometry() geometry.setAttribute('position', new Float32BufferAttribute(positions, 3)) geometry.setAttribute('initialPosition', geometry.attributes.position.clone()) return ( <Points ref={meshRef}> <shaderMaterial uniforms={{ uMouse: { value: new THREE.Vector3() }, uTime: { value: 0 }, uStrength: { value: 2 }, // 控制偏移强度 uMaxDistance: { value: 3 } // 鼠标影响的最大距离 }} vertexShader={` attribute vec3 initialPosition; uniform vec3 uMouse; uniform float uTime; uniform float uStrength; uniform float uMaxDistance; void main() { // 计算粒子到鼠标的向量与距离 vec3 toMouse = uMouse - initialPosition; float distance = length(toMouse); // 根据距离计算偏移量,超出最大距离则无偏移 float influence = smoothstep(uMaxDistance, 0.0, distance); vec3 offset = normalize(-toMouse) * uStrength * influence; // 加入弹性回归效果,模拟气泡回弹 vec3 finalPosition = mix(initialPosition + offset, initialPosition, pow(influence, 0.3)); gl_Position = projectionMatrix * modelViewMatrix * vec4(finalPosition, 1.0); gl_PointSize = 2.0; } `} fragmentShader={` void main() { gl_FragColor = vec4(0.2, 0.6, 1.0, 0.8); } `} /> </Points> ) }
关键注意点
- 鼠标坐标要和粒子处于同一空间:示例用归一化坐标,若粒子在世界空间,需将鼠标坐标转换为世界坐标。
- 调整
uStrength和uMaxDistance可控制气泡效果的灵敏度和影响范围。 - 若要更真实的物理感,可在顶点着色器中加入弹簧公式(如
F = -k*x),结合时间变量计算速度和位置。
内容的提问来源于stack exchange,提问作者Felipe Oliveira
相关产品推荐
相关产品推荐

