无 Raycaster 的球体碰撞
原文作者:jrlazz
原文链接:https://discourse.threejs.org/t/balls-collision-without-raycaster/93553
在线演示:https://jrlazz.github.io/shaders/ballball.html

原文内容编译
原文帖非常简短,仅包含一张 ballball.html 的缩略图与一行说明:
ballball.html
collision of two balls
中文可译为:
ballball.html
两个球的碰撞
因此,该帖重点不在长篇理论,而在于给出一个不依赖 Three.js Raycaster 的球体碰撞演示。在线演示地址为:
https://jrlazz.github.io/shaders/ballball.html
说明:原帖未直接内嵌核心源码块,其核心代码位于演示页面
ballball.html中。下面保留一段根据该主题实现的等价核心检测代码,便于解读与参考。

核心代码(等价保留/提炼)
// GLSL 片段着色器:两个球体相交/碰撞检测
uniform vec4 uBallA; // xyz: 球体中心位置, w: 半径
uniform vec4 uBallB;
void main() {
vec3 delta = uBallB.xyz - uBallA.xyz;
float minDist = uBallA.w + uBallB.w;
// 使用平方距离代替 length(),避免 sqrt,适合 GPU 高并行计算
float distSq = dot(delta, delta);
float hit = step(distSq, minDist * minDist);
// hit == 1.0 表示两球碰撞/相交
// 后续可基于 hit 进行颜色混合、高亮、形变等响应
vec3 color = mix(vec3(0.1), vec3(1.0, 0.3, 0.2), hit);
gl_FragColor = vec4(color, 1.0);
}
3 个核心优化要点
-
用解析距离判断代替 Raycaster
两个球体的碰撞不需要射线与网格三角形求交,只需判断球心距离是否小于半径和,即:
distance(centerA, centerB) < rA + rB
这种方式比Raycaster的射线遍历更简单、更稳定。 -
使用平方距离避免开方运算
在 GLSL/JS 中优先比较dot(delta, delta)与(rA + rB)^2,避免sqrt()或length()带来的额外计算。对于大量球体或逐像素计算场景,这一点对性能影响明显。 -
将碰撞检测放入着色器并行执行
Raycaster通常运行在 CPU 端,而将检测逻辑放入片段着色器后,可以利用 GPU 并行性逐像素计算相交与视觉响应,减少 CPU 与 GPU 之间的数据往返。
GLSL / WebGL 专业术语检查
- Raycaster:保留为“射线投射器(Raycaster)”,是 Three.js 中用于射线拾取与碰撞检测的类。
- shaders:译为“着色器”,指运行在 GPU 上的 GLSL 程序。
- collision:译为“碰撞 / 碰撞检测”。
- ball:结合上下文译为“球体”,而非日常意义上的“球类”。
- GLSL:OpenGL Shading Language,OpenGL/WebGL 中的着色语言。
- fragment shader / vertex shader:译为“片元着色器 / 顶点着色器”。
- uniform / vec4 / vec3:GLSL 类型与变量限定符,保留英文。
- dot / step / length / sqrt:分别对应“点积 / 阶跃函数 / 长度 / 开平方”。
- WebGL:浏览器端 3D 绘图 API,保留英文。
