粒子系统
一、 粒子和点精灵
粒子是一种微小的物体,数学上通常用点来表示其模型。所以显示粒子时,使用点图元(由 D3DPRIMITIVETYPE 类型的 D3DPT_POINTLIST 枚举常量表示)是一个很好的选择。但是,光栅化时,点图元将被映射为一个单个像素。这样就无法为我们提供很大的灵活性,因为实际应用中我们很可能需要各种尺寸的粒子甚至希望能够对这些粒子进行纹理映射。
Direct3D8.0 引入了一种特别的点图元——点精灵( Point Sprite ),该图元极适合应用于粒子系统中。与普通的点图元不同,点精灵可进行纹理映射且尺寸可变。点精灵也不同于广告牌,描述点精灵时仅需要一个单点( single point )即可。由于我们只需要存储和处理一个顶点而非 4 个(广告牌要用 4 个顶点描述),这样就节省了内存和宝贵的运算时间。
1. 结构格式
我们用如下顶点结构来描述粒子的位置和颜色。
该结构金存储了粒子的位置和颜色信息,也可以根据需要为该结构添加一对纹理坐标。
有时我们可能需要为 Particle 结构添加一个浮点类型的变量来表示其尺寸。为了放映该变化,我们必须为灵活顶点格式 FVF 增加 D3DFVF_PSIZE 标记。让每个粒子对象维护其自身的尺寸十分有用,因为这就允许我们单独指定或改变某个粒子的尺寸。但由于大多数图形卡都不支持按照这种方式控制粒子的尺寸,所以我们就不采用这种做法。(可检查结构 D3DCAPS9 中的成员 FVFCaps 中的 D3DFVFCAPS_PSIZE 位来验证。)我们可通过绘制状态来控制粒子的尺寸。下面就是一个顶点格式中含有尺寸成员的例子。
注意,即使硬件不支持 D3DFVFCAPS_PSIZE ,借助像素着色器我们也有可能控制每个粒子的尺寸。
2. 点精灵的绘制状态
点精灵的行为很大程度上是由绘制状态来控制的。我们来回顾一下这些绘制状态。
l D3DRS_POINTSPRITEENABLE 一个布尔值。默认值为 false 。
u 若指定为 true ,则规定整个当前纹理被映射到点精灵上。
u 若指定为 false ,则规定点精灵(如果其顶点结构中含纹理坐标的话)的纹理坐标所指定的纹理元被映射到点精灵上。
l D3DRS_POINTSCALEENABLE 一个布尔值。默认值为 false 。
u 若指定为 true ,则规定点的尺寸将用观察坐标系的单位来度量。观察坐标系的单位是仅用来描述摄像机坐标系中的 3D 点。点精灵的尺寸将依据近大远小的原则进行相应的比例变换。
u 若指定为 false ,则规定点的尺寸将用屏幕坐标系的单位(即像素)来度量。如果将该绘制状态指定为 false ,而且想将点精灵的尺寸设为 3 ,则点精灵将变为屏幕上一个 3 * 3 的像素区域。
l D3DRS_POINTSIZE 用于指定点经历了的尺寸。该值可被解释为观察坐标系中的点精灵尺寸,也可被解释为屏幕坐标系中的点精灵尺寸,这主要取决于绘制状态 D3DRS_POINTSCALEENABLE 的设置。
l D3DRS_POINTSIZE_MIN 指定点精灵可取的最小尺寸。
l D3DRS_POINTSIZE_MAX 指定点精灵可取的最大尺寸。
l D3DRS_POINTSCALL_A , D3DRS_POINTSCALL_B , D3DRS_POINTSCALL_C 这 3 个常量控制了点精灵的尺寸如何随距离发生变化,这里的距离是指点精灵到摄像机的距离。给定距离和这些常量时, Direct3D 使用如下公式计算点精灵的最终尺寸:
其中:
² FianlSize 计算出距离后,点精灵的最终尺寸。
² ViewportHeight 视口高度。
² Size 对应于由绘制状态 D3DRS_POINT_SIZE 所指定的值。
² A , B , C 分别对应于绘制状态 D3DRS_POINTSCALE_A , D3DRS_POINTSCALE_B 和 D3DRS_POINTSCALE_C 所指定的值。
² D 在观察坐标系中点精灵到摄像机的距离。由于在观察坐标系中,摄像机位于坐标原点,所以 ,其中( x, y, z )是点精灵在观察坐标系中的位置。
3. 粒子及其属性
一个粒子除了位置和颜色外往往还具有许多其他的属性。例如,例子可具有一定的速度。但是,绘制粒子时并不需要这些附加属性。所以,我们将用于绘制粒子的数据与粒子的属性分别存储在两个不同的结构中。当我们要创建、销毁或更新例子时,需要涉及粒子的属性;当我们准备绘制粒子时,可将粒子的位置和颜色信息复制到 Particle 结构中。
粒子的属性与我们所要模拟的例子系统的特定类型息息相关。但是,我们通过指定一些常用属性可以使这些属性结构变得通用一些。
二、 粒子系统的组成
粒子系统是众多粒子的集合,并负责对这些粒子进行维护和显示。粒子系统跟踪系统中影响所有例子状态的全局属性,例如粒子的尺寸、粒子的粒子源、将要映射到粒子的纹理等。按照功能来说,粒子系统主要负责更新、显示、杀死以及创建粒子。
1. 绘制一个粒子系统
由于粒子系统是动态的,我们需要在每帧中更新系统中的粒子。一种直观但是缺乏效率的粒子系统绘制方法是:创建一个足够容纳最大数目个粒子的顶点缓存。
对于每一帧进行的操作如下:
1) 更新所有粒子。
2) 将所有处于活动状态的粒子复制到顶点缓存中。
3) 绘制顶点缓存中的粒子。
该方法是可行的,但效率不是最高的。原因之一是该顶点缓存必须足够大以容纳系统中的所有粒子。但更重要的原因是,当我们将粒子从列表复制到顶点缓存的这个过程中,图形卡一直处于空闲状态。
一种更好的方法(即 SDK 中的例程 Point Sprite 所使用的方法)是:创建一个容量合理的顶点缓存(能够容纳 2000 个粒子)。然后我们将该顶点缓存划分为若干片断。例如,我们将每个片断的尺寸指定为可容纳 500 个粒子。然后创建全局变量 i = 0, 用该变量来跟踪当前片断。
对每一帧的操作如下。
1) 更新所有粒子。
2) 将全部活动粒子被绘制。
Ø 若顶点缓存未满则:
ü 用标记 D3DLOCK_NOOVERWRITE 锁定片断 i 。
ü 将 500 个粒子复制到锁定片断 i 中。
Ø 若顶点缓存已满,则:
ü 用自顶点缓存的起始位置开始 i=0 。
ü 将用标记 D3DLOCK_DISCARD 锁定片断 i 。
ü 将 500 个粒子复制到锁定片断 i 中。
3) 绘制片断 i 中的粒子。
4) 下一片断: i++ 。
2. 随机性
系统中的粒子都具有某种随机性。