12.游戏引擎中的粒子和声效系统
12.1 粒子基础
在游戏特效领域,粒子系统(Particle System)是不可或缺的核心技术。游戏中的爆炸、烟雾、火焰等视觉效果,主要依靠粒子系统实现。在科幻类游戏中,粒子系统几乎决定了整个游戏的视觉质感,打击感和氛围感很大程度上依赖于此。

粒子系统最早起源于电影行业。1982年的《星际迷航2:可汗之怒》首次应用了这一概念,随后威廉·里夫斯(William Reeves)在1983年的论文中正式定义了粒子系统:一种用大量微小粒子来表示模糊物体的技术,这些粒子在系统中生成、移动、变化,最后消失。
粒子的基本概念
粒子系统的基础是粒子(Particle)。粒子是空间中的一个小点或小面片,具有基本的物理属性。

每个粒子通常包含以下属性:
- 位置(Position):用三维坐标
p = (x, y, z)表示 - 速度(Velocity):运动速度,用向量
v表示 - 尺寸(Size):粒子的大小
- 颜色(Color):粒子的颜色
- 生命周期(Lifetime):粒子已存在的时间长度
例如,锤子敲击时产生的火花,每个火花就是一个粒子。这一概念较为直观。
粒子生命周期
每个粒子都有自己的生命周期(Life Cycle),从生成到死亡,整个过程包括几个阶段。

粒子的生命周期大致包括:
- 生成(Spawn):粒子被创建出来
- 老化(Aging):粒子的各种属性随时间变化,比如位置、速度、颜色、尺寸等
- 环境交互(React to environment):粒子与环境互动,受重力、风力影响,或者与障碍物碰撞
- 死亡(Death):生命周期结束,粒子被系统回收
生命周期管理至关重要。如果粒子只生成不死亡,场景中的粒子会不断累积,性能会急剧下降。实际开发中,生命周期管理不当会导致卡顿甚至崩溃。这是粒子系统最基础也最重要的功能。
粒子发射器
粒子发射器(Particle Emitter)是生成粒子的装置。粒子从发射器持续生成,每个粒子具有独立的属性,形成各自的运动轨迹。

发射器主要执行以下功能:
- 定义生成规则(Specify generation rules):决定粒子何时、在何处生成
- 定义模拟逻辑(Specify simulation logic):为粒子赋予相应的物理仿真逻辑
- 定义渲染方式(Describe how to render particles):决定粒子的渲染方式
发射器为粒子赋予随机的初始值。例如制作喷泉效果时,粒子的初速度在大小和方向上都需要随机性,以获得真实的视觉效果。
粒子系统
多个发射器组合形成粒子系统(Particle System)。更准确地说,粒子系统是发射器及其生成的所有活跃粒子的总和。

真实的效果往往需要多种不同的粒子组合。以火焰效果为例:
- 火焰粒子:火焰本身,从根部产生,中间最亮,向上逐渐变暗
- 火星粒子:飞溅的火星,具有不同的运动轨迹
- 烟雾粒子:上升的烟雾
这三种粒子组合在一起,才能产生真实的火焰效果。游戏中的特效基本都通过这种方式组合实现。
再以弹药击中石头爆炸的效果为例:
- 击中瞬间的冲击烟尘,向四周扩散
- 四散炸开的碎片
- 各种小碎片(Debris)
特效艺术家(Effect Artist)的核心工作是研究如何组合这些系统。通过观察真实世界的效果,用多个简单系统的组合来模拟,这是粒子系统的核心价值所在。
粒子生成位置
粒子系统可以根据需求选择不同的生成策略,发射器可以定义不同的生成位置。

常见的生成位置包括:
- 单点生成(Single Point Generation):从单个点生成,最常用
- 基于区域生成(Region-based Generation):在指定区域内生成,比如Box、Sphere或者Capsule空间
- 基于网格生成(Grid-based Generation):可以在Mesh上采样,例如在角色身上随机采样点来生成粒子,能够实现丰富的视觉效果
粒子系统对游戏和影视特效都很重要,质量直接影响视觉效果的真实感。高质量的粒子系统能够产生真实的视觉效果,而低质量的粒子系统则容易暴露为简单的面片动画。这需要特效艺术家具备较高的技术水平。
粒子生成模式
粒子生成器可以设置不同的生成模式,控制粒子的生成时机和频率。

主要有两种模式:
连续模式(Continuous):
- 每帧可以改变生成速率
- 基于时间、距离等参数
- 持续不断地生成粒子,比如喷泉效果
爆发模式(Burst):
- 所有粒子同时生成
- 到某个时间点突然爆发,一次性生成大量粒子
- 适合爆炸、冲击这种瞬间效果
根据所需效果,在发射器中设置相应的行为模式即可。
粒子模拟
粒子模拟:作用力
发射器生成粒子后,粒子在空间中的所有行为就是模拟(Simulate)。模拟首先要考虑粒子受到的各种力。

常见的力有这些:
重力(Gravity):
\[
\vec{f} = m\vec{g}
\]m是粒子质量,g是重力加速度。重力让粒子自然下落。粘性阻力(Drag Force):
\[
\vec{f} = -k_d \vec{v}
\]k_d是阻尼系数,v是速度。阻力与速度方向相反,让粒子逐渐减速。风场(Wind Field):
\[
\vec{f} = k \vec{v}_{wind}
\]k是风力系数,v_wind是风速。比如《对马岛之魂》里的”信仰之风”,就是让粒子沿着风向移动来指引方向。
这三种力是粒子最常用的。
粒子模拟:运动更新
粒子模拟和物理系统关系很密切,每个粒子可以看作一个极小的物理质点。

实际应用中,粒子系统不需要严格遵循物理规律,通常用最简单的显式积分(Explicit Integration)来更新粒子状态。
每一帧根据当前速度和受到的力,计算下一帧的速度和位置:
用加速度更新速度:
\[
\vec{v}(t + \Delta t) = \vec{v}(t) + \vec{a}(t) \Delta t
\]用速度更新位置:
\[
\vec{x}(t + \Delta t) = \vec{x}(t) + \vec{v}(t + \Delta t) \Delta t
\]
通过逐帧计算,能够实现喷泉、爆炸等各种效果。虽然算法简单,但效果已经足够真实。
粒子模拟:参数变化
模拟过程中,除了更新位置和速度,还要调整其他各种参数。

常见的参数变化包括:
- 旋转(Rotation):让粒子旋转,增强真实感
- 颜色变化(Color Change):颜色在生命周期中持续变化
- 尺寸变化(Size Change):尺寸随时间变化
例如制作火焰效果时,粒子刚生成时可能是暗红色,中间温度最高时变亮,随着高度增加和温度损失,逐渐变暗。这种颜色变化能够产生真实的火焰视觉效果。
从物理角度分析,火焰在不同高度的颜色确实不同。根据黑体辐射理论,不同温度的辐射体具有不同的颜色特征。

