您的位置:首页 > 运维架构 > 网站架构

【Ogre引擎架构】第七讲 粒子系统-爆破特效

2015-10-04 18:18 567 查看
粒子系统作为一款图形引擎特效系统的核心而存在,Ogre的粒子系统经过专门的扩展插件的研发而变得功能强筋,绝不仅仅是一些OpenGL教材上的那些浅显的烟雾,喷泉效果,确切的说粒子系统主要还是一种架构设计的问题,本身所用算法含量还是比较有限的。这一讲,作者将通过爆破特效的实现过程,分析一下系统的实现原理和流程。

首先按照惯例,先来爆破效果的效果图(由于图片alpha通道问题,有一些锯齿效果),分别是爆破的几个阶段(起,中,末):







实现效果只需要一张图片:



通常一个粒子系统的构成是:System-Technique-Emitter-Affector-Renderer

System包含若干子系统Technique,Technique包含若干Emitter,Affector,Renderer,以及存储Emitter产生的粒子,Renderer负责根据粒子的属性信息产生相应的可渲染对象。

system explosion
{
category                                    Explosion
technique Fire
{
visual_particle_quota                   300
material                                PU/Nucleus
renderer                                billboard
{
billboard_type                      oriented_self
}
emitter                                 Point FireEmitter
{
emission_rate                       120
angle                               360
time_to_live                        1.5
velocity                            1.8
duration                            0.1
all_particle_dimensions             dyn_random
{
min                             0.6
max                             2.4
}
color                              0.8 0.8 0.8 1
}
affector                                Scale
{
xyz_scale                           dyn_curved_linear
{
control_point                   0 60
control_point                   0.3 0.6
control_point                   1 0
}
}
affector                                TextureRotator
{
use_own_rotation                    true
rotation                            dyn_random
{
min                             0
max                             10
}
rotation_speed                      0
}
affector                                Color
{
time_color                         0    1 1 1 1
time_color                         0.3  0.5 0.5 0.5 1
time_color                         1.5    0.4 0.4 0.4 0.2
color_operation                    multiply
}
}

}
粒子系统对应的配置文件就是上面的结构。

System类:

class ParticleSystem:public Tomo::StringInterface,public Tomo::MovableObject
{
public:
ParticleSystem(const string& name,const string& resGroup);
virtual ~ParticleSystem();

virtual const string& GetMovableType() const ;
//render queue
virtual void UpdateRenderQueue(Tomo::RenderQueue* queue);
virtual const Tomo::AxisAlignedBox& GetBoundingBox() const {return m_AABB;}

virtual void NotifyAttached(Tomo::Node* parent);
//notify camera
virtual void NotifyCurrentCamera(Tomo::Camera* cam);

typedef vector<ParticleTechnique*> Techniques;
void	AddTechnique(ParticleTechnique* tech);

float GetTimeElapsedSinceStart() const;

void Update(float timeElapsed);
protected:
string m_ResourceGroup;
Tomo::AxisAlignedBox m_AABB;
Tomo::Controller<float>* m_pTimeController;

Techniques m_Techniques;

float m_TimeElapsedSinceStart;
};
void ParticleSystem::ExecuteTriggerEmitters(ParticleEmitter* emitter,unsigned short requested,float timeElapsed)
{
if(!requested || !m_FreeParticles.size())
return ;
for(unsigned short i=0;i<requested;++i){
Particle* p(0);
string emitterName = emitter->GetEmittedEmitter();
if("" == emitterName)
p = CreateParticle();
else
;

if(!p)
return ;
emitter->InitParticle(p);

m_pRenderer->NotifyParticleEmitted(p);
}
}

void ParticleSystem::Expire(float timeElapsed)
{
Particle* p(0);
for(ActiveParticleList::iterator it=m_ActiveParticles.begin();
it!=m_ActiveParticles.end();){
p = *it;
if(p->timeToLive < timeElapsed){
m_FreeParticles.splice(m_FreeParticles.end(),m_ActiveParticles,it++);
}else{
p->timeToLive -= timeElapsed;
++it;
}
}
}

