【OSG学习笔记】Day 17: 粒子系统(osgParticle)实战

在这里插入图片描述

本章我们将学习实现包含火焰、烟雾、雨雪三种特效的示例代码及详细说明。

粒子系统(osgParticle)

OpenSceneGraph(OSG)的粒子系统(osgParticle)是一个强大的工具包,用于创建各种自然现象和视觉特效,如火焰、烟雾、雨雪、爆炸等。它基于物理模拟原理,通过管理大量微小的"粒子"对象来生成复杂的动态效果。

核心组件

osgParticle系统由以下几个核心组件构成:

  1. ParticleSystem(粒子系统)

    • 管理所有粒子的容器
    • 负责粒子的生命周期、渲染和属性设置
    • 可以关联纹理图像以增强视觉效果
  2. Particle(粒子)

    • 粒子系统的基本单元
    • 定义了粒子的属性:大小、颜色、生命周期、速度等
    • 每个粒子系统都有一个"粒子模板",新生成的粒子会继承这些属性
  3. Emitter(发射器)

    • 负责生成新粒子
    • 控制粒子的初始位置、方向和速度
    • 常见类型:点发射器、线发射器、面发射器和体发射器
  4. Program(程序)

    • 控制粒子的行为和运动
    • 通过添加各种Operator(操作器)来实现物理效果
  5. Operator(操作器)

    • 对粒子施加各种力和效果
    • 常见类型:
      • AccelOperator:添加加速度(如重力)
      • AgeOperator:管理粒子的老化过程
      • FluidFrictionOperator:模拟流体阻力
      • ForceOperator:添加自定义力
      • IntersectorOperator:处理粒子碰撞
  6. Counter(计数器)

    • 控制粒子的生成速率
    • 常见类型:
      • RandomRateCounter:随机速率生成
      • ConstantRateCounter:恒定速率生成

工作流程

使用osgParticle创建特效的基本步骤:

  1. 创建ParticleSystem并设置粒子属性
  2. 创建Emitter并配置粒子生成方式
  3. 创建Program并添加所需的Operator
  4. 将ParticleSystem添加到场景图中
  5. 使用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); // 不兼容API
    
    ps->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)); // 更强的风
    
    // 移除了不存在的RandomForceOperator
    
    program->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();

}

### 运行效果
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/e84611fdb4994b7e81b3cf50df0542ec.png)

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/775de421d0954e97ae979032b4658137.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/032ef0887b804b65b204cab7cf818dd6.png)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_李小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值