再比如烟雾效果,粒子初始时浓密且体积小,随时间推移逐渐扩散变大,颜色变淡,但覆盖范围越来越大。
这些参数变化是模拟过程中最重要的计算。早期粒子系统计算相对简单,预设参数后由艺术家调整即可。现代粒子系统已经变得相当复杂。
粒子模拟:碰撞
更复杂的情况是,粒子需要与环境交互。粒子遇到障碍物时应该被反弹或阻挡。

粒子碰撞的挑战在于性能。如果直接调用物理系统,计算开销会非常大。一个系统可能有几百甚至几千个粒子,需要高效的碰撞检测算法。
对整个世界的碰撞检测也是系统的重要要求。这个模拟过程就构成了整个粒子的生命周期管理。
粒子类型
粒子类型:广告牌粒子
粒子生成后不是抽象的质点,通常有不同的形态。

广告牌粒子(Billboard Particles)是最常用的:
- 每个粒子都是一个精灵(sprite)
- 呈现为三维形态
- 始终面向摄像机
广告牌粒子是始终朝向摄像机的面片,无论摄像机如何旋转,它都会跟随旋转。这种设计之所以有效,是因为真实粒子效果中,每个粒子的颜色和外观都在持续变化,这种变化会吸引注意力,使观察者不易注意到粒子始终朝向摄像机。
设计时需要注意:如果粒子是静态图像,Alpha和内容不变,观察者很容易看出是始终朝向摄像机的billboard。
因此:
- 粒子尺寸较小时,使用简单的颜色和纹理即可
- 粒子尺寸较大时,建议使用动画纹理(Animated Texture),让外观随时间变化,这样观察者就不会注意到是用多个小面片模拟体积感
广告牌粒子是粒子系统中最经典也最古老的形式,早期的粒子系统主要基于此。
粒子类型:网格粒子
随着游戏复杂度提升,广告牌粒子就不够用了。比如表现各种小碎屑时,需要模拟大量的小碎片、金属颗粒等。

网格粒子(Mesh Particles):
- 每个粒子都是一个三维模型
- 可以用来模拟岩石、碎屑等有明显几何信息的颗粒
- 比如爆炸产生的岩石
随机性很重要。最简单的随机化方法是:如果无法创建大量不同的mesh,可以对每个mesh的Transform进行角度旋转、大小缩放,甚至在不同轴向上随机缩放,产生差异化效果。
设计粒子系统时,通常追求随机感,避免重复感。在引擎层面,最重要的是为艺术家提供各种可随机化、可调整的参数。艺术家通常希望所有变量都支持随机化,以获得自然真实的效果。
粒子类型:带状粒子
第三种是带状粒子(Ribbon Particles),也称为轨迹粒子(Trail Particles)。虽然提及较少,但在游戏中应用广泛。

带状粒子会拖出一条光带。例如游戏中发射一支带魔法效果的箭,箭的尾迹在空中形成一条抛物线状的丝带。多个这样的效果同时出现,形成多条带状轨迹。这是ribbon particle的典型应用。

带状粒子的核心机制是:粒子在飞行过程中不断生成额外的控制节点,这些节点以一定宽度连接在一起,形成带状效果。
带状粒子在表现轨迹、刀剑划过的弧线等效果中很有用。游戏中表现时空滞留感时,例如发射气功弹、气功波,带状粒子是典型应用。
带状粒子存在一个问题:如果只是简单地把离散点连起来,会产生折线状的轨迹。

问题很明显:没有平滑形状,存在尖锐的棱角。

因此实现ribbon particle时,通常使用样条曲线插值(Spline Curve Interpolation)。

使用向心Catmull-Rom插值实现平滑:
- 在粒子之间添加额外的片段
- 可以设置分段数量
- 需要更多CPU开销
选择Catmull-Rom曲线的原因:首先计算简单,它是二阶或三阶的参数化公式,可以直接插值。其次严格保证曲线通过所有控制点,在控制点之间插入中间点,形成平滑过渡,使整个特效看起来更圆滑。
设计游戏引擎的粒子系统时,通常需要提供这三类粒子:billboard、网格和带状粒子。同时加入尽可能多的随机扰动参数。实现ribbon particle时,必须使用Catmull-Rom插值曲线,确保插值后的形状足够平滑。
通过以上方式可以构建一个基础的粒子系统。
12.2 粒子渲染
粒子系统在渲染时会遇到两个大问题:排序(Sorting)和性能(Performance)。透明物体的排序和渲染在游戏引擎里本来就需要特别小心,粒子系统因为特殊性,让这些问题变得更复杂也更关键。
Alpha混合顺序
透明物体渲染必须按正确的顺序绘制。根据Alpha混合(Alpha Blending)理论,需要从最远的地方开始,由远及近地绘制。

Alpha混合公式:
\[
C_f = \alpha_1 C_1 + (1 - \alpha_1) C_0
\]
其中:
C_f:最终颜色(Final color)C_1:源颜色(Source color)C_0:目标颜色(Destination color)α₁:源颜色的Alpha值
排序很重要。如果透明物体绘制顺序不正确,Alpha混合结果就会出错。排序正确时,透明物体能正确层叠混合;排序错误时,混合结果会很不自然,容易察觉。
场景中只有少量透明物体(例如玻璃墙)时,排序问题相对容易解决。但粒子系统在游戏中可能同时产生几百甚至上千个billboard粒子,如何正确排序这些粒子的前后关系成为关键问题。
粒子排序方法
场景里往往同时存在大量粒子,排序会消耗很多计算资源。主要有两种排序方法:全局排序(Global Sorting)和按发射器排序(Sort by Emitter)。

排序模式(Sorting Mode):
全局排序(Global):
- 精确(Precise):能够获得正确的排序结果
- 性能消耗较大(High performance consumption):需要非常多的计算资源
层级结构(Hierarchical structure):
- 按系统划分(Divided by system)
- 按发射器划分(Divided by emitter)
- 发射器内部(Inside emitter)
排序规则(Sorting Rules):
- 粒子之间(Between particles):基于粒子与摄像机的距离
- 系统间或发射器间(Between systems or emitters):使用包围盒(Bounding box)
全局排序
全局排序(Global Sorting)不考虑发射器的信息,单纯对所有粒子进行统一排序。

全局排序的特点:
- 把所有粒子系统的粒子合并到一起排序
- 比如系统A产生50个粒子,系统B产生100个,系统C产生70个,把这220个粒子全部合并排序
- 即使不同发射器的粒子有交叠,也能按正确顺序排列
- 排序结果正确,但计算成本高
全局排序的问题:
- 排序成本随元素数量增加:参与排序的元素越多,计算成本越高
- 绘制状态切换成本:不同粒子系统绘制时,很多参数设置需要保持一致,频繁切换绘制状态会增加成本
按发射器排序
按发射器排序(Sort by Emitter)按照发射器为单位进行排序,可以极大地减少计算资源,但可能会出现错误的排序结果。