ParticleIterator ParticleSystem::GetIterator()
{
return ParticleIterator(m_ActiveParticles.begin(),m_ActiveParticles.end());
}

void ParticleSystem::TriggerAffecters(float timeElapsed)
{
ParticleAffector* affector(0);
for(ParticleAffectorList::iterator it=m_vAffectors.begin();
it!=m_vAffectors.end();++it){
affector = *it;
affector->AffectParticles(this,timeElapsed);
}
}
上面的TriggerEmitter,TriggerAffector分别展示了,发射器Emiiter和影响器Affector的应用过程。

Emitter类,Affector类:

void ParticleEmitter::InitParticle(Particle* pParticle)
{
Vector3 Off;
Off.x = 0.8f*Math::SymmetricRandom();
Off.y = 0.8f*Math::SymmetricRandom();
//Off.z = 1.f*Math::SymmetricRandom();

pParticle->position = m_Position+Off;
InitParticleDirection(pParticle);
pParticle->timeToLive = m_MaxTTL;
float dim = m_pDynAllParticleDimens->GetValue();
pParticle->width = dim;
pParticle->height = dim;
pParticle->color = m_Color;
pParticle->origin_color = m_Color;
}
void ColorAffector::Affect(ParticleTechnique* technique,Particle* pParticle,float timeElapsed)
{
ColorValue time_color;
float time = technique->GetParentSys()->GetTimeElapsedSinceStart();
TimeColorMap::iterator it = m_mTimeColors.begin();
for(;it!=m_mTimeColors.end();++it){
if(time < it->first){
if(it == m_mTimeColors.begin())
break;
else{
--it;
break;
}
}
}
if(it == m_mTimeColors.end())
--it;
TimeColorMap::iterator it2 = it;
++it2;
if(it2 != m_mTimeColors.end()){
time_color = it->second+(it2->second - it->second)*((time-it->first)/(it2->first-it->first));
}else{
time_color = it->second;
}
pParticle->color = pParticle->origin_color*time_color;
}
void ScaleAffector::Affect(ParticleTechnique* technique,Particle* pParticle,float timeElapsed)
{
if(m_pDynXYZScale){
float scale = m_pDynXYZScale->GetValue(technique->GetParentSys()->GetTimeElapsedSinceStart())*timeElapsed;
pParticle->width += scale;
pParticle->height += scale;
//
}
}
void TextureRotatorAffector::Affect(ParticleTechnique* technique,Particle* pParticle,float timeElapsed)
{
if(m_pDynRotator){
float angle = m_pDynRotator->GetValue()*timeElapsed;
angle /= 2*PI;
if(pParticle->m_ZRotate)
angle*=-1.f;
Tomo::Matrix rot;
rot.MakeRotateZ(angle);

pParticle->direction = rot*pParticle->direction;
}
}
Renderer类,负责产生可渲染的粒子对象:(读者可以自行参考Ogre的BillboardParticleRenderer类)

