Game Programming with DirectX -- 11[粒子系统的实际应用]
第十一集 粒子系统的实际应用
现实中的不规则物体要精确的模拟需要更多的粒子属性和更好的过程控制, 我们只是简单的模拟一些常见的物体.
以下粒子的参数初始化值和过程控制只是经验模型.
11.1 Snow
创造虚拟的世界里的美丽"雪绒花".
11.1.1 参数初始化值
雪花是从天空缓缓落下的, 随着风向飘散到大地....
(1). 初始位置Y值是一个比较大的正的数值, 表示从天空落下, X和Z位置值可以是随机的正负值, 表示天空各个地方都会有雪花.
(2). 速度; 现实中有重力加速度的作用下落, 这里使用一简单的Y轴负值来模拟, X和Z速度值随机的正负值, 模拟向各个方向飘散.
(3). 生命值; 由于雪花缓缓的飘落, 所以生命值适当的要长一点.
(4). 颜色; 雪花的纹理一般是白色的圆, 粒子颜色有无个人爱好决定.
m_vPos.x = randf(-16.0, 16.0); // X 初始值是 -16.0 到16.0的随机值
m_vPos.y = 20.0;
m_vPos.z = randf(-16.0, 16.0);
m_vVel.x = randf(-0.1f, 0.1f);
m_vVel.y = randf(-0.2f, -0.1f);
m_vVel.z = randf(-0.1f, 0.1f);
m_fLife = randf(16.0, 32.0);
11.1.2 过程控制
雪花的过程控制是随机的减小生命值; 如果生命值大于零, 根据各个方向的速度更新位置; 如果生命值小于零, "删除"粒子 -- 实际是重新 初始化所有数值.
if (m_aP[i].m_fLife > 0.0)
{
fRand = randf(0.0f, 0.2f);
m_aP[i].m_fLife -= fRand;
m_aP[i].m_vPos.x += m_aP[i].m_vVel.x;
m_aP[i].m_vPos.y += m_aP[i].m_vVel.y;
m_aP[i].m_vPos.z += m_aP[i].m_vVel.z;
}
else
{
m_aP[i].Init(g_nType);
}
11.1.3 绘制控制
在上集的基础上, 在渲染粒子的时候关闭了Depth-Buffer的更新标志, 防止粒子因为离视点的远近关系, 远的粒子被近的粒子覆盖(会出现黑黑的正方形边框). 同样在渲染完毕后开启标志, 防止对后面的渲染有影响.
VOID CPSystem::EnterPSprite()
{
FLOAT fSize = FLOAT(g_fSet * g_fSize);
FLOAT fMin = FLOAT(g_fMin * g_fSize);
m_pD3DDev->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
m_pD3DDev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
m_pD3DDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
m_pD3DDev->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE);
m_pD3DDev->SetRenderState(D3DRS_POINTSCALEENABLE, TRUE);
m_pD3DDev->SetRenderState(D3DRS_POINTSIZE, FTOD(fSize));
m_pD3DDev->SetRenderState(D3DRS_POINTSIZE_MIN, FTOD(fMin));
m_pD3DDev->SetRenderState(D3DRS_POINTSCALE_A, FTOD(0.0f));
m_pD3DDev->SetRenderState(D3DRS_POINTSCALE_B, FTOD(0.0f));
m_pD3DDev->SetRenderState(D3DRS_POINTSCALE_C, FTOD(1.0f));
}
VOID CPSystem::ExitPSprite()
{
m_pD3DDev->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
m_pD3DDev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
m_pD3DDev->SetRenderState(D3DRS_POINTSPRITEENABLE, FALSE);
m_pD3DDev->SetRenderState(D3DRS_POINTSCALEENABLE, FALSE);
}
如果整个渲染过程都不需要Depth-Buffer和Z-Buffer, 在创建IDirect3DDevice9时将创建的参数D3DPRESENT_PARAMETERS的EnableAutoDepthStencil直接设为FALSE.这样就不必每次渲染粒子要开/关D3DRS_ZWRITEENABLE标志.
D3DPRESENT_PARAMETERS d3dpm;
FillMemory(&d3dpm, sizeof(D3DPRESENT_PARAMETERS), 0);
//...
d3dpm.EnableAutoDepthStencil = FALSE;
//...
下面的例子都需要开/关D3DRS_ZWRITEENABLE标志
11.2 简单的喷泉
11.2.1 参数初始化值
(1). 初始位置一般是世界坐标原点.
(2). 速度; 首先有向上的Y轴速度, 然后在重力加速度的作用下Y轴速度变成负值, 也就是Y速度开始是正的, 随时间慢慢变小到负值. 例子中很偷懒, Y轴速度只是每帧减小一随机数值.
(3). 生命值; 个人爱好决定.
(4). 颜色; 个人爱好决定.
m_vPos.x = 0.0; // 初始位置是原点
m_vPos.y = 0.0;
m_vPos.z = 0.0;
m_vVel.x = randf(-0.02f, 0.02f);
m_vVel.y = randf(0.1f, 0.2f); // 初始Y值为正
m_vVel.z = randf(-0.02f, 0.02f);
m_fLife = randf(8.0, 16.0);
11.2.2 过程控制
每一帧Y值减去一数值来模拟重力加速度的作用.
m_aP[i].m_fLife -= fRand;
fRand = randf(-0.004f, 0.0f); // 减小的速度值随机生成
m_aP[i].m_vVel.y += fRand;
m_aP[i].m_vPos.x += m_aP[i].m_vVel.x;
m_aP[i].m_vPos.y += m_aP[i].m_vVel.y;
m_aP[i].m_vPos.z += m_aP[i].m_vVel.z;
11.3 简单的烟花
11.3.1 参数初始化值
烟花一般是五颜六色的, 沿一圆心球状向外扩散, 在重力加速度的作用下慢慢下降, 所以烟花的初始化数值比较特殊,
FLOAT fAlpha = randf((-D3DX_PI / 2), (D3DX_PI / 2));
FLOAT fBeta = randf(0.0, D3DX_PI * 2);
m_vPos.x = 0.0;
m_vPos.y = 10.0; // 初始位置(0, 10, 0)
m_vPos.z = 0.0;
m_vVel.x = cos(fAlpha) * sin(fBeta);
m_vVel.y = sin(fAlpha);
m_vVel.z = cos(fAlpha) * cos(fBeta);
m_fLife = 8.0;
对于速度参数, 我们 初始化的数值是单位圆的经度和纬度, 烟花扩散的半径和生命值刚好相反.
11.3.2 过程控制
每一帧粒子的位置是以初始位置为圆点, 生命值的相反数(这里是10.0 – life), 同时Y轴还要减去模拟 重力加速度的作用, Y轴相应减去一数值
FLOAT fSize = 10.0f - m_aP[i].m_fLife;
m_aP[i].m_fLife -= 0.02f;
m_aP[i].m_vPos.x = fSize * m_aP[i].m_vVel.x;
m_aP[i].m_vPos.y = 10.0f + fSize * m_aP[i].m_vVel.y - fSize * 0.6f;
m_aP[i].m_vPos.z = fSize * m_aP[i].m_vVel.z;
11.4 简单的烟雾
和喷泉相似, 只是作为气态的物质, 忽略了重力加速度的作用, 不必每帧都要减小Y方向的速度值.
11.5 雷射光线
将粒子排列在一直线上, 在直线上的点每增加一单位X数值, Y和Z轴的增量是一常数, 这里选取 x : y : z = 1 : 1 : 1 的直线.
11.5.1 参数初始化值
m_vPos.x = 0.0; // 与位置和速度无关
m_vPos.y = 0.0;
m_vPos.z = 0.0;
m_vVel.x = 0.0;
m_vVel.y = 0.0;
m_vVel.z = 0.0;
m_fLife = randf(16.0, 32.0);
11.5.2 过程控制
fRand = randf(-0.4f, 0.4f); // x, y, z 增量相同
m_aP[i].m_vPos.x += fRand;
m_aP[i].m_vPos.y += fRand;
m_aP[i].m_vPos.z += fRand;
11.6 粒子系统实际应用的例子
11.6.1 例子说明
例子只使用了粒子的位置, 速度, 生命和颜色属性, 实际使用的粒子系统中粒子可能包含更多的属性, 这样可以实现更绚丽的效果.
粒子数量的多少也影响效果, 我们的例子中最多使用了128个粒子, 每帧渲染的粒子数量的上限依赖与硬件.
game10 project -- game11 project 中, 为代码实现的简单, 有少量的hard code.
使用鼠标点取不同的文字来观看不同的粒子系统的效果, 点击粒子大小和速度框中的箭头来改变粒子的大小和速度. 使用方向键控制视点的左右和上下移动, 鼠标的滚轮控制视点离物体的距离, ESC键退出.
空格键切换自动旋转, 因为没有加按键的延迟时间, 所以空格键的自动旋转切换速度很快, 需要快速的按动, 如没有切换成功再按.
第十一集 小结
加入些物体学的知识, 会让粒子系统更真实.