按发射器排序的特点:
- 先确定发射器的前后顺序,再在发射器内部对粒子排序
- 计算成本低,适合性能敏感的场景
- 可能出现排序错误
按发射器排序的问题
当两个发射器靠得很近时,会出现排序错误。比如真实情况下绿色粒子和红色粒子有大量交叠,但因为绿色发射器的包围盒可能更靠近相机,所有绿色粒子都会被渲染在前面,看起来很不自然。
这是粒子系统里很常见的问题。早期游戏里,相机移动时烟柱等特效会突然闪烁,通常就是粒子排序出了问题。
排序错误的典型案例
带状粒子(Ribbon Particle)如果用按发射器排序,可能会有严重问题。比如一个带状粒子应该穿过其他粒子的中间,但因为它的包围盒总是最靠近相机,会被排序到最后,导致带状效果浮在所有烟雾之上。用全局排序时,把带状粒子的每一节和其他粒子一起排序,就能得到正确结果。
排序方法的选择
对于游戏引擎开发者,特别是入门阶段,建议先实现按发射器排序,因为全局排序实现起来比较复杂,而且后续会涉及GPU上更复杂的算法。按发射器排序能解决部分问题,但如果不解决排序问题,后果会很严重。
粒子渲染的性能挑战
粒子系统看似简单,只是一些粒子在移动,但它往往是游戏的性能瓶颈。
不透明物体的渲染:
绘制不透明物体时,无论场景多复杂,只需要绘制最后一个像素。视线穿过场景时,可能看到很复杂的几何层次,但Z-Buffer(深度缓冲)会把所有后面的物体都滤掉,只保留最靠近眼睛的点,然后着色。屏幕上的所有像素,每个只需要着色一次。
透明物体的渲染:
绘制透明物体时,情况完全不同。如果透明物体来自环境,一根视线看过去,最多也就看到七八个透明物。但粒子系统完全不同,它可能在同一像素上产生大量的Overdraw(过度绘制)。
Overdraw问题:
例如面前有一个烟雾爆炸效果,可能在眼前迅速产生几十个甚至上百个覆盖整个屏幕大小的粒子。一个屏幕大小的粒子在4K分辨率下就是约800万个像素点。如果同时存在几十个甚至上百个这样的粒子,计算量级达到数亿甚至数十亿,游戏帧率会迅速下降。
因此粒子系统经常导致游戏帧率显著下降。
低分辨率渲染技术
现代游戏里,基本都会用半分辨率(Half Resolution)技术来解决粒子渲染的性能问题。

全分辨率粒子渲染的问题:
当粒子充满场景时,半透明粒子会导致必须在同一像素上反复绘制,这往往会导致帧数大幅下降。最坏情况是粒子充满整个屏幕。

低分辨率粒子渲染的解决方案:
渲染流程:
渲染场景(Render scene):
- 生成场景颜色(Scene Color)
- 生成场景深度(Scene Depth)
降采样(Downsampling):
- 把场景深度降采样到半分辨率(Half-Res Depth)
- XY方向都缩小一半,总像素量减少四倍
离屏渲染粒子(Render off-screen particle):
- 用半分辨率深度缓冲渲染粒子
- 生成粒子颜色(Particle Color RGB)
- 生成粒子Alpha(Particle Alpha)
双边上采样(Bilateral upsampling):
- 把低分辨率的粒子数据上采样到全分辨率
- 用场景深度作为指导,确保准确的放置和混合
最终混合(Final blending):
- 把粒子与场景进行Alpha混合
混合公式:
\[
result_color = dst_color \cdot (1 - src_alpha) + src_color \cdot src_alpha
\]
\[
result_alpha = dst_alpha \cdot (1 - src_alpha)
\]
技术细节:
- 深度测试:如果粒子的Z值在已经绘制的深度之后,就不需要绘制
- Alpha累积:所有粒子的透明物计算结果需要集体计算一个Alpha值
- 混合过程:Alpha值与前面的不透明物体混合,混合过程是一个上采样的过程
- 上采样优化:通过简单的插值、变化和扰动,能得到很不错的效果
半分辨率技术的优势:
游戏引擎里,现在越来越多的渲染开始用半分辨率技术。随着显示器分辨率越来越高:
- 1080P:约200万像素
- 4K:约800万像素
- 8K:像素量特别大
即使在当前高性能GPU上,也无法实现全尺寸渲染,因此必须使用降采样和上采样的方法。这涉及到DLSS(Deep Learning Super Sampling)、FSR(FidelityFX Super Resolution)等技术,都是使用低精度绘制,然后通过算法恢复到高分辨率。这是当前渲染中常用的技术。
性能优化建议:
实现粒子系统时,需要特别注意性能问题。粒子系统容易出现看似简单的效果突然导致游戏帧率下降的情况。还有一些更复杂的优化算法,例如:
- 粒子离相机特别近时,可以裁剪掉一些粒子
- 使用其他方法降低在同一像素点上重复绘制的粒子数量
这也是粒子系统的重要功能。
模拟效率的影响
除了绘制问题,粒子系统的模拟(Simulation)对效率影响也特别大。
现代游戏中,一个场景中很容易达到几万甚至上十万的粒子数量,才能产生所需的效果。如此大量的粒子,每一帧都需要各种计算处理,对CPU造成很大的负载。
总结
粒子渲染是粒子系统里最具挑战性的部分之一,主要面临这些问题:
排序挑战:
- 全局排序:结果正确但计算成本高
- 按发射器排序:计算成本低但可能出现错误
- 需要根据实际需求选择合适的排序方法
性能挑战:
- Overdraw问题:大量粒子导致同一像素被反复绘制
- 计算量级巨大:可能达到数亿甚至数十亿的计算量
优化方案:
- 低分辨率渲染:用半分辨率技术减少像素数量
- 降采样和上采样:通过双边上采样恢复原始分辨率
- 深度测试和Alpha累积:优化渲染流程
模拟效率:
- 大量粒子的模拟计算对CPU造成很大负载
- 需要优化算法和数据结构来提高效率
12.3 GPU粒子
现代游戏场景中,粒子数量很容易达到几万甚至上十万,每一帧都需要进行各种计算处理,对CPU来说是个很大的负载。粒子系统天然具有并行特性,非常适合用GPU进行计算。现代游戏基本都用GPU来实现粒子系统的仿真,不仅能节约CPU资源,还能加速整个渲染流程。

为什么使用GPU
使用GPU处理粒子系统有几个明显优势:
- 高度并行的工作负载(Highly parallel workload):GPU有大量并行处理器,非常适合模拟大量粒子
- 释放CPU资源(Release CPU resources):把粒子计算移到GPU,CPU可以专注于运行游戏逻辑
- 易于访问深度缓冲区(Easy access to depth buffer):GPU上可以直接访问深度缓冲进行碰撞检测,不需要从CPU读取
GPU粒子系统框架

