弹跳粒子 (Bouncing Particles)
📖 简介

弹跳粒子(Bouncing Particles)是一个实时物理模拟场景:数百万粒子被限制在圆形、椭圆或任意凸多边形边界内部运动,与边界发生弹性碰撞后沿反射方向继续飞行。粒子初始位置可以由 多种数学形状(螺旋、星形、玫瑰线、利萨如曲线等)采样生成,初始速度则支持均匀、径向、切向等方向模式。基于 GPU 上的碰撞求解与逐粒子着色,bouncing 能够呈现丝滑流畅、色彩绚丽的粒子碰撞艺术。
核心能力:
- GPU 实时物理模拟:纯 GPU 双遍渲染管线,粒子状态(位置 + 速度)存储在纹理中,支持数百万粒子流畅碰撞
- 多种边界容器:圆形、椭圆与任意凸多边形(3-64 顶点)三种容器,支持一键预设与自由拖拽编辑
- 13 种初始位置布局:从 Box、圆形、心形到利萨如曲线、超椭圆,甚至支持公式自定义粒子布局
- 6 种初始速度模式:均匀方向、径向内外、切向顺逆时针与完全随机,塑造多样的流动形态
- 逐粒子速度着色:粒子的颜色与速度相关,配合调色板呈现动态绚丽的视觉效果
- 弹性碰撞物理:基于反射定律的精确碰撞求解,每子步最多处理 4 次连续碰撞
🧮 数学背景
什么是弹跳粒子?
弹跳粒子模拟的核心是经典力学中的粒子 - 边界碰撞系统。大量互不碰撞的粒子在一个封闭的二维区域内做匀速直线运动,当粒子触及边界时,其速度按照镜面反射定律发生翻转:
其中 是边界在碰撞点处的单位外法向量。这个看似简单的规则,在圆形、椭圆和多边形等不同边界的组合下,会产生丰富多彩的轨迹图案。
粒子运动模型
每个粒子的状态由位置 和速度 组成。每帧按照时间步长 推进:
当 超出边界(且减去粒子碰撞半径 后仍然越界)时,系统会计算粒子与边界的精确交点,将粒子投影回边界内侧,并将速度沿碰撞点法线方向反射。
边界类型
| 边界类型 | 数学描述 | 碰撞求解方式 |
|---|---|---|
| 圆形 | 解析求解射线与圆的二次方程交点 | |
| 椭圆 | 在归一化坐标空间(除以半轴)中退化为圆求解 | |
| 凸多边形 | 顶点序列 围成的凸区域 | 逐边扫描检测穿出时刻 + 最近点投影回内部 |
子步与连续碰撞
为了在高速运动下保持碰撞精度,系统将每帧的时间步细分(Substeps,1-16),并在每个子步内至多处理 4 次连续碰撞(MAX_COLLISIONS_PER_SUBSTEP)。这意味着即使粒子在一帧内多次反弹(例如从尖角处高速飞出),也能获得正确的结果。
🖥️ 界面概览
所有控制项位于右侧的属性面板中,分为五个主要部分:
- Simulation(模拟):配置粒子数量、子步数、时间步长与碰撞参数
- Initial Position(初始位置):选择初始位置布局与形状参数
- Initial Velocity(初始速度):选择初始速度模式与速度参数
- Boundary(边界):配置边界容器形状、颜色与多边形顶点
- Gradient(渐变):调整渐变调色板
说明:弹跳粒子是纯实时物理模拟场景,不包含方程参数、相机、公式显示等面板。场景始终以 2D 平面渲染,录制通过配置的录制时长自动停止。
⚙️ 配置指南
1. Simulation(模拟设置)

