microsoftxiao

记忆 流逝

导航

粒子(二)[译]

绘制粒子系统

因为粒子系统是动态的,我们需要在系统中每帧都更新。直觉上下面的粒子系统给人的效率很低:

创建一个足够大小的顶点缓冲区

在每帧里更新所有的粒子。

复制所有存活的粒子到顶点缓冲

绘制顶点缓冲

这个手动处理,但是不是最有效的。举例来说,在系统中顶点缓冲必须足够大。但是更重要的是在复制所有粒子到顶点缓冲到列表中时图形卡是空闲的。例如,设想我们的系统有10000个粒子;起初我们需要顶点缓冲是10000,完整的内存。In adition the graphics card will sit and do nothing until all 10000 particles in the list are copied to the vertex buffer and we call DrawPrimitive. 这个假设是CPU和GPU不同步工作的很好的例子。

一个好的步骤是在SDK中使用点精灵的离子。PointSprites.exe 在 Summer 2003和DirectX10之间的各SDK版本里有关于点精灵的使用例子。

注意:这是一个简单的描述,但是说明了想法。它假设我们有500个粒子填充在实体上,因为我们经常的没有杀死和创建粒子从帧到帧的变化。例如,假设我们有200个粒子从当前帧复制到左边。因为200粒子不会填充到一个实体片段,我们把它考虑为特殊的情况。这个假设卡仪仅仅在最终的片段开始填充到当前的片段,因为如果没有最终的片段的话,在下个片段将至少执行500个粒子。

Note: This is a simplified description,but it illustrates the idea. It assumes that we will always have 500 particles to file an entire segment,which in reality doesn't happed bacause we are constantly killing and creating particles so the number of particles existing varies from frame to frame.For example ,suppose we only have 200 particles left to copy over and render in the current frame.Because 200 particles won't fill an entire segment, we handle this scenario as o specials cose in the code.This scenario can only happen on the last segment being filled for the current frame bascause if it's not the last segment, that implies there must be at least 500 particles to move onto the next segment.

创建一个fair-sized顶点缓冲(有2000个粒子).我们分开顶点缓冲成片段;举例来说,我们设置500个为一个片段。全局变量i=0,维持一个片段的轨迹。

例如每帧:

A.更新所有的粒子

B.直到所有的粒子都被渲染:

如果顶点缓冲没有满,那么,锁定片段i用D3DLOCK_NOOVERWRITE标志

复制500个粒子到片段i

如果顶点缓冲满了,那么开始在顶点缓冲i=0.b.用D3DLOCK_DISCARD标志锁定。

渲染片段i

增加片段。

评论:回想我们的动态顶点缓冲区,因此我们可以动态锁定D3DLOCK_NOOVERWRITE和 D3DLOCK_DISCARD。这些标志允许我们锁定不被同时渲染的顶点被渲染。举例来说,假设我们渲染片段0,使用D3DLOCK_NOOVERWRITE标志,我们可以锁定和填充片段1同时我们可以渲染片段0,This prevents a rendering stall that otherwise would incur.这样可以防止渲染停止。让GPU一直工作。

这个步骤是更有效率的。起初,我们减少了顶点缓冲区的大小。第二CPU和GPU是一致工作的;因此,我们复制小的一批粒子到顶点缓冲区这时CPU工作,然后我们画小的片段到屏幕,这时GPU工作。然后我们复制下一片到顶点,然后再画。知道所有的粒子被渲染,如你看到的,图形卡在填充期间不再空闲。

我们现在转到我们的渲染的实现计划。使用这个方法很容易渲染粒子系统,我们使用下面的数据成员在PSystem类里。

_vbSize-一系列的顶点缓冲区的粒子。这个值日是不依赖具体粒子系统的。

_vbOffset-这个变量标志偏移量,在粒子里自动的,不是字节)复制到下一个片段。例如,如果一个小一片从0到499个顶点缓冲,偏移量从将是500。

_vbBatchSize-我们定义的小片在粒子里。

我们通过如下代码实现:

void PSystem::render(){
if(!particles.empty())
{
preRender();
device->SetTexture(0,tex);
device->SetFVF(Particle::FVF);
device->SetStreamSource(0,vb,0,sizeof(Particle));
if(vbOffset>=vbSize)
_vbOffset = 0;
Particle* v = 0;
vb->Lock(
vbOffset * sizeof(Particle),
vbBatchSize * sizeof(Particle),
(void**)&v,
vbOffset ? D3DLOCK NOOVERWRITE : D3DLOCK_DISCARD);
DWORD numParticlesInBatch = 0;
std::list::iterator i;
for(i=particles.begin();i!=particles.end();i++){
if(i->isAlive)
{
v->position = i->position;
v->color = (D3DCOLOR)i->color;
v++;
numParticlesInBatch++;
if(numParticlesInBatch==vbBatchSize)
{
vb->Unlock();
device->DrawPrimitive(
D3DPT_POINTLIST,
vbOffset,
vbBatchSize);
vbOffset += vbBatchSize;
if(vbOffset >= vbSize)
vbOffset = 0;
vb->Lock(
vbOffset * sizeof(Particle),
vbBatchSize * sizeof(Particle),
(void**)&v,
vbOffset ? D3DLOCK_NOOVERWRITE:
D3DLOCK_DISCARD);
numParticlesInBatch = 0;
}//end if
}
}
_vb->Unlock();
if(numParticlesInBatch)
{
device->DrawPrimitive(
D3DPT_POINTLIST,
vbOffset,
numParticlesInBatch);
}
_vbOffset += vbBatchSize;
postRender();
}
}
}

随机 Randomness

有一系列的随机的粒子系统。例如,如果我们建造雪,我们不想所有的雪花都下落都一样的,我们想让它近似于真实的,但不是绝对真实。这对于粒子系统是容易的,我们添加下面两个函数到文件。第一个函数返回随机的时间间隔,低范围和高范围:

float d3d::GetRandomFloat(float lowBound,float highBound) { if(lowBound >= highBound) return lowBound; float f = (rand() % 10000) * 0.0001f; return (f* (highBound - lowBound) + lowBound; }

下个函数输出随机的在盒子中定义的最小和最大的点的值

void d3d::GetRandomVector(D3DXVECTOR3 *out, D3DXVECTOR3* min,D3DXVECTOR3* max){ out->x = GetRandomFloat(min->x,max->x); out->y = GetRandomFloat(min->y,max->y); out->z = GetRandomFloat(min->z,max->z); }

注意:记得用srand()产生随机种子。
译自《Introduction to 3D Game Programming with DirectX 9》 microsoftxiao

posted on 2006-04-06 12:31  龙巢NET刀  阅读(532)  评论(0)    收藏  举报