计算着色器(Compute Shader)提供了高速通用计算能力,能充分利用GPU上大量并行处理器的优势。
GPU粒子系统的核心流程:
- 降采样深度纹理(Down-sampled depth texture):从上一帧的场景深度中生成,用于碰撞检测
- 粒子处理(在计算着色器中执行):
- 生成粒子(Spawn particles)
- 模拟(Simulate):使用深度纹理进行碰撞检测
- 排序粒子(Sort particles)
- 多视图处理:支持多个视图(View 0、View 1等)
- 渲染流程(以View 0为例):
- 渲染场景(Render scene):生成场景颜色和深度
- 降采样半分辨率深度纹理:用于后续碰撞查询
- 渲染不透明粒子:使用深度缓冲进行碰撞检测
- 渲染透明粒子:需要正确排序
这个流程形成了一个反馈循环:渲染场景生成的深度纹理会用于下一帧的粒子模拟。
粒子列表管理
GPU粒子系统通过维护几个列表来管理粒子的生命周期。核心思想是用粒子池(Particle Pool)存储所有粒子数据,用死亡列表(Dead List)和存活列表(Alive List)来管理粒子的状态。
初始状态

系统初始化时:
- 粒子池(Particle Pool):包含所有粒子数据的单一缓冲区,假设最多容纳8个粒子(实际可能是几万到上十万)
- 死亡列表(Dead List):初始状态下,所有8个槽位都处于可用的DEAD状态,包含索引[0, 1, 2, 3, 4, 5, 6, 7],
dead_count=8 - 存活列表0(Alive List 0):上一帧的存活列表,初始为空,
alive_count_0=0
粒子池是一个包含所有粒子数据的单一缓冲区存储,包括位置、速度、颜色、尺寸等所有属性。
生成粒子

当发射器需要生成新粒子时:
- 调度计算着色器线程:假设生成5个粒子,就调度5个计算着色器线程执行生成计算
- 从死亡列表取粒子:从死亡列表尾部取出5个空位(索引3, 4, 5, 6, 7)
- 填充粒子数据:将粒子数据写入粒子池的对应位置
- 更新列表:将取出的索引写入当前帧的存活列表,死亡列表剩余3个索引[0, 1, 2],
dead_count=3,alive_count_0=5
关键点:对死亡列表和存活列表的访问需要保证原子性(Atomicity)。计算着色器提供了原子操作,可以保证多线程并发访问时的数据一致性。
模拟和剔除

模拟阶段:
- 调度线程:调度
alive_count_0个线程,每个线程处理一个存活粒子 - 模拟计算:
- 计算位置、速度
- 进行深度碰撞检测等操作
- 数据写回粒子池
- 生命周期检查:如果粒子生命周期结束(比如粒子6已消亡),将其索引加入死亡列表,同时跳过写入下一帧的存活列表
- 写入下一帧存活列表:活着的粒子(如3, 4, 5, 7)写入Alive List 1,
alive_count_1=4
视锥体剔除(Frustum Culling):
- 执行视锥体剔除,写入计算结果
- 计算距离到距离缓冲区
- 剔除后的存活列表包含可见粒子(如3, 4, 7),
after_culling_count=3 - 粒子5被剔除,因为它不在视锥体内
重要细节:视锥体剔除不会反向改变存活列表。粒子的生死与是否可见无关。例如射出一群烟火,即使不观察它,它也在空中飞行。如果因为上一帧看不见就把它从存活列表中移除,回头再看时就会消失,产生奇怪的效果。因此粒子模拟是view-independent的,而剔除是view-dependent的。
排序、渲染与交换

排序:
- 根据距离缓冲区排序:剔除后根据距离缓冲区对存活列表进行排序
- 排序结果:存活列表从[3, 4, 7]变为[7, 3, 4](按距离从远到近或从近到远)
渲染:
根据排序后的存活列表,生成渲染用的vertex和index buffer,按顺序渲染粒子。
存活列表交换:
交换存活列表0和存活列表1(以及存活计数0和存活计数1)。这是双缓冲策略,确保每一帧都能正确管理粒子的生命周期。
并行归并排序
GPU上进行排序需要使用并行排序算法。粒子系统最难的部分就是排序,特别是需要全局排序时。CPU上可以用快速排序等算法,但在GPU上需要专门的并行算法。

并行归并排序(Parallel Merge Sort)是经典算法:
- 第一步:把所有相邻的两个元素排序,得到多个已排序的小数列
- 第二步:每两个已排序的数列合并成更大的已排序数列
- 重复合并:继续合并,直到得到完全排序的数组
时间复杂度是O(n log n),每一步的计算复杂度是O(log n)。
两种合并策略

方法一:每个源元素对应一个线程
- 每个线程决定目标位置
- 问题:写入操作不连续,内存访问跳跃,缓存连贯性(cache coherence)会大幅降低
- 对于几万到几十万的粒子,内存跳跃会非常大,效率很低
方法二(更好):每个目标元素对应一个线程
- 每个线程在源数组中查找应该放在目标位置的元素
- 写入顺序是连续的(1, 2, 3, 4, 5…),内存访问更友好
方法二是行业常用的方法,因为它能依次连续写入,这对GPU实现特别重要。具体实现需要使用二分查找等技术,此处不展开细节。
深度缓冲区碰撞
除了排序,GPU粒子系统还可以进行粒子和场景的碰撞检测。

深度缓冲区碰撞(Depth Buffer Collision)的流程:
- 重投影粒子位置:将粒子位置重投影至上一帧的屏幕空间纹理坐标
- 读取深度值:从上一帧的深度纹理中读取深度值
- 碰撞检测:检查粒子是否与深度缓冲区发生碰撞,但并非完全位于其后方(使用厚度值进行判断)
- 碰撞响应:若发生碰撞,计算表面法线并使粒子反弹
这种方法使用屏幕空间深度来模拟场景几何形状,然后让粒子在其中进行碰撞。虽然不如完整的物理引擎精确,但计算效率高,效果也足够好。

从实际效果来看,使用屏幕空间深度缓冲进行碰撞的粒子系统,效果远好于没有碰撞的粒子。粒子系统始终是在效果和计算效率之间进行权衡的系统。
总结
GPU粒子系统是现代游戏引擎的标配,主要优势包括:
- 并行计算:充分利用GPU的并行处理能力,适合处理大量粒子
- 资源管理:通过粒子池、死亡列表、存活列表的双缓冲策略,高效管理粒子生命周期
- 性能优化:在GPU上进行视锥体剔除、排序等操作,减少数据传输
- 碰撞检测:使用深度缓冲区进行简化的碰撞检测,在效果和性能之间取得平衡
12.4 高级粒子系统应用
现代游戏的粒子系统已经远不局限于实现视觉特效,可以基于粒子系统实现更丰富的功能。比如游戏中大量NPC的运动行为就可以用粒子系统实现。此时每个粒子不仅具有常见的物理属性,还会携带顶点等几何信息。
人群模拟