void BillboardParticleRenderer::UpdateRenderQueue(RenderQueue* queue,list<Particle*>& currentParticles,bool cullIndividual)
{
m_pBillboardSet->SetCullIndividually(cullIndividual);

//update billboard set geometry
m_pBillboardSet->BeginBillboards(currentParticles.size());
Billboard bb;
for(list<Particle*>::iterator it=currentParticles.begin();it!=currentParticles.end();++it){
Particle* p = *it;
bb.m_Pos = p->position;
bb.m_Color = p->color;
bb.m_Direction = p->direction;
bb.m_Width = p->width;
bb.m_Height = p->height;
m_pBillboardSet->InjectBillboard(bb);
}
m_pBillboardSet->EndBillboards();
m_pBillboardSet->UpdateRenderQueue(queue);
}
void BillboardSet::InjectBillboard(const Billboard& bb)
{
if(m_NumVisibleBillboards == m_PoolSize)
return ;
//if(!BillboardVisible(bb))
//	return ;
if(m_PointRendering){
GenVertices(0,bb);
}else{
//gen billboard axes
GenBillboardAxes(&m_CamX,&m_CamY,&bb);
if(m_AllDefaultSize){
//gen offsets
GenVertOffsets(m_DefaultWidth,m_DefaultHeight);
}else
GenVertOffsets(bb.m_Width,bb.m_Height);
//gen vertices
GenVertices(m_VOffset,bb);
}
++m_NumVisibleBillboards;
}
void BillboardSet::GenVertices(const Vector3* const offsets,const Billboard& b)
{
if(m_PointRendering){
*m_pLockMainBuffer++ = b.m_Pos.x;
*m_pLockMainBuffer++ = b.m_Pos.y;
*m_pLockMainBuffer++ = b.m_Pos.z;

RGBA color = b.m_Color.GetAsRGBA();
RGBA* pColor = static_cast<RGBA*>(static_cast<void*>(m_pLockMainBuffer));
*pColor++ = color;

m_pLockMainBuffer = static_cast<float*>(static_cast<void*>(pColor));
}else{
static FloatRect texRect(0.f,0.f,1.f,1.f);

//vertex0
*m_pLockMainBuffer++ = m_VOffset[0].x+b.m_Pos.x;
*m_pLockMainBuffer++ = m_VOffset[0].y+b.m_Pos.y;
*m_pLockMainBuffer++ = m_VOffset[0].z+b.m_Pos.z;
//color
RGBA color = b.m_Color.GetAsRGBA();
RGBA* pColor = static_cast<RGBA*>(static_cast<void*>(m_pLockMainBuffer));
*pColor++ = color;
m_pLockMainBuffer = static_cast<float*>(static_cast<void*>(pColor));
//tex coords
*m_pLockMainBuffer++ = texRect.left;
*m_pLockMainBuffer++ = texRect.top;

//vertex1
*m_pLockMainBuffer++ = m_VOffset[1].x+b.m_Pos.x;
*m_pLockMainBuffer++ = m_VOffset[1].y+b.m_Pos.y;
*m_pLockMainBuffer++ = m_VOffset[1].z+b.m_Pos.z;
//color
pColor = static_cast<RGBA*>(static_cast<void*>(m_pLockMainBuffer));
*pColor++ = color;
m_pLockMainBuffer = static_cast<float*>(static_cast<void*>(pColor));
//tex coords
*m_pLockMainBuffer++ = texRect.right;
*m_pLockMainBuffer++ = texRect.top;

//vertex2
*m_pLockMainBuffer++ = m_VOffset[2].x+b.m_Pos.x;
*m_pLockMainBuffer++ = m_VOffset[2].y+b.m_Pos.y;
*m_pLockMainBuffer++ = m_VOffset[2].z+b.m_Pos.z;
//color
pColor = static_cast<RGBA*>(static_cast<void*>(m_pLockMainBuffer));
*pColor++ = color;
m_pLockMainBuffer = static_cast<float*>(static_cast<void*>(pColor));
//tex coords
*m_pLockMainBuffer++ = texRect.left;
*m_pLockMainBuffer++ = texRect.bottom;

//vertex3
*m_pLockMainBuffer++ = m_VOffset[3].x+b.m_Pos.x;
*m_pLockMainBuffer++ = m_VOffset[3].y+b.m_Pos.y;
*m_pLockMainBuffer++ = m_VOffset[3].z+b.m_Pos.z;
//color
pColor = static_cast<RGBA*>(static_cast<void*>(m_pLockMainBuffer));
*pColor++ = color;
m_pLockMainBuffer = static_cast<float*>(static_cast<void*>(pColor));
//tex coords
*m_pLockMainBuffer++ = texRect.right;
*m_pLockMainBuffer++ = texRect.bottom;
}
}
GenVertices函数中对HardwareBuffer的填充,参考【第四讲-HardwareBuffer】。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: