Day 17: 粒子系统(osgParticle)实战
本章我们将学习实现包含火焰、烟雾、雨雪三种特效的示例代码及详细说明。
粒子系统(osgParticle)
OpenSceneGraph(OSG)的粒子系统(osgParticle)是一个强大的工具包,用于创建各种自然现象和视觉特效,如火焰、烟雾、雨雪、爆炸等。它基于物理模拟原理,通过管理大量微小的"粒子"对象来生成复杂的动态效果。
核心组件
osgParticle系统由以下几个核心组件构成:
-
ParticleSystem(粒子系统)
- 管理所有粒子的容器
- 负责粒子的生命周期、渲染和属性设置
- 可以关联纹理图像以增强视觉效果
-
Particle(粒子)
- 粒子系统的基本单元
- 定义了粒子的属性:大小、颜色、生命周期、速度等
- 每个粒子系统都有一个"粒子模板",新生成的粒子会继承这些属性
-
Emitter(发射器)
- 负责生成新粒子
- 控制粒子的初始位置、方向和速度
- 常见类型:点发射器、线发射器、面发射器和体发射器
-
Program(程序)
- 控制粒子的行为和运动
- 通过添加各种Operator(操作器)来实现物理效果
-
Operator(操作器)
- 对粒子施加各种力和效果
- 常见类型:
- AccelOperator:添加加速度(如重力)
- AgeOperator:管理粒子的老化过程
- FluidFrictionOperator:模拟流体阻力
- ForceOperator:添加自定义力
- IntersectorOperator:处理粒子碰撞
-
Counter(计数器)
- 控制粒子的生成速率
- 常见类型:
- RandomRateCounter:随机速率生成
- ConstantRateCounter:恒定速率生成
工作流程
使用osgParticle创建特效的基本步骤:
- 创建ParticleSystem并设置粒子属性
- 创建Emitter并配置粒子生成方式
- 创建Program并添加所需的Operator
- 将ParticleSystem添加到场景图中
- 使用ParticleSystemUpdater确保粒子系统随时间更新
高级特性
- 纹理和渲染:支持点精灵(Point Sprite)渲染,使粒子始终面向相机
- 粒子行为:可以通过自定义Operator实现复杂的物理模拟
- 粒子交互:支持粒子间的碰撞检测和响应
- 性能优化:通过批处理和剔除技术提高大量粒子的渲染效率
应用场景
osgParticle广泛应用于:
- 游戏开发中的特效(爆炸、魔法效果等)
- 虚拟仿真中的自然现象模拟
- 影视动画中的视觉特效
- 科学可视化中的流体和气体模拟
通过调整粒子系统的各种参数,可以创建出几乎任何你能想象到的动态效果。前面提供的代码示例展示了如何实现几种常见的特效,你可以基于这些基础进一步定制和扩展,创建更复杂的视觉效果。
实战效果
particle.cpp
···cpp
#include <osgViewer/Viewer>
#include <osgParticle/ParticleSystem>
#include <osgParticle/ParticleSystemUpdater>
#include <osgParticle/ModularEmitter>
#include <osgParticle/ModularProgram>
#include <osgParticle/AccelOperator>
#include <osgParticle/FluidFrictionOperator>
#include <osgParticle/SectorPlacer>
#include <osgParticle/RadialShooter>
#include <osgParticle/RandomRateCounter>
#include <osg/Geode>
#include <osg/BlendFunc>
#include <osg/Point>
#include <osg/PointSprite>
#include <osg/Texture2D>
#include <osgDB/ReadFile>
#include <osgGA/StateSetManipulator>
#include <osgGA/GUIEventHandler>
#include <osgText/Text>
#include <osg/PositionAttitudeTransform>
#include
// 粒子效果类型枚举
enum ParticleEffect {
FIRE,
SMOKE,
RAIN,
SNOW
};
// 粒子系统创建器类
class ParticleSystemCreator {
public:
static osgParticle::ParticleSystem* createFireEffect() {
osgParticle::ParticleSystem* ps = createBaseParticleSystem(“images/fire.png”);
osgParticle::Particle& ptemplate = ps->getDefaultParticleTemplate();
// 设置粒子属性ptemplate.setLifeTime(1.5f);ptemplate.setSizeRange(osgParticle::rangef(0.1f, 0.8f));ptemplate.setAlphaRange(osgParticle::rangef(1.0f, 0.0f));ptemplate.setColorRange(osgParticle::rangev4(osg::Vec4(1.0f, 0.5f, 0.1f, 1.0f), // 开始颜色 (橙色)osg::Vec4(1.0f, 1.0f, 0.0f, 0.0f) // 结束颜色 (黄色到透明)));ptemplate.setMass(0.1f);ptemplate.setRadius(0.05f);return ps;
}static osgParticle::ParticleSystem* createSmokeEffect() {osgParticle::ParticleSystem* ps = createBaseParticleSystem("images/smoke.png");osgParticle::Particle& ptemplate = ps->getDefaultParticleTemplate();// 设置粒子属性ptemplate.setLifeTime(4.0f);ptemplate.setSizeRange(osgParticle::rangef(0.2f, 1.5f));ptemplate.setAlphaRange(osgParticle::rangef(0.1f, 0.0f));ptemplate.setColorRange(osgParticle::rangev4(osg::Vec4(0.3f, 0.3f, 0.3f, 0.5f), // 开始颜色 (深灰色)osg::Vec4(0.8f, 0.8f, 0.8f, 0.0f) // 结束颜色 (浅灰色到透明)));ptemplate.setMass(0.05f);ptemplate.setRadius(0.1f);return ps;
}static osgParticle::ParticleSystem* createRainEffect() {osgParticle::ParticleSystem* ps = createBaseParticleSystem("images/raindrop.png");osgParticle::Particle& ptemplate = ps->getDefaultParticleTemplate();// 设置粒子属性ptemplate.setLifeTime(5.0f);ptemplate.setSizeRange(osgParticle::rangef(0.05f, 0.1f));ptemplate.setAlphaRange(osgParticle::rangef(0.8f, 0.8f));ptemplate.setColorRange(osgParticle::rangev4(osg::Vec4(0.7f, 0.7f, 1.0f, 0.8f), // 开始颜色 (淡蓝色)osg::Vec4(0.5f, 0.5f, 1.0f, 0.8f) // 结束颜色 (深蓝色)));ptemplate.setMass(0.3f);ptemplate.setRadius(0.01f);return ps;
}static osgParticle::ParticleSystem* createSnowEffect() {osgParticle::ParticleSystem* ps = createBaseParticleSystem("images/snowflake.png");osgParticle::Particle& ptemplate = ps->getDefaultParticleTemplate();// 设置粒子属性ptemplate.setLifeTime(10.0f);ptemplate.setSizeRange(osgParticle::rangef(0.1f, 0.3f));ptemplate.setAlphaRange(osgParticle::rangef(0.9f, 0.9f));ptemplate.setColorRange(osgParticle::rangev4(osg::Vec4(1.0f, 1.0f, 1.0f, 0.9f), // 开始颜色 (白色)osg::Vec4(1.0f, 1.0f, 1.0f, 0.9f) // 结束颜色 (白色)));ptemplate.setMass(0.01f);ptemplate.setRadius(0.05f);return ps;
}
private:
static osgParticle::ParticleSystem* createBaseParticleSystem(const std::string& texturePath) {
osgParticle::ParticleSystem* ps = new osgParticle::ParticleSystem();
// 基本粒子系统设置ps->setDefaultAttributes(texturePath, true, false);ps->getDefaultParticleTemplate().setShape(osgParticle::Particle::QUAD);// 删除不兼容的API调用// ps->setParticleScaleByDistance(1.0f, 50.0f, 100.0f); // 不兼容APIps->setDoublePassRendering(true);// 启用点精灵osg::StateSet* ss = ps->getOrCreateStateSet();ss->setAttribute(new osg::Point(20.0f), osg::StateAttribute::ON);ss->setTextureAttributeAndModes(0, new osg::PointSprite, osg::StateAttribute::ON);// 设置混合模式osg::BlendFunc* blendFunc = new osg::BlendFunc();blendFunc->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);ss->setAttributeAndModes(blendFunc);ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);return ps;
}
};
// 粒子发射器创建器类
class ParticleEmitterCreator {
public:
static osgParticle::ModularEmitter* createFireEmitter(osgParticle::ParticleSystem* ps) {
osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter();
emitter->setParticleSystem(ps);
// 设置计数器 (粒子生成率)osgParticle::RandomRateCounter* counter = new osgParticle::RandomRateCounter();counter->setRateRange(50, 100); // 50-100 粒子/秒// 设置放置器 (粒子生成位置)osgParticle::SectorPlacer* placer = new osgParticle::SectorPlacer();placer->setCenter(osg::Vec3(0.0f, 0.0f, 0.0f));placer->setRadiusRange(0.1f, 0.3f);// 设置发射器 (粒子初始速度)osgParticle::RadialShooter* shooter = new osgParticle::RadialShooter();shooter->setInitialSpeedRange(0.5f, 1.5f);shooter->setPhiRange(0.0f, osg::PI * 0.1f);shooter->setThetaRange(0.0f, osg::PI * 0.1f);emitter->setCounter(counter);emitter->setPlacer(placer);emitter->setShooter(shooter);return emitter;
}static osgParticle::ModularEmitter* createSmokeEmitter(osgParticle::ParticleSystem* ps) {osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter();emitter->setParticleSystem(ps);osgParticle::RandomRateCounter* counter = new osgParticle::RandomRateCounter();counter->setRateRange(20, 30); // 20-30 粒子/秒osgParticle::SectorPlacer* placer = new osgParticle::SectorPlacer();placer->setCenter(osg::Vec3(0.0f, 0.0f, 0.5f)); // 在火焰上方生成placer->setRadiusRange(0.2f, 0.5f);osgParticle::RadialShooter* shooter = new osgParticle::RadialShooter();shooter->setInitialSpeedRange(0.1f, 0.3f);shooter->setPhiRange(0.0f, osg::PI * 0.2f);emitter->setCounter(counter);emitter->setPlacer(placer);emitter->setShooter(shooter);return emitter;
}static osgParticle::ModularEmitter* createRainEmitter(osgParticle::ParticleSystem* ps) {osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter();emitter->setParticleSystem(ps);osgParticle::RandomRateCounter* counter = new osgParticle::RandomRateCounter();counter->setRateRange(500, 800); // 500-800 粒子/秒osgParticle::SectorPlacer* placer = new osgParticle::SectorPlacer();placer->setCenter(osg::Vec3(0.0f, 0.0f, 10.0f)); // 在顶部生成placer->setRadiusRange(8.0f, 10.0f);osgParticle::RadialShooter* shooter = new osgParticle::RadialShooter();shooter->setInitialSpeedRange(2.0f, 5.0f);shooter->setThetaRange(0.0f, osg::PI_2); // 向下发射emitter->setCounter(counter);emitter->setPlacer(placer);emitter->setShooter(shooter);return emitter;
}static osgParticle::ModularEmitter* createSnowEmitter(osgParticle::ParticleSystem* ps) {osgParticle::ModularEmitter* emitter = new osgParticle::ModularEmitter();emitter->setParticleSystem(ps);osgParticle::RandomRateCounter* counter = new osgParticle::RandomRateCounter();counter->setRateRange(200, 300); // 200-300 粒子/秒osgParticle::SectorPlacer* placer = new osgParticle::SectorPlacer();placer->setCenter(osg::Vec3(0.0f, 0.0f, 10.0f)); // 在顶部生成placer->setRadiusRange(10.0f, 15.0f);osgParticle::RadialShooter* shooter = new osgParticle::RadialShooter();shooter->setInitialSpeedRange(0.5f, 1.5f);shooter->setThetaRange(0.0f, osg::PI_2); // 向下发射emitter->setCounter(counter);emitter->setPlacer(placer);emitter->setShooter(shooter);return emitter;
}
};
// 粒子程序创建器类 (控制粒子行为)
class ParticleProgramCreator {
public:
static osgParticle::ModularProgram* createFireProgram() {
osgParticle::ModularProgram* program = new osgParticle::ModularProgram();
// 添加重力osgParticle::AccelOperator* gravity = new osgParticle::AccelOperator();gravity->setToGravity();// 添加流体阻力osgParticle::FluidFrictionOperator* fluidFriction = new osgParticle::FluidFrictionOperator();fluidFriction->setFluidToAir();program->addOperator(gravity);program->addOperator(fluidFriction);return program;
}static osgParticle::ModularProgram* createSmokeProgram() {osgParticle::ModularProgram* program = new osgParticle::ModularProgram();// 添加重力 (较小的值)osgParticle::AccelOperator* gravity = new osgParticle::AccelOperator();gravity->setAcceleration(osg::Vec3(0.0f, 0.0f, 0.2f));// 添加流体阻力osgParticle::FluidFrictionOperator* fluidFriction = new osgParticle::FluidFrictionOperator();fluidFriction->setFluidToAir();program->addOperator(gravity);program->addOperator(fluidFriction);return program;
}static osgParticle::ModularProgram* createRainProgram() {osgParticle::ModularProgram* program = new osgParticle::ModularProgram();// 添加重力osgParticle::AccelOperator* gravity = new osgParticle::AccelOperator();gravity->setToGravity();// 添加风阻力osgParticle::FluidFrictionOperator* wind = new osgParticle::FluidFrictionOperator();wind->setFluidToAir();wind->setWind(osg::Vec3(1.0f, 0.0f, 0.0f)); // 添加风program->addOperator(gravity);program->addOperator(wind);return program;
}static osgParticle::ModularProgram* createSnowProgram() {osgParticle::ModularProgram* program = new osgParticle::ModularProgram();// 添加重力 (较小的值)osgParticle::AccelOperator* gravity = new osgParticle::AccelOperator();gravity->setAcceleration(osg::Vec3(0.0f, 0.0f, -1.0f));// 添加风阻力osgParticle::FluidFrictionOperator* wind = new osgParticle::FluidFrictionOperator();wind->setFluidToAir();wind->setWind(osg::Vec3(1.5f, 0.0f, 0.0f)); // 更强的风// 移除了不存在的RandomForceOperatorprogram->addOperator(gravity);program->addOperator(wind);return program;
}
};
// 事件处理器:切换粒子效果
class ParticleSwitcher : public osgGA::GUIEventHandler {
public:
ParticleSwitcher(osg::Group* particleGroup)
: _particleGroup(particleGroup), _currentEffect(FIRE) {
createAllEffects();
showEffect(FIRE);
}
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) {if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN) {switch (ea.getKey()) {case '1': showEffect(FIRE);return true;case '2': showEffect(SMOKE);return true;case '3': showEffect(RAIN);return true;case '4': showEffect(SNOW);return true;case 'r': toggleRainSnow();return true;}}return false;
}void showEffect(ParticleEffect effect) {// 隐藏所有效果for (int i = 0; i < 4; ++i) {_particleGroup->getChild(i)->setNodeMask(0);}// 显示当前效果_particleGroup->getChild(effect)->setNodeMask(0xFFFFFFFF);_currentEffect = effect;
}void toggleRainSnow() {if (_currentEffect == RAIN) {showEffect(SNOW);} else if (_currentEffect == SNOW) {showEffect(RAIN);}
}
private:
void createAllEffects() {
// 创建火焰效果
osg::Group* fireGroup = new osg::Group();
osgParticle::ParticleSystem* firePS = ParticleSystemCreator::createFireEffect();
osgParticle::ModularEmitter* fireEmitter = ParticleEmitterCreator::createFireEmitter(firePS);
osgParticle::ModularProgram* fireProgram = ParticleProgramCreator::createFireProgram();
fireProgram->setParticleSystem(firePS);
osg::Geode* fireGeode = new osg::Geode();fireGeode->addDrawable(firePS);fireGroup->addChild(fireEmitter);fireGroup->addChild(fireProgram);fireGroup->addChild(fireGeode);// 创建烟雾效果osg::Group* smokeGroup = new osg::Group();osgParticle::ParticleSystem* smokePS = ParticleSystemCreator::createSmokeEffect();osgParticle::ModularEmitter* smokeEmitter = ParticleEmitterCreator::createSmokeEmitter(smokePS);osgParticle::ModularProgram* smokeProgram = ParticleProgramCreator::createSmokeProgram();smokeProgram->setParticleSystem(smokePS);osg::Geode* smokeGeode = new osg::Geode();smokeGeode->addDrawable(smokePS);smokeGroup->addChild(smokeEmitter);smokeGroup->addChild(smokeProgram);smokeGroup->addChild(smokeGeode);// 创建雨效果osg::Group* rainGroup = new osg::Group();osgParticle::ParticleSystem* rainPS = ParticleSystemCreator::createRainEffect();osgParticle::ModularEmitter* rainEmitter = ParticleEmitterCreator::createRainEmitter(rainPS);osgParticle::ModularProgram* rainProgram = ParticleProgramCreator::createRainProgram();rainProgram->setParticleSystem(rainPS);osg::Geode* rainGeode = new osg::Geode();rainGeode->addDrawable(rainPS);rainGroup->addChild(rainEmitter);rainGroup->addChild(rainProgram);rainGroup->addChild(rainGeode);// 创建雪效果osg::Group* snowGroup = new osg::Group();osgParticle::ParticleSystem* snowPS = ParticleSystemCreator::createSnowEffect();osgParticle::ModularEmitter* snowEmitter = ParticleEmitterCreator::createSnowEmitter(snowPS);osgParticle::ModularProgram* snowProgram = ParticleProgramCreator::createSnowProgram();snowProgram->setParticleSystem(snowPS);osg::Geode* snowGeode = new osg::Geode();snowGeode->addDrawable(snowPS);snowGroup->addChild(snowEmitter);snowGroup->addChild(snowProgram);snowGroup->addChild(snowGeode);// 添加到粒子组_particleGroup->addChild(fireGroup);_particleGroup->addChild(smokeGroup);_particleGroup->addChild(rainGroup);_particleGroup->addChild(snowGroup);// 添加粒子系统更新器osgParticle::ParticleSystemUpdater* updater = new osgParticle::ParticleSystemUpdater();updater->addParticleSystem(firePS);updater->addParticleSystem(smokePS);updater->addParticleSystem(rainPS);updater->addParticleSystem(snowPS);_particleGroup->addChild(updater);
}osg::ref_ptr<osg::Group> _particleGroup;
ParticleEffect _currentEffect;
};
// 创建信息显示
osgText::Text* createInfoText(const std::string& message, float yPos) {
osgText::Text* text = new osgText::Text();
text->setFont(“fonts/arial.ttf”);
text->setCharacterSize(20.0f);
text->setPosition(osg::Vec3(10.0f, yPos, 0.0f));
text->setColor(osg::Vec4(1.0f, 1.0f, 0.5f, 1.0f));
text->setText(message);
text->setBackdropType(osgText::Text::OUTLINE);
return text;
}
int main() {
// 创建查看器
osgViewer::Viewer viewer;
// 创建根节点
osg::Group* root = new osg::Group();// 添加地面
osg::Geode* groundGeode = new osg::Geode();
osg::Geometry* groundGeometry = osg::createTexturedQuadGeometry(osg::Vec3(-15.0f, -15.0f, 0.0f),osg::Vec3(30.0f, 0.0f, 0.0f),osg::Vec3(0.0f, 30.0f, 0.0f)
);
osg::Texture2D* groundTexture = new osg::Texture2D(osgDB::readImageFile("images/ground.jpg"));
if (groundTexture) {groundTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);groundTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);groundGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, groundTexture);
}
groundGeode->addDrawable(groundGeometry);
root->addChild(groundGeode);// 创建粒子组
osg::Group* particleGroup = new osg::Group();
root->addChild(particleGroup);// 添加事件处理器
viewer.addEventHandler(new ParticleSwitcher(particleGroup));// 添加状态设置处理器(允许调整渲染状态)
viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));// 添加信息显示
osg::Geode* textGeode = new osg::Geode();
textGeode->addDrawable(createInfoText("OSG Particle System Demo", 550.0f));
textGeode->addDrawable(createInfoText("Press 1: Fire Effect", 520.0f));
textGeode->addDrawable(createInfoText("Press 2: Smoke Effect", 490.0f));
textGeode->addDrawable(createInfoText("Press 3: Rain Effect", 460.0f));
textGeode->addDrawable(createInfoText("Press 4: Snow Effect", 430.0f));
textGeode->addDrawable(createInfoText("Press R: Toggle Rain/Snow", 400.0f));
root->addChild(textGeode);// 设置场景数据
viewer.setSceneData(root);// 设置相机位置
viewer.getCamera()->setViewMatrixAsLookAt(osg::Vec3(0.0f, -10.0f, 8.0f), // 眼睛位置osg::Vec3(0.0f, 0.0f, 2.0f), // 观察点osg::Vec3(0.0f, 0.0f, 1.0f) // 上方向
);// 启动查看器
return viewer.run();
}
### 运行效果