人群模拟(Crowd Simulation)是粒子系统的高级应用之一。游戏中需要展现大量人群、鸟群或动物群时,传统方法是在场景中放置大量独立物体,但这种方法性能开销大。很多游戏引擎用粒子系统来实现人群模拟。
核心思想是把每个角色变成一个网格粒子(Mesh Particle)。这个mesh不仅是静态几何,还可以动起来。每个顶点存储它受控于哪个骨骼(Bone),用最简单的蒙皮(Skinning)方式,每个顶点只受一根骨骼控制。
虽然单骨骼控制会出现骨骼转动时的断裂,不如完整蒙皮真实,但因为粒子很小,这种小裂缝不会被注意到。有了骨骼和最简单的蒙皮信息,就可以让粒子动起来。
动态粒子网格

动态粒子网格(Dynamic Particle Mesh)展示了粒子与网格之间的转换关系。
技术细节:在顶点位置向量的w分量(vertex_position.w)中存储关节索引(Joint Index),即Alpha(vertex_position.w) = 关节索引。这样每个顶点都知道自己受哪个骨骼控制,实现简单的骨骼动画。
粒子系统可以实现从mesh到particle再到mesh的转换:
- Mesh可以打散成多个particle
- Particle可以汇聚成新的mesh
- 这种转换可以用于各种视觉效果,比如角色分解、重组等
粒子动画纹理

粒子动画纹理(Particle Animation Texture)将所有可能的动画状态编码成纹理。
系统定义多个动画片段(Animation Clips):
CLIP_run(跑步)CLIP_sprint(冲刺)CLIP_walk(行走)CLIP_standFire(站立射击)CLIP_crouchFire(蹲下射击)CLIP_command(指令)CLIP_deathRun、CLIP_deathSprint等死亡动画
每个片段包含:
- 起始帧(clipStarts)
- 长度(clipLengths)
- 平均速度(clipAvgSpeeds)
- 关节偏移(jointOffsets)
所有动画数据编码成一个纹理,粒子系统通过读取纹理来获取动画信息。这样每个particle的模拟不再是简单的速度加减,而是变成一个状态机(State Machine)。每个particle在自己的时间点,在状态机之间切换,实现复杂的动画行为。
导航纹理

导航纹理(Navigation Textures)用于控制群体在场景中的运动路径。
系统包含两部分:
有符号距离场(Signed Distance Field,SDF):
- 灰度图表示场景布局
- 白色/浅灰色区域表示可行走区域(建筑物、道路)
- 深色区域表示障碍物或不可行走区域
- 灰度值表示到边界的距离
方向纹理(Direction Texture):
- 使用RG通道存储方向信息
- 为场景中任何位置提供移动方向指引
- 形成类似道路的导向图
有了SDF和方向纹理,可以算出场景中任何位置的空间导向图。给任何particle一个指引性,它就会受这个方向场的影响,开始移动。粒子会绕过障碍物,大体上往目标方向走。
人群运行时行为

人群运行时行为(Crowd Runtime Behavior)的设计目标是设计目标位置以引导人群移动。
作用力系统:
- 朝向目标位置的移动欲望:粒子有向目标移动的倾向
- 远离阻挡几何体的排斥力:粒子会避开障碍物
- 摄像机作用力:如果距离足够近,摄像机也会产生作用力
这些力都会转化为影响人群移动的作用力。系统使用力场图来可视化这些力的分布,不同颜色区域表示不同的力场强度或方向。
高级粒子演示

骨骼网格发射器(Skeletal Mesh Emitter)展示了粒子系统的高级功能:
- 动态程序化样条线(Dynamic Procedural Spline):粒子可以沿着程序生成的样条线移动
- 对其他玩家的反应(Reaction to other players):粒子可以感知并响应其他实体
这些功能让粒子系统不仅能产生视觉效果,还能与环境和其他实体互动。

与环境互动(Interaction with Environment)展示了粒子如何与环境几何体交互:
- 粒子可以沿着曲面流动
- 粒子可以与环境物体碰撞
- 粒子可以响应重力和其他物理力
这些互动让粒子效果更加真实和动态。
粒子系统设计理念
现代粒子系统有两种主要设计理念:
预设堆叠式模块

预设堆叠式模块(Preset Stacked Module)是传统粒子系统的设计方式,以虚幻引擎的级联粒子系统(Cascade Particle System)为代表。
优点:
- 作为堆叠模块快速添加行为
- 非技术美术人员可以通过一套典型行为集实现高度控制
- 一目了然,易于理解
缺点:
- 固定功能,新功能需要新代码
- 基于代码,游戏团队代码存在分歧
- 固定粒子数据,数据共享基本无法实现
这种方式适合快速制作常见效果,但扩展性和灵活性有限。
基于图形的设计

基于图形的设计(Graph-based Design)使用可视化编程图来定义粒子行为。
优点:
- 可参数化且可共享的图形资产
- 减少代码差异
- 提供模块化工具而非硬编码功能
系统通过节点图定义粒子行为:
- SimIn:输入节点,提供初始属性(Lifetime、Position、Rotation、Scale、Color、Alpha、Velocity)
- UpdateLifetime:更新生命周期
- DepthBufferCollision:深度缓冲碰撞检测
- UpdatePosition:更新位置
- SimOut:输出节点,收集最终模拟结果
还可以定义分组操作:
- Update velocity using gravity:使用重力更新速度
- Color gradient:颜色渐变
- Alpha gradient:Alpha渐变
这种方式提供了极大的灵活性,但学习曲线较陡。
混合设计

混合设计(Mixed Design)结合了两种方式的优点,以虚幻引擎的Niagara系统(Niagara System)为代表。
设计原则:
- 图表提供完全控制(Charts provide complete control)
- 堆栈提供模块化行为和直观可读性(Stacks provide modular behavior and intuitive readability)
系统架构:
模块(Module):
- 封装行为(可视为”编写函数”)
- 相互堆叠
- 使用图表表示
发射器(Emitter):
- 模块容器
- 单一用途,可重复使用
- 使用堆栈组织
系统(System):
- 将多个发射器整合为一个”效果”的容器
- 使用堆栈组织
这种混合设计既保持了易用性,又提供了强大的扩展能力。Niagara系统代码量超过100万行,体现了现代粒子系统的复杂性。
现代粒子系统的复杂性
现代粒子系统已经远远超出了最初的理解。从简单的预设参数控制,发展到:
- 可编程的模拟:每个particle的simulation可以读取环境变量、感知周围扰动、查找最近的neighbor particle等
- 动态几何:Mesh可以变成particle,particle可以汇聚成mesh
- 状态机集成:每个particle内部可以维持状态机,在状态间切换
- 环境交互:粒子可以感知环境、响应几何体、与其他实体互动
- 可视化编程:通过图形化界面定义复杂行为,无需编写代码
总结
粒子系统从1982年提出到现在已经40多年,从最初简单的视觉效果工具,发展成现代游戏引擎中最重要的系统之一。现代粒子系统可以:
- 实现复杂视觉效果:爆炸、烟雾、火焰等传统特效
- 人群模拟:用粒子系统实现大量NPC的运动
- 动态几何转换:Mesh和particle之间的相互转换
- 环境交互:粒子与环境、其他实体的复杂互动
- 可编程行为:通过状态机、图形化编程实现复杂逻辑
现代粒子系统的复杂度已经远超最初的理解。以Niagara系统为例,代码量超过100万行,体现了这个系统的深度和广度。对于想深入研究粒子系统的开发者,以Niagara系统为蓝本,花费几个月甚至半年时间深度钻研,会有很大收获。
粒子系统的发展离不开技术艺术家(Technical Artist)的贡献。他们用想象力和创造力,让工程师意识到底层系统的可能性和可行性。粒子系统的发展历程,也是游戏引擎技术不断进步的缩影。
12.5 声音基础
音效是影响游戏氛围和玩家体验的重要一环。许多游戏需要使用大量音效来调动玩家情绪,增强真实感,营造氛围。在游戏引擎中,粒子系统和声音系统通常紧密配合,多数效果系统需要配合音效,缺少音效的粒子效果会显得不够真实。

