-
简介
在第十九课中我们使用NeHe教程中的方式实现了一个简单的粒子效果,在osg中也提供了一个专门处理粒子系统的库---osgParticle,下面我们就用osgParticle库来实现同样的效果。
在具体实现之前,我们先了解一下osgParticle,osgParticle的粒子系统主要的模块包括如下部分:
- ParticleSystem粒子系统类,继承自osg::Drawable可绘制对象,用来管理粒子的绘制,它包含一个Particle的数组,Particle可以理解为很多粒子中其中的一个。
ParticleSystem利用粒子的模板来产生大量的粒子
- Particle类,刚才已经解释了,Particle可以看做ParticleSystem的一个模板,ParticleSystem依据它产生大量的粒子
- Emitter类:它包含三个部分(一个组合类):即Shooter(发射器:发射出粒子的速度和方向),Counter(计数器:每帧发射多少粒子)以及Placer(放置器:决定粒子初始位置在哪儿)
- Program类:Program类里面包含一个Operator类的数组,用来存放一系列的操作,在对粒子进行渲染之前、渲染中以及渲染后都可以进行一些设置,包括粒子的方方面面(颜色、位置、生命、加速度等等),非常的灵活。我们需要自定义粒子的一些操作实际上是继承Operator类并重写里面的operator函数来完成粒子的一些自定义操作
粒子系统的基本操作过程可以参考osg示例解析之osgParticle,里面对粒子系统的设置过程进行了说明。
同样我们还是需要定义粒子结构体用来更新数据:
- struct Particle
- {
- bool active;
- float life;
- float fade;
- float r;
- float g;
- float b;
- float x;
- float y;
- float z;
- float xi;
- float yi;
- float zi;
- float xg;
- float yg;
- float zg;
- };
- for (int i=0; i<MaxParticles; i++)
- {
- particles[i].active=true;
- particles[i].life=1.0f;
- particles[i].fade=float(rand()%100)/1000.0f+0.003f;
- particles[i].r=colors[int(i*(12.0/MaxParticles))][0];
- particles[i].g=colors[int(i*(12.0/MaxParticles))][1];
- particles[i].b=colors[int(i*(12.0/MaxParticles))][2];
- particles[i].xi=float((rand()%50)-26.0f)*10.0f;
- particles[i].yi=float((rand()%50)-25.0f)*10.0f;
- particles[i].zi=float((rand()%50)-25.0f)*10.0f;
- particles[i].xg=0.0f;
- particles[i].yg=-0.8f;
- particles[i].zg=0.0f;
- }
- //设置粒子模板
- osgParticle::Particle particleTemplate;
- particleTemplate.setShape(osgParticle::Particle::QUAD_TRIANGLESTRIP);
- //负值表示粒子永远不会"死亡"
- particleTemplate.setLifeTime(-1);
- particleTemplate.setPosition(osg::Vec3(0.0, 0.0, 0.0));
- particleTemplate.setRadius(1.0f);
- //设置粒子系统的光照,纹理等(与普通的Drawable设置一样)
- osgParticle::ParticleSystem *particleSystem = new osgParticle::ParticleSystem();
- particleSystem->setDefaultAttributes("Data/Particle.bmp", false, false);
- osg::BlendFunc *blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE);
- particleSystem->getOrCreateStateSet()->setAttributeAndModes(blendFunc,osg::StateAttribute::ON);
- particleSystem->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
- particleSystem->setDefaultParticleTemplate(particleTemplate);
- osgParticle::ModularEmitter *emitter = new osgParticle::ModularEmitter;
- emitter->setParticleSystem(particleSystem);
- osgParticle::PointPlacer *placer = new osgParticle::PointPlacer;
- placer->setCenter(0, 0, 0);
- emitter->setPlacer(placer);
- osgParticle::RandomRateCounter *counter = new osgParticle::RandomRateCounter;
- counter->setRateRange(1000, 1000);
- emitter->setCounter(counter);
- g_counter = counter; //为了设置1000个粒子之后停止发射
- osgParticle::RadialShooter *shooter = new osgParticle::RadialShooter;
- emitter->setShooter(shooter);
- osgParticle::ModularProgram *program = new osgParticle::ModularProgram;
- program->setParticleSystem(particleSystem);
- ColorOption *co = new ColorOption;
- program->addOperator(co);
- void beginOperate(osgParticle::Program *pro)
- osg::Geode *particleGeode = new osg::Geode;
- particleGeode->addDrawable(particleSystem);
- osg::Group *particleEffect = new osg::Group;
- particleEffect->addChild(emitter);
- particleEffect->addChild(particleGeode);
- particleEffect->addChild(program);
- particleEffect->addChild(psu);
- osgParticle::ParticleSystem *ps = pro->getParticleSystem();
- if (ps->numParticles() >= MaxParticles)
- {
- g_counter->setRateRange(0,0);
- }
键盘的交互部分和第十九课中的代码类似,详细部分参看本课的完整代码。
编译运行程序:
附:本课源码(源码中可能存在错误和不足,仅供参考)
- #include "../osgNeHe.h"
- #include <QtCore/QTimer>
- #include <QtGui/QApplication>
- #include <QtGui/QVBoxLayout>
- #include <osgViewer/Viewer>
- #include <osgDB/ReadFile>
- #include <osgQt/GraphicsWindowQt>
- #include <osg/MatrixTransform>
- #include <osg/BlendFunc>
- // osgParticle Related Header Files
- #include <osgParticle/Particle>
- #include <osgParticle/ParticleSystem>
- #include <osgParticle/ParticleSystemUpdater>
- #include <osgParticle/ModularEmitter>
- #include <osgParticle/ModularProgram>
- #include <osgParticle/RandomRateCounter>
- #include <osgParticle/PointPlacer>
- #include <osgParticle/SectorPlacer>
- #include <osgParticle/RadialShooter>
- #include <osgParticle/AccelOperator>
- #include <osgParticle/FluidFrictionOperator>
- //
- //
- //
- const int MaxParticles = 1000;
- float slowdown = 2.0f;
- float xspeed;
- float yspeed;
- float zoom = -40.0f;
- GLuint delay;
- int col;
- bool g_restart = false;
- struct Particle
- {
- bool active;
- float life;
- float fade;
- float r;
- float g;
- float b;
- float x;
- float y;
- float z;
- float xi;
- float yi;
- float zi;
- float xg;
- float yg;
- float zg;
- };
- Particle particles[MaxParticles];
- static GLfloat colors[12][4] =
- {
- {1.0f,0.5f,0.5f, 1.0f},{1.0f,0.75f,0.5f, 1.0f},{1.0f,1.0f,0.5f, 1.0f},{0.75f,1.0f,0.5f, 1.0f},
- {0.5f,1.0f,0.5f, 1.0f},{0.5f,1.0f,0.75f, 1.0f},{0.5f,1.0f,1.0f, 1.0f},{0.5f,0.75f,1.0f, 1.0f},
- {0.5f,0.5f,1.0f, 1.0f},{0.75f,0.5f,1.0f,1.0f},{1.0f,0.5f,1.0f, 1.0f},{1.0f,0.5f,0.75f, 1.0f}
- };
- //
- //
- //Particle Manipulator
- class ParticleEventHandler : public osgGA::GUIEventHandler
- {
- public:
- ParticleEventHandler(){}
- virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
- {
- osgViewer::Viewer *viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
- if (!viewer)
- return false;
- if (!viewer->getSceneData())
- return false;
- if (ea.getHandled())
- return false;
- switch(ea.getEventType())
- {
- case(osgGA::GUIEventAdapter::KEYDOWN):
- {
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Tab)
- {
- g_restart = true;
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Up)
- {
- if ((yspeed<200))
- yspeed+=1.0f;
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Down)
- {
- if ((yspeed>-200))
- yspeed-=1.0f;
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Left)
- {
- if ((xspeed>-200))
- xspeed-=1.0f;
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Right)
- {
- if ((xspeed<200))
- xspeed+=1.0f;
- }
- }
- default: break;
- }
- return false;
- }
- };
- class ViewerWidget : public QWidget, public osgViewer::Viewer
- {
- public:
- ViewerWidget(osg::Node *scene = NULL)
- {
- QWidget* renderWidget = getRenderWidget( createGraphicsWindow(0,0,100,100), scene);
- QVBoxLayout* layout = new QVBoxLayout;
- layout->addWidget(renderWidget);
- layout->setContentsMargins(0, 0, 0, 1);
- setLayout( layout );
- connect( &_timer, SIGNAL(timeout()), this, SLOT(update()) );
- _timer.start( 10 );
- }
- QWidget* getRenderWidget( osgQt::GraphicsWindowQt* gw, osg::Node* scene )
- {
- osg::Camera* camera = this->getCamera();
- camera->setGraphicsContext( gw );
- const osg::GraphicsContext::Traits* traits = gw->getTraits();
- camera->setClearColor( osg::Vec4(0.0, 0.0, 0.0, 0.0) );
- camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) );
- camera->setProjectionMatrixAsPerspective(45.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 0.1f, 100.0f );
- camera->setViewMatrixAsLookAt(osg::Vec3d(0, 0, 1), osg::Vec3d(0, 0, 0), osg::Vec3d(0, 1, 0));
- this->setSceneData( scene );
- addEventHandler(new ParticleEventHandler);
- return gw->getGLWidget();
- }
- osgQt::GraphicsWindowQt* createGraphicsWindow( int x, int y, int w, int h, const std::string& name="", bool windowDecoration=false )
- {
- osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
- osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
- traits->windowName = name;
- traits->windowDecoration = windowDecoration;
- traits->x = x;
- traits->y = y;
- traits->width = w;
- traits->height = h;
- traits->doubleBuffer = true;
- traits->alpha = ds->getMinimumNumAlphaBits();
- traits->stencil = ds->getMinimumNumStencilBits();
- traits->sampleBuffers = ds->getMultiSamples();
- traits->samples = ds->getNumMultiSamples();
- return new osgQt::GraphicsWindowQt(traits.get());
- }
- virtual void paintEvent( QPaintEvent* event )
- {
- frame();
- }
- protected:
- QTimer _timer;
- };
- osgParticle::RandomRateCounter *g_counter = NULL;
- //
- class ColorOption : public osgParticle::Operator
- {
- public:
- ColorOption()
- : osgParticle::Operator(){}
- ColorOption(const ColorOption ©, const osg::CopyOp ©op = osg::CopyOp::SHALLOW_COPY)
- : osgParticle::Operator(copy, copyop){}
- META_Object(osgParticle, ColorOption);
- void beginOperate(osgParticle::Program *pro)
- {
- osgParticle::ParticleSystem *ps = pro->getParticleSystem();
- if(!ps)
- return;
- if (ps->numParticles() >= MaxParticles)
- {
- g_counter->setRateRange(0,0);
- }
- //如果按下Tab键
- if (g_restart) {
- osgParticle::ParticleSystem *ps = pro->getParticleSystem();
- if (!ps)
- return;
- for (unsigned i = 0; i < ps->numParticles(); ++i)
- {
- particles[i].x=0.0f;
- particles[i].y=0.0f;
- particles[i].z=0.0f;
- particles[i].xi=float((rand()%50)-26.0f)*10.0f;
- particles[i].yi=float((rand()%50)-25.0f)*10.0f;
- particles[i].zi=float((rand()%50)-25.0f)*10.0f;
- }
- g_restart = false;
- } else {
- for (int i = 0; i < ps->numParticles(); ++i)
- {
- float x=particles[i].x;
- float y=particles[i].y;
- float z=particles[i].z+zoom;
- ps->getParticle(i)->setPosition(osg::Vec3(x,y,z));
- osg::Vec4 color(particles[i].r,particles[i].g,particles[i].b,particles[i].life);
- ps->getParticle(i)->setColorRange(osgParticle::rangev4(color, color));
- particles[i].x+=particles[i].xi/(slowdown*1000);
- particles[i].y+=particles[i].yi/(slowdown*1000);
- particles[i].z+=particles[i].zi/(slowdown*1000);
- particles[i].xi+=particles[i].xg;
- particles[i].yi+=particles[i].yg;
- particles[i].zi+=particles[i].zg;
- particles[i].life-=particles[i].fade;
- if (particles[i].life<0.0f)
- {
- particles[i].life=1.0f;
- particles[i].fade=float(rand()%100)/1000.0f+0.003f;
- particles[i].x=0.0f;
- particles[i].y=0.0f;
- particles[i].z=0.0f;
- particles[i].xi=xspeed+float((rand()%60)-32.0f);
- particles[i].yi=yspeed+float((rand()%60)-30.0f);
- particles[i].zi=float((rand()%60)-30.0f);
- particles[i].r=colors[col][0];
- particles[i].g=colors[col][1];
- particles[i].b=colors[col][2];
- }
- }
- }
- }
- void operate (osgParticle::Particle *P, double dt)
- {
- }
- };
- osg::Node* buildScene()
- {
- //
- for (int i=0; i<MaxParticles; i++)
- {
- particles[i].active=true;
- particles[i].life=1.0f;
- particles[i].fade=float(rand()%100)/1000.0f+0.003f;
- particles[i].r=colors[int(i*(12.0/MaxParticles))][0];
- particles[i].g=colors[int(i*(12.0/MaxParticles))][1];
- particles[i].b=colors[int(i*(12.0/MaxParticles))][2];
- particles[i].xi=float((rand()%50)-26.0f)*10.0f;
- particles[i].yi=float((rand()%50)-25.0f)*10.0f;
- particles[i].zi=float((rand()%50)-25.0f)*10.0f;
- particles[i].xg=0.0f;
- particles[i].yg=-0.8f;
- particles[i].zg=0.0f;
- }
- //
- //设置粒子模板
- osgParticle::Particle particleTemplate;
- particleTemplate.setShape(osgParticle::Particle::QUAD_TRIANGLESTRIP);
- //负值表示粒子永远不会"死亡"
- particleTemplate.setLifeTime(-1);
- particleTemplate.setPosition(osg::Vec3(0.0, 0.0, 0.0));
- particleTemplate.setRadius(1.0f);
- //设置粒子系统
- osgParticle::ParticleSystem *particleSystem = new osgParticle::ParticleSystem();
- particleSystem->setDefaultAttributes("Data/Particle.bmp", false, false);
- osg::BlendFunc *blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE);
- particleSystem->getOrCreateStateSet()->setAttributeAndModes(blendFunc,osg::StateAttribute::ON);
- particleSystem->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
- particleSystem->setDefaultParticleTemplate(particleTemplate);
- osgParticle::ModularEmitter *emitter = new osgParticle::ModularEmitter;
- emitter->setParticleSystem(particleSystem);
- osgParticle::PointPlacer *placer = new osgParticle::PointPlacer;
- placer->setCenter(0, 0, 0);
- emitter->setPlacer(placer);
- osgParticle::RandomRateCounter *counter = new osgParticle::RandomRateCounter;
- counter->setRateRange(1000, 1000);
- emitter->setCounter(counter);
- g_counter = counter;
- osgParticle::RadialShooter *shooter = new osgParticle::RadialShooter;
- emitter->setShooter(shooter);
- osgParticle::ModularProgram *program = new osgParticle::ModularProgram;
- program->setParticleSystem(particleSystem);
- ColorOption *co = new ColorOption;
- program->addOperator(co);
- osgParticle::ParticleSystemUpdater *psu = new osgParticle::ParticleSystemUpdater;
- psu->addParticleSystem(particleSystem);
- osg::Geode *particleGeode = new osg::Geode;
- particleGeode->addDrawable(particleSystem);
- osg::Group *particleEffect = new osg::Group;
- particleEffect->addChild(emitter);
- particleEffect->addChild(particleGeode);
- particleEffect->addChild(program);
- particleEffect->addChild(psu);
- osg::Group *root = new osg::Group;
- root->addChild(particleEffect);
- return root;
- }
- int main( int argc, char** argv )
- {
- QApplication app(argc, argv);
- ViewerWidget* viewWidget = new ViewerWidget(buildScene());
- viewWidget->setGeometry( 100, 100, 640, 480 );
- viewWidget->show();
- return app.exec();
- }