D3D中的粒子系统(2)

前端之家收集整理的这篇文章主要介绍了D3D中的粒子系统(2)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

14.2粒子系统的组成

粒子系统是粒子的集合,用来保存和显示这些粒子。粒子系统维护所有粒子的全部属性,影响系统中的所有粒子:粒子的尺寸,起始的位置及应用在粒子上的纹理等。粒子系统的方法负责更新、显示、杀死和创建粒子。

虽然不同的具体(与抽象是相对的)粒子系统有不同的行为,我们归纳并找到一些所有的粒子系统共有的基本属性,我们把这些公共的属性放到一个抽象的cParticleSystem基类,它是我们所有的具体粒子系统的父类,现在让我们看一下cParticleSystem类:

class cParticleSystem
{
protected:
IDirect3DDevice9* m_device;
D3DXVECTOR3 m_origin;
cBoundingBox m_bounding_Box;
float m_emit_rate;// rate new particles are added to system
float m_size;// size of particles
IDirect3DTexture9* m_texture;
IDirect3DVertexBuffer9* m_vertex_buffer;
list<sParticleAttribute> m_particles;
int m_max_particles;// max allowed particles system can have

// following three data elements used for rendering the particle system efficiently

DWORD m_vb_num;
// particle number in vertex buffer
DWORD m_vb_offset; // offset in vertex buffer to lock
DWORD m_vb_batch_num; // number of vertices to lock starting at m_vb_offset

public:
cParticleSystem();
virtual ~cParticleSystem();

virtualbool init(IDirect3DDevice9* device,constchar* texture_filename);
virtualvoid reset();

// sometimes we don't want to free the memory of a dead particle,but rather respawn it instead.
virtualvoid reset_particle(sParticleAttribute* particl_attr) = 0;
virtualvoid add_particle();

virtualvoid update(float time_delta) = 0;

virtualvoid pre_render();
virtualvoid render();
virtualvoid post_render();

bool is_empty();
bool is_dead();

protected:
virtualvoid remove_dead_particles();
};

一些数据成员:

·m_origin粒子系统的原点, 这是粒子系统产生时的位置。

·m_bounding_Box创建粒子系统使用的边界盒,用于限制粒子的活动范围。例如,假如我们让雪系统只落在一个围绕高山的峰顶的体积内,我们会定义一个包括这个体积的边界盒,出界的粒子将会被杀死。

·m_emit_rate增加到系统中的粒子的速度。通常的标准是每秒。

·m_size系统中所有粒子的尺寸。

·m_particles系统中粒子属性的一个列表。 我们用这个列表创建,释放及更新粒子。 当我们准备画粒子时,我们COPY列表节点的一部分到顶点缓存并画粒子,同时我们COPY另外一批粒子,然后重复这一过程直到绘制完所有粒子。

·m_max_particles在给定的时间内,系统中允许的粒子最大数。例如,如果创建粒子的速度比释放快的话,随着时间的增长粒子的数量将会是巨大的,这个成员将避免出现这样的问题。

·m_vb_num在给定的时间内顶点缓存中能够保存的粒子的数量,这个值与实际的粒子系统中的粒子数量无关。

注意:m_vb_offsetm_vb_batch_num数据成员在渲染粒子系统时使用,我们在稍后讨论。

方法

cParticleSystem/ ~cParticleSystem用来初始化默认值和用来释放设备接口 (vertex buffer,texture)

cParticleSystem::cParticleSystem()
{
m_device = NULL;
m_vertex_buffer = NULL;
m_texture = NULL;
}

cParticleSystem::~cParticleSystem()
{
safe_release<IDirect3DVertexBuffer9*>(m_vertex_buffer);
safe_release<IDirect3DTexture9*>(m_texture);
}

init这个方法做与设备无关的初始化工作,比如创建用来保存点精灵的顶点缓存或创建纹理。

bool cParticleSystem::init(IDirect3DDevice9* device,const char* texture_filename)
{
// Vertex buffer's number does not equal the number of particles in our system.
// We use the vertex buffer to draw a portion of our particles at a time.
// The arbitrary number we choose for the vertex buffer is specified by the m_vb_num variable.

m_device = device;

HRESULT hr;

hr = device->CreateVertexBuffer(
m_vb_num *
sizeof(sParticle),
D3DUSAGE_DYNAMIC | D3DUSAGE_POINTS | D3DUSAGE_WRITEONLY,
PARTICLE_FVF,
D3DPOOL_DEFAULT,0)">// D3DPOOL_MANAGED can't be used with D3DUSAGE_DYNAMIC
&m_vertex_buffer,
NULL);

if(Failed(hr))
{
MessageBox(NULL,"CreateVertexBuffer() - Failed","ParticleSystem",MB_OK);
returnfalse;
}