模拟面板控制粒子系统的核心物理参数:
Particles(粒子数量):参与模拟的粒子总数
- 范围:1 - 无限(步进 1000)
- 默认值:2,000,000(约 200 万)
- 建议:
- 快速预览:100,000 - 500,000
- 实时交互:1,000,000 - 3,000,000
- 高质量渲染:5,000,000+
- 注意:粒子数越多,状态纹理越大,GPU 显存占用与计算量随之上升
Substeps(子步数):每帧将时间步细分的次数
- 范围:1 - 16
- 默认值:1
- 作用:将每帧运动拆分为多个小步推进,提高高速粒子的碰撞精度
- 建议:粒子速度快或碰撞频繁时适当增大(如 2-4),以获得更稳定的反弹轨迹
Step Size(时间步长):每个子步的物理时间步长
- 范围:0.0001 - 0.25
- 默认值:0.012
- 作用:控制粒子的单步位移。步长越大粒子每帧移动越远,碰撞越频繁
Particle Size(粒子大小):粒子的屏幕像素直径
- 范围:0.1 - 20.0
- 默认值:1.25
- 作用:仅影响视觉呈现,不影响碰撞物理。较大的粒子视觉上更醒目
Collision Radius(碰撞半径):粒子的物理碰撞半径
- 范围:0.0 - 0.25
- 默认值:0.006
- 作用:碰撞检测时边界向内收缩该半径,使粒子在贴近边界时才发生反弹。增大此值会让粒子看起来"提前"反弹
提示:Particle Size(视觉)与 Collision Radius(物理)是相互独立的参数。若希望粒子视觉较大但不影响碰撞精度,可以只增大 Particle Size。
2. Initial Position(初始位置)

初始位置决定粒子系统启动时粒子分布在何处。系统提供 13 种布局模式:
| 模式 | 中文名称 | 说明 | 特有参数 |
|---|---|---|---|
| Box | 矩形 | 在矩形区域内均匀随机分布 | X Min/Max、Y Min/Max |
| Circle | 圆形 | 在圆盘内均匀分布 | 形状中心、形状半径 |
| Heart | 心形 | 沿心形线分布 | 形状中心、形状半径 |
| Infinity | 无穷 | 沿无穷符号曲线分布 | 形状中心、形状半径 |
| Ring | 圆环 | 在内外半径之间分布 | 形状中心、形状半径、内径比 |
| Spiral | 螺旋 | 沿阿基米德螺旋分布 | 形状中心、形状半径、圈数、紧密度 |
| Star | 星形 | 沿星形轮廓分布 | 形状中心、形状半径、角数、内径比 |
| Lissajous | 利萨如 | 沿利萨如曲线分布 | 形状中心、形状半径、A、B、相位差 |
| Rose | 玫瑰线 | 沿玫瑰曲线分布 | 形状中心、形状半径、K |
| Superellipse | 超椭圆 | 沿超椭圆曲线分布 | 形状中心、形状半径、N |
| Hypocycloid | 内摆线 | 沿内摆线分布 | 形状中心、形状半径、K |
| Epicycloid | 外摆线 | 沿外摆线分布 | 形状中心、形状半径、K |
| Formula | 公式 | 使用公式自定义布局 | 形状中心、形状半径、公式 |
模式通用参数
- Shape Center X/Y(形状中心):布局形状在场景中的中心位置
- Shape Radius(形状半径):布局形状的缩放半径
形状特有参数
- Inner Ratio(内径比,Ring/Star):内圈相对外圈的半径比例,控制环的厚度或星形凹陷程度
- Points(角数,Star):星形的角数(≥ 3)
- Turns(圈数,Spiral):螺旋的旋转圈数
- Tightness(紧密度,Spiral):螺旋从中心向外扩散的紧密度
- A / B(Lissajous):利萨如曲线的频率参数
- Delta(Lissajous):利萨如曲线的相位差(角度制)
- K(Rose):玫瑰线的花瓣数
- N(Superellipse):超椭圆指数,决定形状从菱形(N=1)到圆角矩形(N=2)再到方形(N→∞)的过渡
- K(Hypocycloid/Epicycloid):内摆线/外摆线的旋转点数
公式模式
- Formula(公式):使用
x、y变量和sin、cos、tan、sqrt、abs、pow、log、exp等函数编写条件公式(如Math.sin(x*5) + Math.cos(y*3) > 0),满足条件的点被采样为粒子初始位置 - 使用
PI常量与Math.sin等 Java 风格函数语法 - 公式修改后在输入框失焦时自动生效
填充模式
非 Box 模式的形状布局支持两种填充方式:
Fill(填充):粒子分布在形状内部区域
Outline(轮廓):粒子仅分布在形状轮廓线上
Line Width(线宽):轮廓模式下粒子的分布线宽
提示:初始位置采样时,系统会自动丢弃落在边界容器之外的采样点(最多尝试 512 次),确保所有粒子都从容器内部开始运动。
3. Initial Velocity(初始速度)

初始速度决定粒子启动时的运动方向与快慢,系统提供 6 种模式:
| 模式 | 说明 |
|---|---|
| Uniform(均匀) | 所有粒子沿同一方向运动 |
| Out(径向向外) | 从速度中心向外辐射 |
| In(径向向内) | 从外向内汇聚到速度中心 |
| CW(切向顺时针) | 绕速度中心顺时针旋转 |
| CCW(切向逆时针) | 绕速度中心逆时针旋转 |
| Random(随机) | 每个粒子方向完全随机 |
速度参数
Speed(速度):粒子的初始速率(≥ 0)
- 默认值:0.2
- 作用:速度越大,粒子运动越快、碰撞越频繁
Direction(方向):仅 Uniform 模式可用
- 范围:0° - 360°
- 作用:所有粒子的统一运动方向
Vx / Vy(速度中心):仅径向/切向模式可用
- 作用:定义径向发射或旋转运动的中心点位置
Spread(角度扩散):速度方向的角度散布范围
- 范围:0° - 360°
- 作用:为粒子速度方向叠加随机扰动,0° 时所有粒子方向一致
Noise(噪声):速度方向噪声强度
- 范围:0.0 - 1.0
- 作用:在角度扩散基础上叠加更强的随机抖动,模拟湍流效果
Seed(随机种子):随机数生成种子
- 作用:相同的种子产生相同的随机分布,方便复现特定构图
4. Boundary(边界)