声音对游戏的重要性体现在多个方面。从进化角度,人类大脑从脑干开始发育,脑干最初是感应周围环境振动的器官,人类对震动的感知始于脑干。声音对情绪的影响具有直觉性,相比视觉画面,声音往往能更高效地影响玩家体验。
在游戏引擎设计中,声音系统(Sound System)承担着关键角色。优秀的游戏作品,音乐和音效通常具有出色的表现。例如《死亡空间》(Dead Space)、《死亡搁浅》(Death Stranding)等游戏,声音工程师(Sound Engineer)和声音设计师(Sound Designer)是这些游戏的核心要素。
音量
声音的大小称为音量(Volume),它表示声波的振幅。

从物理角度,声音的本质是空气的振动。当空气发生振动时会产生相应的压强,该压强的大小对应人感知到的音量。声音是纵波,空气被压缩弹开,在单位面积上产生一定的压强,该压强对应人感知到的音量。
音量的基本术语

与音量相关的基本术语包括:
声压(p,Sound Pressure):
- 由声波引起的局部大气压与环境大气压的偏差
- 国际单位制单位:帕斯卡(Pa)
粒子速度(V,Particle Velocity):
- 粒子在介质中传播波时的速度
- 国际单位制单位:米每秒(m/s)
声强(I,Sound Intensity):
- 声波在垂直于传播方向的单位面积上所携带的功率
- 国际单位制单位:瓦特每平方米(W/m²)
三者之间的关系:
\[
I = p \cdot v
\]
其中 I 是声强,p 是声压,v 是粒子速度。
声压级

音量的单位是分贝(dB,Decibel),它是基于人对于声音的感知来定义的。
声压级(Lp,Sound Pressure Level):
- 声音有效压力相对于参考值的对数度量
- 国际单位制单位为分贝(dB)
声压级的计算公式:
\[
L_p = 20\log_{10}\left(\frac{p}{p_0}\right) \text{ dB}
\]
其中:
p:有效声压p₀:参考声压,人耳听觉阈值
参考声压(p₀):
- 在空气中常用值为:
p₀ = 20 μPa(20微帕斯卡) - 约相当于一只蚊子在三米外飞行的声音
分贝是一个基于人体认知的概念。它首先定义了最小的分贝为零的压强,即人类能感觉到的最小压强。其他声音以它为基数进行对数对比,每十倍一个台阶,乘以20。这意味着:
- 分贝为0:3米外蚊子拍翅膀的声音
- 分贝为20:声音产生压强的10倍
- 分贝为40:100倍
- 分贝为60:1000倍
- 分贝为120:约100万倍(人类能容忍的听觉极限)
人类对声音的感知是非线性的,而是基于对数的。在游戏引擎设计中,开放给声音设计师(Audio Designer)的所有工具里,所有声音的音量(Volume)全部用分贝或类分贝的数值表达,均以20的log₁₀次幂来表达,以符合人类对声音的自然认知。
音高
音高(Pitch)是描述人耳对声音调子感受的物理量,它取决于声音振动的频率。音高越高,声音就越尖锐。

频率越高,声音越尖锐。人耳能听到的频率范围约为20赫兹到20千赫兹(20 Hz - 20 kHz),这是人耳可感知的基本频率范围。
音色
音色(Timbre)是描述声波形状的量。不同的乐器在演奏时会产生不同形式的基波,因此即使声波的频率相同也会产生不同的音色。

音色可以理解为泛音(Overtones)或谐波(Harmonics)的组合,包括:
- 频率(Frequency):不同频率的波叠加
- 相对强度(Relative Intensity):每个基波的权重不同
例如,演奏相同音调时,使用钢琴、小提琴或其他乐器,产生的听觉效果完全不同。这体现了音色的差异。音色可理解为在频率相近的情况下,波的叠加形式不同,或各基波的权重不同,导致听觉效果的差异。
相位与噪声消除
由于声音是纵波,当空气收缩时,若同时存在使空气舒张的波,这两个波之间会形成抵消效果。

这是现代降噪耳机(Noise Cancelling Headphones)的基本原理。降噪耳机通过侦测空气中声波的相位和强度,生成反向的位移波,使两个波反向抵消,从而形成降噪效果。
尽管在游戏开发中应用有限,但这是重要的科学原理,展示了相位在声音处理中的应用。
人耳听觉特性
在游戏引擎设计中,需要了解人类听觉范围,以及不同声音类型(说话、音乐、环境噪音、爆炸声等)的频率和分贝范围。

人耳可闻声(Sounds Audible to the Human Ear):
- 频率范围:20赫兹至20千赫兹(20 Hz - 20 kHz)
- 声压级范围:0-130分贝(0-130 dB)
尽管人耳可识别的频率范围为20至20千赫兹,但高于20千赫兹的声音,例如达到1万赫兹时,人类仍能感知。虽然无法直接听见,但会影响音色的感知。因此,在电影制作或高质量音频录制中,实际记录的声音频率通常高于20千赫兹。
听觉是重要的心理感受,人体对声音震动的感知非常敏感。
数字声音
自然界中的声音是连续的信号,因此要使用计算机存储或者表达声音,就需要对连续的信号进行离散化。最常用的声音采样设备是PCM(Pulse Code Modulation,脉冲编码调制),它可以把连续的信号量化为离散的数字信号。
脉冲编码调制(PCM)

脉冲编码调制(PCM)是对采样模拟声音信号进行编码的标准方法。
PCM的基本思想包括三个步骤:
- 采样(Sampling):对波动的信号进行采样,采样得到的数据还是浮点型,具有无限精度
- 量化(Quantization):把采样数据变成一个可以存储的定点型或浮点型数据
- 编码(Encoding):对数据进行编码,目的是压缩和表达
采样