hr = D3DXCreateTextureFromFile(device,texture_filename,&m_texture);

;
}

true;
}

o 注意: 我们使用动态的顶点缓存(D3DUSAGE DYNAMIC)。 因为我们需要在每帧中更新我们的粒子,意思是我们将会去存取顶点缓存的内存,回想一下,访问一个静态的顶点缓存慢得不可接受, 所以我们使用动态的顶点缓存。

o 查看我们用过的 D3DUSAGE_POINTS标记,它说明顶点缓存将保存点精灵。

o 顶点缓存的尺寸是由m_vb_num预先确定的,而且与系统中粒子的数量无关。 也就是说,m_vb_num将小于等于系统中粒子的数量。 这是因为渲染粒子系统是一批一批的,不是一次渲染全部。

o 我们使用默认的内存池(pool)代替通常使用的托管内存池,因为动态顶点缓存不能用在托管内存池中。

reset这个方法重新设置系统中每个粒子的属性:

void cParticleSystem::reset()
{
for (list<sParticleAttribute>::iterator iter = m_particles.begin(); iter != m_particles.end(); iter++)
reset_particle(&(*iter));
}

reset_particle这个方法重新设置粒子的属性如何重设粒子的属性,这依赖于具体粒子系统的特性。因此我们定义这个方法为虚拟的,等待子类去实现。

add_particle这个方法用来在系统中增加一个粒子。在增加它到粒子列表之前,使用reset_particle方法先初始化粒子:

cParticleSystem::add_particle()
{
sParticleAttribute attr;
reset_particle(&attr);

m_particles.push_back(attr);
}

update这个方法更新系统中所有的粒子。因为这个的方法的执行取决于具体粒子系统的特性,因此我们定义这个方法为抽象的,等待子类去实现。

render这个方法用来显示系统中所有的粒子。

pre_render用它来初始化渲染状态,在渲染前设置。 因为系统与系统之间是不同的,所以我们定义它为虚拟的。 默认将执行下列代码:

cParticleSystem::pre_render()
{
m_device->SetRenderState(D3DRS_LIGHTING,FALSE);
m_device->SetRenderState(D3DRS_POINTSPRITEENABLE,TRUE);
m_device->SetRenderState(D3DRS_POINTSCALEENABLE,TRUE);
m_device->SetRenderState(D3DRS_POINTSIZE,float_to_dword(m_size));
m_device->SetRenderState(D3DRS_POINTSIZE_MIN,float_to_dword(0.0f));

// control the size of the particle relative to distance
m_device->SetRenderState(D3DRS_POINTSCALE_A,float_to_dword(0.0f));
m_device->SetRenderState(D3DRS_POINTSCALE_B,float_to_dword(0.0f));
m_device->SetRenderState(D3DRS_POINTSCALE_C,float_to_dword(1.0f));

// use alpha from texture
m_device->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
m_device->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);

m_device->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
m_device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
m_device->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
}

注意:我们使用alpha混合渲染,以便设置纹理的alpha通道,来设置纹理像素的透明,用它产生多种效果。一种特殊的情况是:获得象纹理那样的非矩形的粒子。例如,获得一个圆形“雪球形”的粒子,我们使用一个简单的带有alpha通道的纹理,它看上去是背景为黑色的带有白色圆形的样子。因此,显示出来时只是一个白圆,这比白色的矩形纹理要好。

post_render用它去保存所有渲染状态。因为系统与系统间是不同的,所以我们定义它为虚拟的。默认将执行下列代码:

cParticleSystem::post_render()
{
m_device->SetRenderState(D3DRS_LIGHTING,TRUE);
m_device->SetRenderState(D3DRS_POINTSPRITEENABLE,FALSE);
m_device->SetRenderState(D3DRS_POINTSCALEENABLE,FALSE);
m_device->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE);
}

is_empty如果为True则在当前的系统中没有粒子, 否则为false.

cParticleSystem::is_empty()
{
m_particles.empty();
}

is_dead如果为True则系统中的所有粒子都是死的,否则为false

cParticleSystem::is_dead()
{
for(list<sParticleAttribute>::iterator iter = m_particles.begin(); iter != m_particles.end(); iter++)
{
// Is there at least one living particle? If yes,the system is not dead.
(iter->is_alive)
// No living particles found,the system must be dead.
;
}

remove_dead_particles搜索_particle性表,从表中杀死并删除粒子。

cParticleSystem::remove_dead_particles()
{
list<sParticleAttribute>::iterator iter = m_particles.begin();

while (iter != m_particles.end())
{
if (! iter->is_alive)
// erase returns the next iterator,so no need to increment to the next one ourseleves.
iter = m_particles.erase(iter);
else
iter++;
// next in list
} }

猜你在找的VB相关文章