Directx9粒子的点滴
- 粒子的定义
粒子是dx9的基本图元,叫点精灵:Point Sprite。点精灵也没啥特别,只是一个概念而已,在这个概率里,你按照MS给你指定的流程去渲染buffer就认为你做了点精灵操作了,大概就是本文中红色文字那几行相关代码就表明了是一个粒子操作。JST SOSO.
- 创建
1
hr = device->CreateVertexBuffer(
2
_vbSize * sizeof(Particle),
3
D3DUSAGE_DYNAMIC | D3DUSAGE_POINTS | D3DUSAGE_WRITEONLY,
4
Particle::FVF,
5
D3DPOOL_DEFAULT, // D3DPOOL_MANAGED can't be used with D3DUSAGE_DYNAMIC
6
&_vb,
7
0);
- FVF如下
const
DWORD Particle::FVF
=
D3DFVF_XYZ
|
D3DFVF_DIFFUSE;
- 打开渲染状态
1
_device->SetRenderState(D3DRS_LIGHTING, false);
2
_device->SetRenderState(D3DRS_POINTSPRITEENABLE, true);
3
_device->SetRenderState(D3DRS_POINTSCALEENABLE, true);
4
_device->SetRenderState(D3DRS_POINTSIZE, d3d::FtoDw(_size));
5
_device->SetRenderState(D3DRS_POINTSIZE_MIN, d3d::FtoDw(0.0f));
6
7
// control the size of the particle relative to distance
8
_device->SetRenderState(D3DRS_POINTSCALE_A, d3d::FtoDw(0.0f));
9
_device->SetRenderState(D3DRS_POINTSCALE_B, d3d::FtoDw(0.0f));
10
_device->SetRenderState(D3DRS_POINTSCALE_C, d3d::FtoDw(1.0f));
11
12
// use alpha from texture
13
_device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
14
_device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
15
16
_device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
17
_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
18
_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
- 渲染完了,需要还原渲染状态到一般情况
1
_device->SetRenderState(D3DRS_LIGHTING, true);
2
_device->SetRenderState(D3DRS_POINTSPRITEENABLE, false);
3
_device->SetRenderState(D3DRS_POINTSCALEENABLE, false);
4
_device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
- 执行渲染
1
_device->SetFVF(Particle::FVF);
2
_device->SetStreamSource(0, _vb, 0, sizeof(Particle));
3
_device->DrawPrimitive(D3DPT_POINTLIST,_vbOffset,_vbBatchSize);
- 执行帧渲染还有不少学问的:
其一:粒子生命周期。你得指定一个例子生命周期,不然例子不会消失。在帧渲染里,渲染开始之前,你搞个update方法进行相关操作,然后再渲染。这样的好处是update里还可以做其他操作。
1
void Firework::update(float timeDelta)
2

{
3
std::list<Attribute>::iterator i;
4
5
for(i = _particles.begin(); i != _particles.end(); i++)
6
{
7
// only update living particles
8
if( i->_isAlive )
9
{
10
i->_position += i->_velocity * timeDelta;
11
12
i->_age += timeDelta;
13
14
if(i->_age > i->_lifeTime) // kill
15
i->_isAlive = false;
16
}
17
}
18
}
19
其二:粒子属性。VB里只包含了例子的坐标和颜色信息,你得给每个例子指定一个属性,这样在update的时候可以修改属性,例如例子的颜色,位置,年龄,婚否,在世否。然后在渲染的时候去访问VB,并重新给VB的元素赋值。大概像这样:
1
Particle* v = 0;
2
3
_vb->Lock(
4
_vbOffset * sizeof( Particle ),
5
_vbBatchSize * sizeof( Particle ),
6
(void**)&v,
7
_vbOffset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD);
8
9
std::list<Attribute>::iterator i;
10
for(i = _particles.begin(); i != _particles.end(); i++)
11
{
12
if( i->_isAlive )
13
{
14
//
15
// Copy a batch of the living particles to the
16
// next vertex buffer segment
17
//
18
v->_position = i->_position;
19
v->_color = (D3DCOLOR)i->_color;
20
v++; // next element;
21
22
}
23
}
24
25
_vb->Lock(
26
其三:分批渲染。粒子可能有好多万,你不能炫富。你得分批渲染,可以类似这样做:
批处理计数=0;
for 所有顶点的遍历
打开VB缓存
修改顶点属性
批处理计数++
如果批处理计数>=批次处理最大数
关闭顶点缓存
渲染这个批次的顶点
顶点偏移指针进行偏移,指导下一批次的开始
打开VB缓存
批处理计数=0
end 所有定点的遍历
//渲染余下的顶点,这些定点正好是批次处理最大数的余数
关闭顶点缓存
if 批处理计数!=0
DrawPrimitive渲染
以上其实就是一个非常简单的将一个巨量元素进行分批次处理的逻辑。不知道是不是OGRE中的BATCH处理的原型。反正3D API没多聪明,你将巨量元素交给它,它是不会进行智能处理的。这样也好,容易理解多了。也方便我们智能处理。
其四:纹理。
D3DRS_POINTSPRITEENABLE
这个影响默认贴图方式。当设置为true时,贴图会完全贴上Point Sprites粒子;如果设置为false,那么,当粒子的顶点结构中有UV坐标时贴图才有效,并且根据不同粒子的UV坐标贴到不同的位置。默认是FALSE。
D3DRS_POINTSCALEENABLE
控制了粒子是否执行3D视图变换。如果不变换,粒子不管多远都一样大,有点像文字那样大小固定了。默认是FALSE。
粒子渲染的api其实非常简单,渲染逻辑也不复杂,简单来说就是在帧渲染中用批处理思想执行一个update操作和一个render操作。update交给cpu更新啊,render交给gpu处理啊,cpu和gpu你都上不起啊。
也算玩了一把粒子。