采样(Sampling)是将连续信号转换为离散信号的过程。
采样频率(Sampling Frequency):
- 每秒采样次数,单位为赫兹(Hz)
奈奎斯特-香农采样定理(Nyquist-Shannon Sampling Theorem):
- 信号不扭曲其基础信息所需的最低采样频率,应为其最高频率分量的两倍
对于声波,人耳能听见的频率范围是20 Hz到20 kHz。根据香农采样定理,对任何频率的信号,只要采样密度是其频率的两倍,即可实现无损表示。因此,对声波的采样只需超过20 kHz的两倍,例如40 kHz、44.1 kHz或48 kHz即可。
实际应用中,采样率通常略高于理论值,因为虽然人耳能区分的声音频率不超过20 kHz,但更高频的声音会影响音质,因此采样率会适当提高。
量化

采样后的信号需要通过量化(Quantization)的过程编码为数字。
位深度(Bit Depth):
- 位深度是指每个样本中的信息位数
现代音频方法中,通常使用16位或24位比特存储量化值。需要注意的是,声音的强度感知是非线性的,因此量化方法存在多种流派:
- 线性方法:采用线性方式存储变化,如1、2、3、4…
- 对数方法:采用对数或指数方法存储,能够更高效地利用存储空间
各种方法各有利弊,在游戏引擎中需要根据实际需求选择。
音频格式
基于采样和量化的过程,就可以把声音使用计算机可以识别和保存的数据。目前常用的声音格式如下:

音频格式对比:
WAV(未压缩):
- 质量:高
- 存储:低效率(文件大)
- 多声道:支持
- 专利:无专利问题
FLAC(无损):
- 质量:高
- 存储:中等效率
- 多声道:支持
- 专利:无专利问题
MP3(有损):
- 质量:低
- 存储:高效率(文件小)
- 多声道:仅支持立体声(左右两个声道)
- 专利:有专利保护
OGG(有损):
- 质量:低
- 存储:高效率(文件小)
- 多声道:支持
- 专利:无专利保护
在游戏开发中,通常不使用无损格式,因为数据量过大。一般会采用有损格式。
MP3存在两个问题:
- 多声道限制:MP3格式设计时仅支持立体声(左右两个声道),无法支持5.1音源等环绕声的采样。游戏开发中经常需要环绕音,MP3无法满足需求。
- 专利保护:MP3具有严格的专利保护,引擎若要支持MP3格式,需要支付专利费用。
因此,在游戏引擎中,越来越多开发者选择OGG格式,因为OGG格式无专利保护,可自由使用。这体现了开源精神。
每种声音格式各有利弊,在引擎开发时需要根据需求选择合适的格式。
基于声音的基础原理,包括声强、音色、声音的存储格式,以及数字化处理,可以在游戏引擎中实现完整的音频系统。
12.6 三维音频渲染
在三维游戏引擎中,需要在三维空间内设置音效,使玩家获得身临其境的感受。三维音频渲染是一套独立的渲染系统,与光学渲染是两个概念,但其复杂度和专业度同样很高。

在三维空间中存在无数声源,每个声源本身是简单的音频信号源。同一段音频信号,在空间中的不同位置,听觉感受会不同。距离越近,声音越大;距离越远,声音越小。此外,还需要区分声源在左侧还是右侧,以及声源与听者之间的空间关系。
3D音源
3D音源(3D Sound Source)是单声道音频信号,从特定位置发出。在游戏引擎中,每个3D音源都有明确的空间位置,其声音特性会随听者位置的变化而变化。
Listener
在三维音频渲染中,需要引入Listener(听者)的概念,类似于光学渲染中的相机。

Listener(虚拟麦克风)需要包含以下物理信息:
- 位置(Position):听者在空间中的位置
- 在FPS游戏中,通常位于角色身上
- 在TPS游戏中,通常位于观察点和角色之间的某个位置
- 速度(Velocity):听者的运动速度,用于计算多普勒效应
- 方向(Direction):听者的朝向,影响声音的感知
Listener是三维音频渲染的几何基础,用于构建声音的空间感。
空间音频渲染
人类通过双耳感知声音的空间位置。双耳之间存在三个主要差异:
- 音量差异:声源靠近左耳时,左耳听到的音量更大,右耳因头部阻挡音量较小
- 时间差异:声音到达左右耳的时间差,人类能感知到十几毫秒的差异
- 音色差异:同一声音到达左右耳时,音色会发生微妙变化
这些差异共同构成了人类对声音空间位置的感知能力。

空间音频渲染(Spatial Audio Rendering)用于将声音相对于听者进行定位的技术,包括:
- 声像定位(Sound image localization)
- 声场(Sound field)
- 双耳音频(Binaural audio)
声道平衡
当音响有多个通道时,可以调整不同通道的参数来产生空间感。

声道平衡(Channel Balance)将音频信号分配到新的立体声或多声道声场中。例如7.1环绕声系统,通过多个扬声器(前左、前右、中置、侧环绕、后环绕等)构建沉浸式的多方向声场。
平移(Panning)
平移(Panning)是通过调整多个通道上的声音大小、音色和延迟,产生虚拟空间感的过程。
线性平移
最简单的平移方法是线性插值。

线性平移(Linear Panning)的核心思想:对于增益为1的立体声信号,左右声道的增益之和应等于1。
\[
\text{Gain}_{left} = x
\]
\[
\text{Gain}_{right} = 1 - x
\]
\[
\text{Gain}{left} + \text{Gain}{right} = 1
\]
其中 x 是平移参数,范围从0(完全左)到1(完全右)。
线性平移的功率问题
人类对响度的感知实际上与声波的功率成正比,功率等于信号幅度的平方。

功率计算公式:
\[
\text{Power}{right} = \text{Gain}^2{right} = (1 - x)^2
\]
\[
\text{Power}{left} = \text{Gain}^2{left} = x^2
\]
\[
\text{Power}_{total} = x^2 + (1 - x)^2
\]
当声音在中间进行声像定位时(x=0.5),总功率会下降:
\[
\text{Power}_{total} = (0.5)^2 + (1 - 0.5)^2 = 0.5
\]

等功率声像定位
为解决线性平移的功率下降问题,采用等功率声像定位(Equal Power Sound Localization)。

在声像定位过程中,通过保持功率约束而非振幅恒定来维持恒定响度:
\[
\text{Power}{total} = \text{Gain}^2{left} + \text{Gain}^2_{right} = 1.0
\]
该方程有几种可能的解,其中一种是正弦/余弦方程:
\[
\text{Gain}_{left} = \sin(x)
\]
\[
\text{Gain}_{right} = \cos(x)
\]
使用三角函数可以保证总功率恒定为1,从而获得更真实的声像定位效果。

实际游戏中的平移算法比上述介绍的要复杂得多,可以表达更复杂的空间位置变化,包括前后、上下等方向。
衰减(Attenuation)
当声源远离听者时会出现衰减(Attenuation)现象,随着距离增加,听者能接收到的声音会不断减少。不仅接收到的音量会发生变化,距离也会对接收到的声音频率产生一定影响。

