关于采用Particle Shader Raycast实现粒子鼠标交互效果的技术咨询
嘿,这个需求我太熟了!直接修改几何体顶点确实会有生硬感——毕竟每次鼠标交互都要更新CPU端的顶点数据,不仅卡顿,过渡也不自然。用Shader来实现绝对是正确的方向,全程GPU处理,流畅度和效果都会提升一大截。我给你详细拆解下实现思路和关键步骤:
核心原理
Shader方案的核心是在GPU端为每个顶点/粒子实时计算与鼠标的交互偏移,不需要修改CPU端的几何体数据。通过传递鼠标的世界空间坐标到Shader,让每个顶点根据自身与鼠标的距离,平滑地调整位置(或大小、透明度),实现自然的“推开”效果。
具体实现步骤
1. 把鼠标坐标转换成世界空间坐标
Shader需要的是鼠标在3D场景中的位置,而不是屏幕像素坐标。以Three.js为例,你可以通过射线检测把鼠标的屏幕坐标转成场景中的世界坐标:
- 先将屏幕坐标转为NDC(归一化设备坐标,范围-1到1)
- 用射线与场景中的某个平面(比如地面或几何体所在平面)相交,得到鼠标的世界位置
- 把这个位置通过
uniform变量传递给Shader
2. 在顶点着色器中实现平滑偏移逻辑
这是效果自然的关键,我们要用平滑的距离衰减函数代替生硬的射线命中判断:
- 计算当前顶点到鼠标世界位置的距离
- 用
smoothstep函数生成一个平滑的偏移因子:距离鼠标越近,因子越大;超过设定的作用半径后,因子归零 - 根据偏移因子计算顶点的偏移方向(推开的话就是从鼠标指向顶点的反方向),并应用到顶点位置上
3. 绑定鼠标交互事件
在鼠标移动(或按下)事件中,实时更新Shader中的鼠标位置uniform,让效果跟随鼠标动态变化。
关键代码示例
顶点着色器(以Three.js的Points材质为例)
// 从JS传递的uniform变量 uniform vec3 mouseWorldPos; uniform float pushStrength; // 推开的力度 uniform float pushRadius; // 生效的半径 void main() { // 计算顶点与鼠标的距离 float distToMouse = distance(position, mouseWorldPos); // 生成平滑的偏移因子:在0到pushRadius之间从1衰减到0 float pushFactor = smoothstep(pushRadius, 0.0, distToMouse); // 计算推开方向:从鼠标指向顶点的反方向 vec3 pushDir = normalize(position - mouseWorldPos); // 应用偏移 vec3 transformedPos = position + pushDir * pushFactor * pushStrength; // 常规顶点着色器流程 gl_Position = projectionMatrix * modelViewMatrix * vec4(transformedPos, 1.0); // 可选:让粒子大小随距离变化,增强效果 gl_PointSize = 2.0 + pushFactor * 3.0; }
JS端鼠标坐标转换与uniform更新
// 初始化变量 const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); const plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); // 假设场景平面是z=0 const intersectionPoint = new THREE.Vector3(); // 鼠标移动事件 function handleMouseMove(event) { // 转成NDC坐标 mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; // 计算鼠标在场景中的世界位置 raycaster.setFromCamera(mouse, camera); raycaster.ray.intersectPlane(plane, intersectionPoint); // 更新Shader的uniform变量 yourMaterial.uniforms.mouseWorldPos.value.copy(intersectionPoint); } // 绑定事件 window.addEventListener('mousemove', handleMouseMove);
效果优化建议
- 平滑过渡:可以给
pushFactor加上幂运算(比如pow(pushFactor, 2.0)),让衰减曲线更平缓,避免边缘突兀 - 性能提升:如果是大量粒子,优先使用
InstancedMesh或Points材质,GPU并行处理的性能远优于CPU修改顶点 - 动态效果:可以给
pushStrength加一个随时间衰减的动画,让推开的效果有“回弹”感,更接近你参考的网站效果 - 多交互支持:如果需要触摸设备适配,只需把鼠标事件换成触摸事件,坐标转换逻辑一致
内容的提问来源于stack exchange,提问作者Salvatore
相关产品推荐
相关产品推荐