边界面板定义粒子被限制在其中的容器形状:
通用参数
- Shape Center X/Y(形状中心):边界容器在场景中的中心位置
- Boundary Color(边界颜色):边界描边的颜色(渲染时带半透明效果)
边界形状
系统提供四种预设形状与自由多边形编辑:
- Circle(圆形):圆形边界,可调节半径
- Ellipse(椭圆):椭圆边界,可分别调节长半轴与短半轴
- Triangle(三角形):正三角形边界,可调节外接圆半径
- Hex(六边形):正六边形边界,可调节外接圆半径
多边形编辑
除了预设形状,还可以在编辑画布上自由构建多边形边界:
- 直接拖拽画布中的顶点调整位置
- Add(添加):添加新顶点(最多 64 个)
- Reset(重置):恢复默认的六边形顶点
- 每个顶点可通过 X/Y 坐标精确输入,点击删除按钮移除顶点(最少保留 3 个)
注意:边界为多边形时,粒子碰撞基于凸多边形求解。使用预设三角形/六边形时,可通过半径滑块整体缩放。
5. Gradient(渐变调色板)
弹跳粒子使用逐粒子调色板着色:每个粒子根据其 ID 在调色板中采样颜色,并叠加速度着色,支持三种渐变模式:
Manual(手动模式):
- 手动添加、删除和调整颜色节点
- 支持拖拽重排颜色顺序
- 四种随机策略:单色、类似色、互补色、分裂互补色
Cosine(余弦模式):
- 使用 IQ 余弦调色板公式:
color(t) = a + b · cos(2π(c·t + d)) - 分别控制 R/G/B 三个通道的偏移、振幅、频率和相位
- 支持一键随机化和应用
- 使用 IQ 余弦调色板公式:
Curve(曲线模式):
- 通过可编辑的贝塞尔曲线分别控制 R/G/B 三个通道
- 提供最灵活的颜色控制
- 支持一键随机化和应用
速度着色:粒子颜色会依据其瞬时速度产生亮度变化——运动较快的粒子更亮,慢的粒子更暗,从而强化动态感。
🎬 动画与录制
弹跳粒子是实时物理模拟场景,不支持参数振荡动画,但支持视频录制:
- 实时演化:粒子持续运动与碰撞,无需时间线即可呈现动态效果
- 录制时长控制:场景通过配置中的
record_duration(秒)控制录制停止时机,运行时间达到录制时长后自动结束录制 - 时间线配置:场景的
timeline通常只包含wait类型条目,用于等待粒子运动演化
{
"timeline": [
{
"type": "wait",
"duration": 10.0,
"label": "Wait",
"easing": "SINE_IN_OUT",
"enabled": true
}
]
}🚀 性能与最佳实践
推荐配置
| 目标 | Particles | Substeps | Step Size | 碰撞半径 |
|---|---|---|---|---|
| 快速预览 | 100,000 - 500,000 | 1 | 0.01 - 0.02 | 0.001 - 0.005 |
| 实时交互 | 1,000,000 - 3,000,000 | 1-2 | 0.01 | 0.002 - 0.006 |
| 高质量渲染 | 5,000,000+ | 2-4 | 0.008 - 0.012 | 0.001 - 0.004 |
性能优化技巧
粒子数量是最关键的性能因素:
- 每帧所有粒子的碰撞求解都在 GPU 上并行完成,粒子数直接决定计算量
- 预览阶段使用低粒子数(如 20 万)快速评估布局与颜色,最终渲染时再提高
子步数与时间步长的权衡:
- 增大 Substeps 或 Step Size 会显著增加每帧计算量
- 高速粒子(Speed 大)建议增大 Substeps 保证碰撞精度,但避免过大
多边形边界的开销:
- 多边形碰撞需要逐边检测,顶点越多开销越大
- 圆形与椭圆边界使用解析求交,计算量最小
视觉与物理分离:
- 调整 Particle Size 不影响物理模拟,适合低代价改善观感
- 谨慎调节 Collision Radius,它会改变粒子的实际运动轨迹
❓ 常见问题
画面卡顿或掉帧
问题:粒子数量很大时渲染不流畅
解决方案:
- 降低 Particles 数量
- 减小 Substeps 与 Step Size
- 使用圆形/椭圆边界替代高顶点数多边形
粒子运动轨迹异常或"穿墙"
问题:高速粒子偶尔穿透边界
原因:单帧位移过大,碰撞求解未能捕获穿出时刻
解决方案:
- 增大 Substeps(如从 1 增加到 2-4)
- 减小 Step Size
- 适当增大 Collision Radius 提供缓冲
初始位置显示在边界之外
问题:部分粒子初始时出现在容器外部
原因:初始布局形状超出了容器边界
解决方案:
- 缩小 Shape Radius 使布局整体落入容器内
- 调整 Shape Center 使布局居中
- 使用与容器匹配的初始布局形状(如圆形容器搭配 Circle/Spiral 布局)
粒子全部静止不动
问题:画面没有任何运动
原因:初始速度为零或方向设置不当
解决方案:
- 增大 Speed(如 0.1 - 0.5)
- 检查初始速度模式是否合理(Random 或 Uniform + Direction)
- 检查时间步长是否过小
颜色过于单一或混乱
问题:粒子颜色缺乏层次
解决方案:
- 切换到 Cosine 或 Curve 渐变模式生成和谐配色
- 调整调色板节点数量与分布
- 速度着色会自动让快慢粒子呈现亮度差异
📐 经典示例
螺旋放射(圆形容器)
容器:Circle(半径 0.55)
初始位置:Spiral(圈数 5.5,紧密度 0.16)
初始速度:Uniform(速度 0.2)
粒子数:2,000,000,粒子大小 1.25,碰撞半径 0.006星形放射(圆形容器)
容器:Circle(半径 0.55)
初始位置:Star(角数 5,内径比 0.62)
初始速度:Radial Out(速度 0.2,噪声 0.4)
粒子数:5,000,000,粒子大小 0.1玫瑰绽放(圆形容器)
容器:Circle(半径 0.55)
初始位置:Rose(K = 7)
初始速度:Radial Out(速度 0.2,噪声 0.4)
粒子数:2,000,000,粒子大小 0.1六边形漩涡(六边形容器)
容器:Hexagon(半径 0.62)
初始位置:Circle(半径 0.16)
初始速度:Uniform(速度 0.2)
粒子数:500,000,粒子大小 0.1椭圆涡环(椭圆容器)
容器:Ellipse(长半轴 0.6,短半轴 0.55)
初始位置:Ring(内径比 0.57)
初始速度:Uniform(速度 0.2)
粒子数:2,000,000,粒子大小 1.25🖼️ 更多效果展示


