我翻译的,费了好大尽。可能对大家有用吧。 Particle例子系统 许多自然现象都存在一些小的粒子做一些相似的运动。(例如,下落的雪花,火焰里的火苗,未来战争枪械里的子弹).粒子系统就使用这样的模型。 目标 学习在Direct3D中如何描述一个粒子 设计灵活粒子系统的底层类来包括所有粒子的通用属性。 具体出来粒子系统,命名为雪,爆炸,枪粒子。 粒子和点精灵 在数学上粒子通常是些非常小的对象。它可以用原始的点(D3DPT_POINTLIST类型) 来描述,来显示一个粒子。然而原始的点仅仅是单个的像素。这给我们带来了不灵活,我们喜欢各种各样的大小和每种纹理的例子。在DirectX 8.0之前都只能局限在使用原始点上面。后来,程序员将billboard来显示粒子。 billboard是印在世界矩阵正方向上的,所以它总是朝向摄影机。 Direct3D 9.0引入了特殊的原始点叫做点精灵,来制作大部分的粒子系统。不像普通的原始点,点精灵可以使用纹理贴图和改变大小。不像billboard,我们可以用单个点来描述点精灵。存储在内存并处理,因为我们仅仅存储和处理顶点覆盖的四个需要存储的billboard(quad) Structure Format结构格式 我们使用顶点结构来描述粒子位置和颜色: struct Particle{ D3DXVECTOR3 position; D3DCOLOR color; static const DWORD FVF; } const DWORD Particle::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE; 这个结构简单的存储它的位置和颜色。依赖于你应用的需要,你可以存储纹理坐标。我们在吓一章讨论点精灵的纹理。还可以添加浮点变量来指定粒子的大小。我们必须添加D3DFVF_PSIZE标志到我们的灵活顶点格式。每个粒子都有维持自己的大小是有用的,因为它允许我们指定和改变粒子的大小。然而,大部分图形卡不支持控制粒子的大小影响了我们。检测D3DFVFCAPS_PSIZE为在FVFCaps成员。取而代之的,我们控制每个粒子的渲染状态,一会你就可以看件。下面是控制大小的结构例子。 struct Particle{ D3DXVECTOR3 position; D3DCOLOR color; float size; static const DWORD FVF; }; const DWORD Particle::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_PSIZE; 我们要注意操作每个粒子的代销需要顶点着色,甚至如果D3DFVFCAPS_PSIZE是不被支持的。顶点着色将在第五部分介绍。 点精灵渲染状态 点精灵很大的控制渲染状态。让我们回顾下渲染状态。 D3DRS_POINTSPRITEENABLE-一个Boolean值。默认为false 指定当前实体将贴图设置到点精灵 假则需要指定纹理坐标到点精灵。 _device->SetRenderState(D3DRS_POINTSPRITEENABE,true D3DRS_POINTSCALEENABLE一个Boolean值,默认false 真则改变视图单元的点大小。视图单元简单的3D点在摄影机空间。点精灵的大小将被记录在此空间。依赖于如何远离他,像其他的粒子对象看起来非常的小。 若假点大小仅仅在屏幕空间。,屏幕空间是像素空间。所以如果你指定假例如,设置点精灵大小为3,点将变为3x3的像素区。 _device->SetRenderState(D3DRS_POINTSCALEENABLE,true) D3DRS_POINTSIZE-用来指定点精灵的大小。这个值也是在视图空间或屏幕空间,依赖于D3DRS_POINTSCALEENABLE状态的设定。下面的代码小片设置为2.5单元。 _device->SetRenderState(D3DRS_POINTSIZE,d3d::FtoDw(2.5f)); d3d::FtoDw是一个我们添加到d3dUtility中的转换float到DWORD的函数。我们必须这样做,因为调用IDirect3DDevice9::SetRenderState假定为为DWORD值而不是float DWORD d3d::FtoDw(float f){ return *((DWORD*)&f); } D3DRS_POINTSIZE_MIN - 指定点精灵大小的最小值。例如可以设置为0.2 _device->SetRenderState(D3DRS_POINTSIZE_MIN,d3d::FtoDw(0.2f); D3DRS_POINTSCALE_a,D3D_POINTSCALE_B,D3DRS_POINTSCALE_C- 三个常量控制点精灵改变的距离,这个距离是从点精灵到摄影机的距离。 Direct3D使用下面的规则计算点精灵最后的大小 FinalSize = ViewportHeight * Size * sqrt(1/A + B(D) + C(pow(D,2)) FinalSize:距离计算后的点精灵的大小 ViewportHeight:视图的高度 Size:符合D3DRS_POINTSIZE渲染状态的指定值。 A,B,C:符合D3DRS_POINT_SCALE_A,B,C的指定值。 D:视图大小到摄影机的位置的距离。从在原点的摄影机,这个值是 D=sqrt(pow(x,2),pow(y,2),pow(z,2)),x,y,z是点精灵在使徒上的位置。 下面的代码将设置点精灵的常量距离将被设的很小: device->SetRenderState(D3DRS_POINTSCALE_A,d3d::FtoDw(0.0f)); device->SetRenderState(D3DRS_POINTSCALE_B,d3d::FtoDw(0.0f)); device->SetRenderState(D3DRS_POINTSCALE_C,d3d::FtoDw(1.0f));
一个粒子由于很多属性组成,如位置和颜色;实际上,粒子有确定的速度。然而这里附加的属性在粒子上不需要渲染。因此,我们需要在分开的结构上渲染粒子和粒子的属性。当我们创建和更新粒子时,我们使用属性工作,当我们准备渲染时,我们复制粒子结构的位置和颜色。 粒子的实行特殊的粒子是用我们建模得来的。然而,我们可以概括为一个bit,讨论公共的熟悉属性。下面的例子结构包括一些公共的属性。大部分系统不都需要它,一些系统也许需要的没有列出来。 struct Attribute{ D3DXVECTOR3 position; D3DXVECTOR3 velocity; D3DXVECTOR3 accelaration; float leftTime; float age; D3DXCOLOR color; D3DXCOLOR colorFade; bool isAlive; };
一个粒子系统搜集粒子和和可靠的主要显示这些粒子。粒子系统维持在系统中所有粒子的轨迹,像粒子的大小,位置,纹理使用等。工作方法,粒子更新,显示杀掉并创建粒子。 虽然不同的的粒子系统有不同的行为,我们可以概括的找到些公共的属性。我们设置这个公共的属性为抽象的基类PSystem,它是具体粒子系统的父类。让我们看下这个类。 class PSystem{ 选择的数据成员: _origin-系统的原点。系统原点的粒子。 _boundingBox-范围盒,粒子使用范围盒来限制粒子。例如,假设我们希望下雪系统仅仅降落;我们定义范围盒子覆盖范围,超过范围将被杀掉。 _emitRate这个速度是新的。通常每秒量一次。 大小-系统中所有粒子的大小。 _particles-系统中属性的列表。我们用这个表创建,更新和销毁粒子。当我们准备画粒子时,我们复制粒子结点里的顶点缓冲的一部分。然后,复制另一批来画粒子,知道所有的粒子被画出来,我们重复这一过程。这样很单调,我们将在14.2.1来解释画的过程。 maxParticles-粒子系统允许存在的时间,例如,一个粒子很快被创建和销毁,我们结束粒子超过的时间。这个值帮助我们避免一些情节。 _vbSize-粒子顶点缓冲区的大小。这个值不以来实际粒子系统的值。 注意:数据成员_vbOffset和_vbBatchSize在粒子系统中使用。我们将在14.2.1来讨论。 方法: PSystem/~PSystem-构造函数和析构函数,初始化顶点缓冲,纹理等。 init-这个方法不依赖初始化工作,像创建顶点缓冲区存储点精灵和创建纹理等。顶点缓冲区的创建包括一些标志我们必须讨论它: hr = device->CreateVertexbuffer(vbSize * sizeof(Particle), D3DUSAGE_DYNAMIC | D3DUSAGE_POINTS|D3DUSAGE_WRITEONLY, Particle::FVF,D3DPOOL_DEFAULT,&vb,0); 注意:这里使用动态的顶点缓冲。这是因为我们需要每帧更新我们的缓冲,这意味着我们需要访问顶点缓冲的成员。回想起访问静态的顶点缓冲被拒绝且缓慢,我们因此用动态的顶点缓冲。 遵守,我们使用D3DUSAGE_POINTS标志,我们将指定使用点精灵。 顶点缓冲的大小被_vbSize预定义,在系统中has nothing to do with the number of particles。确切的说,_vbSize很少和系统中粒子数相等。这是因为我们在粒子系统中渲染一批粒子而不是一个。我们在14.2.1解释渲染过程。 我们使用默认内存池来取代通常的管理内存池因为动态顶点缓冲没有放在管理内存池的地方。 reset-这个方法重设在系统中每个粒子的属性。 void PSystem::reset(){ std::list::iterator i; for(i=particles.bengin()i!=particles.end();i++){ resetParticle(&(*i)); } } resetParticle-这个方法重新设置粒子属性。在指定的特殊粒子系统上怎样重设粒子的属性将依赖指定的特殊的粒子系统。因此,我们这个方法抽象成实现的subclass。 addParticle-这个粒子系统的方法。它使用resetParticle方法在添加到列表前就初始化粒子。 void PSystem::addParticle(){ Attribute attribute; resetParticle(&attribute); particles.push_back(attribute); } update-这个方法在系统中更新所有的粒子。它的实现依赖于指定的系统,我们定义这个抽象的方法。 render-这个方法在系统中显示所有的粒子。实现是quite involve, 我们在14.2.1来讨论它。 preRender-必须在初始化渲染状态前使用它。从系统到系统,我们让它是虚的。默认实现如下: device->SetRenderState(D3DRS_LIGHTING,false); device->SetRenderState(D3DRS_POINTSRITEENABLE,true) device->SetRenderState(D3DRS_POINTSCALEENABLE,true); device->SetRenderState(D3DRS_POINTSIZE,d3d::FtoDw(size)); device->SetRenderState(D3DRS_POINTSIZE_MIN,d3d::FtoDw(0.0f)); device->SetRenderState(D3DRS_POINTSCALE_A,d3d::FtoDw(0.0f)); device->SetRenderState(D3DRS_POINTSCALE_B,d3d::FtoDw(0.0f)); device->SetRenderState(D3DRS_POINTSCALE_C,d3d::FtoDw(1.0f)); device->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE); device->SetTextureStageState(0,D3DTSS_ALPHOP,D3DTOP_SELECTARG1); ddvice->SetRenderState(D3DRS_ALPHABLENDENABLE,true); device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA); device->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);} 注意,我们开启了Alpha混合所以当前的纹理的Alpha管道被透明成指定的纹理像素。我们使用各种效果;一个特殊的得到粒子不是矩形区域,是纹理。例如,一个雪花的粒子,我们使用白色的纹理加了Alpha通道的黑色圆形。因此,仅仅绕白色举行纹理将显示。 postRender-将恢复任何特殊的渲染状态。从系统到系统将发生变化,我们设置为虚的。默认实现如下: void PSystem::postRender(){ device->SetRenderState(D3DRS_LIGHTING,true); device->SetRenderState(D3DRS_POINTSPRITEENABLE,false); device->SetRenderState(D3DRS_POINTSCALEENABLE,false); device->SetRenderState(D3DRS_ALPHABLENDENABLE,false); } isEmpty-如果当前没有粒子将为真,否则为假。 isDead-系统中每个粒子将死去,否则为假。注意,每个粒子dead 不以为这系统是空的。空意味着系统里没有粒子。死意味这我们的系统里有粒子,但是他们被标志为死。 removeDeadParticles-搜索属性表_particle和从列表中移除任何死的粒子。 void PSystem::removeDeadParticles(){ std::list::iterator i; i = particles.begin(); while(i!=particles.end()){ if(i->isAlive==false){ i=particles.erase(i); } else{ i++; } } } 评论:此方法通常在subclass'的更新方法里移除任何被杀的粒子(标志为死的). 然而,在这些粒子系统中,它也许标志为死被移除它们更好。那样的话,取代的就是重新分配从出生和死去,我们简单的重置死粒子创建新的。这个雪系统我们在14.3中示范了这种技术。 终于翻译完了,看来制约国内水平除了编程能力,就是英语水平也制约着国内游戏制作能力。译自 《Introducation to 3D Game Programming with DirectX 9》 microsoftxiao |
-
2006-09-15