在现实世界中,球面声波的声压(p)会随着与球心距离的倒数(1/r)而衰减:
\[
p(r) \propto \frac{1}{r}
\]
在游戏引擎中,不同距离的声音频率特性也会发生变化。例如在射击游戏中,远距离的枪声会逐渐失去高频成分,只保留低频的轰鸣声,有助于判断声源的远近。
衰减形状
游戏引擎提供了多种衰减形状,以适应不同的声源类型。
球体衰减(Sphere Attenuation):

适用于大多数点声源,模拟声音在现实世界中的传播方式。声音的变化只与声源和听者之间的距离有关。
胶囊体衰减(Capsule Attenuation):

适用于水管等场景,其声音不希望呈现为空间中单一特定点发出的效果。例如流水声会沿着管道长度方向传播。当接收端沿轴方向运动时,声音的衰减基本保持不变。
方盒衰减(Box Attenuation):

适用于房间环境音或氛围音的设定,可以按照房间的实际形状来定义音效盒的形态。当声音超出某个范围时,会迅速衰减。
锥形衰减(Cone Attenuation):

适用于需要定向衰减模式的场景,例如公共广播扬声器。高音喇叭输出的声音具有定向性,使用锥形衰减可以模拟这种效果。
阻碍与遮挡(Obstruction and Occlusion)
由于声音的本质是波,在封闭环境中需要考虑声波与环境的互动。
阻碍(Obstruction):

当声音被场景中的障碍物阻挡时,可以通过衍射的方式继续传播。声音在障碍物边缘会产生新的波源,形成波的衍射。
遮挡(Occlusion):
当声音的传播被部分阻挡时,直接路径会通过开口传递,同时也会通过反射和衍射到达听者。
在游戏引擎中,处理阻碍和遮挡的方法包括:
- 从听者位置向声源投射若干条不同角度的发散射线
- 查询受冲击表面的材质属性,通过被阻挡射线数量来确定其吸收声音能量的程度

不同材质对不同频率的声音吸收率不同。例如混凝土、砖块、窗帘等材质,在不同频率段(低频、中低频、中高频、高频)的吸收系数存在显著差异。这种频率相关的吸收特性会影响声音的传播效果。
混响(Reverb)
声音在室内场景中会出现混响(Reverb)现象,这是由于声波在场景中不断反射所导致的。
混响由三个主要组成部分:

- 直达声(Dry Sound,Direct Sound):声音直接从声源传到听者,没有经过任何反射
- 早期反射声(Early Reflections,Echo):声音经过几次反射后到达听者,通常有较短的延迟
- 后期混响(Late Reverberations,Diffuse Tail):经过多次反射形成的扩散声场,提供空间感和深度感
混响的效果很大程度上取决于材质的吸声特性,不同材质在不同频率上有着显著的性能差异。另一方面,混响也取决于场景的几何特征。
混响时间(Reverberation Time):

混响时间是衡量声音在特定房间内衰减速度的指标。房间尺寸与材料选择共同决定了混响时间的长短。
吸收(Absorption):材料的吸收系数与材质数量共同决定吸收效果。
混响时间的计算公式:
\[
A = S \times a \text{ (m²sab)}
\]
\[
T = 0.16 \times \frac{V}{A} \text{ (s)}
\]
其中:
a:吸声系数S:面积A:等效吸声面积V:房间内的体积T:混响时间0.16:比例因子,表示初始声压级降低60分贝所需的时间(单位为秒)
不同材质在不同频率段的吸收系数存在显著差异:

例如装饰石柱、地毯、石膏、玻璃、大理石、木门等材质,在125 Hz、250 Hz、500 Hz、1 kHz、2 kHz、4 kHz等频率段的吸收系数各不相同。这些数据用于声学模拟。
实用混响控制:

混响效果控制基于声学参数,主要包括:
- 预延迟(Pre-delay):信号进入混响单元前的延迟时间。较长的预延迟可以模拟更大的空间,第一个回声需要更长时间才能被听到
- 高频比率(HF ratio):用于控制高频相对于低频混响时间的衰减系数
- 湿声级别(Wet level):应用于混响声音的增益系数
- 干声级别(Dry level):应用于直达声音的增益系数
通过调整不同的混响组合比例,可以实现丰富的声学效果。
运动中的声音:多普勒效应
当声源发生运动时,由于多普勒效应(Doppler Effect)会导致接收端接收到的频率发生变化。

多普勒效应:当波源与观察者相对运动时,观察者所感知到的波的频率会发生变化。
多普勒效应的计算公式:

\[
f’ = \frac{V + V_0}{V + V_s} f
\]
其中:
f:原始频率f':多普勒频移后听者接收到的频率V:空气中的声速V₀:听者的速度V_s:声源速度
多普勒效应是游戏中对速度感知特别重要的效果,例如飞机飞过、车辆行驶、子弹掠过等场景,都会产生多普勒频移,增强打击感和速度感。
声场(Sound Field)
空间化-声场(Spatialization - Sound Field):

全向环绕声(Omnidirectional Surround Sound),又称环绕声场技术,用于360度视频和VR。在VR采集时,对空间进行声音采样,可以实现对整个声场的采集,而不仅仅局限于特定的听者位置。
音频中间件
虽然声音是游戏引擎的重要组成部分,但很少有游戏引擎团队会直接编写声音引擎,一般会使用音频中间件(Audio Middleware)。
常用中间件:

目前市面上常用的专业级声学引擎包括FMOD和Wwise等。这些引擎为声音设计师(Audio Designer)提供了符合其工作习惯的调音环境,让音频工程师专注于在3D世界中进行声音的组合渲染,而声音的生产和管理则交给声音设计师完成。
音频中间件如何工作:

音频中间件提供了完整的音频工作流程,包括:
- 音频事件的创建和管理
- 效果器的添加和配置
- 混响、干湿音等空间效果的设置
- 实时参数控制(RTPC)
- 状态管理和游戏同步
声音设计师可以在中间件中完成音频的设计和调试,然后通过SDK集成到游戏引擎中。
音频世界建模
表现大规模场景的声学特性是非常复杂的任务。

音频世界建模(Audio World Modeling)需要考虑:
- 表面与物体的几何形状及属性:场景的几何结构影响声音的传播和反射
- 听音空间的声学特性:不同区域具有不同的混响和吸收特性
以开放世界游戏为例,为了表现整个城市的音场效果,需要将不同区域的混响效果归纳成不同的类型,形成分布图。这样当角色在城市中移动时,能够听到不同区域的环境音变化,每个区域都有其独特的声学特征。
三维音频渲染是游戏引擎中复杂度很高的系统,涉及声学、心理学、信号处理等多个领域。通过合理运用平移、衰减、混响、多普勒效应等技术,可以在三维空间中构建真实的声音场,为玩家提供沉浸式的听觉体验。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com