🔧 技术细节
GPU 渲染管线
弹跳粒子使用纯 GPU 双遍渲染管线:
状态更新遍(Update Pass):
- 读取当前粒子状态纹理(RGBA32F,R/G = 位置,B/A = 速度)
- 按 Substeps 细分时间步,对每个子步执行边界碰撞求解(圆形/椭圆/多边形)
- 每个子步内至多处理 4 次连续碰撞(
MAX_COLLISIONS_PER_SUBSTEP) - 使用 Ping-Pong 双缓冲技术实现状态纹理交替读写
渲染遍(Render Pass):
- 将粒子位置乘以 Scale/Offset 变换后投影到屏幕
- 以
GL_POINTS方式绘制,并在片元着色器中用gl_PointCoord裁剪为圆形 - 粒子颜色按粒子 ID 从调色板采样,并依据瞬时速度叠加亮度着色
- 粒子大小(Point Size)以屏幕像素为单位
粒子状态纹理
粒子的位置与速度被打包存储在一张 texSize × texSize 的 RGBA32F 纹理中:
每个纹素存储一个粒子的完整状态(4 个 float,共 16 字节),顶点着色器通过 gl_VertexID 解码粒子的纹理坐标。
碰撞求解算法
| 边界 | 算法 |
|---|---|
| 圆形 | 将粒子运动视为射线,解析求解射线与圆(半径减去碰撞半径)的二次方程,得到精确撞击时刻与法线 |
| 椭圆 | 将坐标与位移按半轴归一化,退化为单位圆求交,再反归一化得到法线 |
| 凸多边形 | 逐边计算粒子与每条边的穿出时刻,取最早撞击边;若粒子已越出边界,则投影到最近边上并沿法线反射 |
弹性反射
所有碰撞统一遵循镜面反射公式:
速度大小在碰撞前后保持不变(完全弹性碰撞),因此粒子能量守恒,运动永不衰减。
🎨 创作技巧
布局探索
- 从预设形状开始:先在圆形容器中尝试 Circle、Spiral、Star 等基础布局
- 组合容器与布局:让布局形状与容器形状呼应(如六边形容器 + 星形布局)
- 使用公式布局:熟悉基本模式后,编写
Math.sin(x*5) + Math.cos(y*3) > 0之类公式构建独一无二的粒子图案
动态表现
- 速度模式塑造运动:Radial Out 呈现放射爆发,CW/CCW 呈现漩涡旋转,Uniform 呈现整齐流动
- 速度中心改变焦点:将速度中心偏离容器中心,可以创造出偏心旋转的动态美感
- 噪声与扩散:增大 Spread/Noise 让运动更狂野,保持 0 则获得纯净有序的轨迹
视觉调优
- 粒子大小与密度:小粒子(0.1)+ 高粒子数呈现细腻的沙粒质感,大粒子(1.25+)更醒目
- 速度着色:利用粒子速度带来的亮度差异强化运动方向感
- 边界颜色:深色背景 + 明亮边界 + 高饱和调色板是经典搭配
⚠️ 注意事项
GPU 显存:
- 粒子状态纹理大小为 ⌈√N⌉ × ⌈√N⌉,采用 RGBA32F 格式,每粒子占用 16 字节
- 例如 500 万粒子需要约 80 MB 显存存储状态纹理
子步数与性能:
- Substeps 每增大 1 倍,Update Pass 计算量近似翻倍
- 高粒子数场景建议保持 Substeps 为 1-2
多边形边界限制:
- 顶点数量限制为 3-64 个
- 边界必须是凸多边形,凹多边形会导致碰撞异常
粒子互不碰撞:
- 粒子之间不会相互碰撞,仅与边界发生碰撞
- 这是刻意设计,以保持系统的确定性并聚焦于边界交互的视觉效果
📚 参考资源
数学理论
- Wikipedia: Specular Reflection — 镜面反射的数学原理
- Wikipedia: Elastic Collision — 弹性碰撞
- Wikipedia: Lissajous Curve — 利萨如曲线
- Wikipedia: Rose (mathematics) — 玫瑰线
计算机图形学
- Ray–sphere intersection — 射线与球(圆)求交
- Point-in-polygon — 点在多边形内判定
- OpenGL 着色器编程指南
- GLSL 语言规范
