admin 管理员组

文章数量: 1086019


2024年3月12日发(作者:canvas同济)

…… 

GRAPHICS AND IMAGE PROCESSING…一……… ……………一………………………………………… …… … 

OsG的粒子特效仿真 

侯学隆宋伟健 

摘 要:OSG是一款市场占有率非常高的3D图形开发库,粒子特效的开发的一直是其难点。在 

介绍粒子系统的基础上,分析了OSG中的粒子对象类的功能与继承关系,提炼粒子特效仿真的思 

路,并给出一个粒子特效实例。 

关键词:粒子系统:特殊仿真:OSG 

雨、雾、云、爆炸、火焰、烟尘、燃烧等战场特效是增强 4)根据粒子属性变化的动态特性改变其属性值。 

虚拟环境逼真度的有效手段,在三维游戏、战场环境仿真中具 

5)删除系统中已死亡的粒子。 

有重要作用。OSG是一款高性能的3D图形开发库,在游戏、 6)绘制所有剩余的粒子。 

视景仿真、可视化仿真、虚拟现实等领域具有很高的市场占有 步骤3)、4)、5)、6)反复循环就形成了物体的动态变化 

率。OSG基于粒子系统思想提供了一整套完整的C++类库对特 

过程。 

效进行抽象、建模与仿真.如何有效利用这些类构建特定的战 

场特效是广大OSG开发者非常关注的问题,也是一个难点问 

2 OSG粒子特效相关类 

题。在此主要介绍OSG粒子类及特效仿真方法,并给出一个 OSG提供了一系列类对粒子系统进行封装,域名空间为 

具体的仿真实例。 osgParticle,程序员使用这些类可以设计各种粒子特效。主要 

1 粒子系统 

的相关类包括: 

2.1 osgParticle::ModularEmitter 

对于诸如雨、雪、云、火焰、烟雾、爆炸等这些特殊视景 标准放射器(osgParticle::ModularEmitter)用于管理计数 

效果的实时模拟一直是计算机图形学研究的难点,而粒子系统 

器、放置器、发射器和粒子群,它为用户控制粒子系统中多个 

是解决这一难点的有效方法。 

元素提供了一个标准机制,如图1所示。 

粒子系统的基本思想是:用粒子群来描述不规则物体的属 

性及其变化,每个粒子均具有形状、大小、颜色、透明度、运 

Os Re龟 8nc d 

动速度、运动方向、生命周期等属性。这些粒子不是一个静态 

fl  

的整体,而是随着时间的推移处在不断运动中的粒子集合体。 

其中粒子群的分布结构可以改变,各个粒子的位置可以移动, 

新的粒子可以不断产生,同时旧的粒子不断死亡 

l。sg:N。de l 

同其他描述不规则物体的方法相比,粒子系统具有以下3 

个特点: 

{ 

osgParIicle::PatlicleProcessor 

(1)对物体的描述不是通过原始的具有边界的面元(如多边 

形集合)来描述,而是通过一组定义在空问的原始粒子来描述。 

(2)粒子系统不是一个静态实体,每个粒子的属性均是时 

}霸嘲酶锈每遵蚓 

间的函数。 

(3)由粒子系统描述的物体不是预先定义好的.其形状位 

Osgpa糠}0 0幽lar嚣m瞅 f 

置等属性均用随机过程来描述。 

通常用粒子系统绘制一幅画面需要如下步骤: 

图1 放射器类继承/派生关系 

1)分析物体的静态特性,定义粒子的初始属性。 

2.2 osgParticle::ParticleSystem 

2)分析物体的运动规律,建立粒子属性变化的动态特性。 

粒子群(osgPanicle::PamcleSystem)是OSG粒子系统的核 

3)在系统中生成具有一定初始属性的新粒子。 心,它维护并管理一系列粒子的创建、更新、渲染和销毁。粒 

垴壤趣2姣。巧lo等.1豢1护 

…… … ‘ ‘ … …‘ … … 一 

实用第一 智慧密集 

∞ 目 目 !∞ & s i ∞ ! ∞ E 日 ± ! i 自 

子系统类继承自Drawable类,控制粒子的渲染,因此与其他 

Drawable对象的渲染类似,控制其渲染属性stateAttribute即 

可。OSG提供了一个方便的函数setDerauhAttrihutes以允许用 

户控制粒子使用的纹理、允许/禁止发光、允许/禁止光照3个 

属性。派生类ConnectedParticleSystem用于导弹、飞机等轨迹 

特效,单个粒子被渲染成带状(ribbon),如图2所示。 

图2粒子群类的继承/派生关系 

2.3 OsgPar“cIe::ParticIe 

粒子(osgParticle::

r 

Particle)是粒子系统的基本单元,它被 

放射器创建后加入到粒子群中。粒子具有物理属性和图像属 

性,它的形状可以是任意的点(POINT)、四边形(QUAD)、 

四边形带(QUAD_TRIPSTRIP)、六角形(HEXAGON)或者线 

(LINE)。每个粒子都有自己的生命周期,即每个粒子可以存活 

的时间(秒)。生命周期为负数的粒子可以存活无限长时间。 

粒子系统通过改变生命周期的最大和最小值来控制单个粒子的 

渲染。所有的粒子都具有大小

雾 

(SIZE)、ALPHA值和颜色 

(COLOR)属性,这些属性均可以指定一个范围,即最大和最 

小值。粒子属性会根据已经消耗的时间,在最小和最大值之间 

进行线性插值(用户也可自定义插值方式)。 

2.4 OsaParticIe::Placer 

放置器(osgParticle::Placer)将粒子作为输入,用于设置 

粒子的位置。它作为ModularEmitter的一部分,用户可以使用 

预定义的放置器或者定义自己的放置器。预定义的放置器包 

括:点放置器PointP1aceT(所有的粒子从同一点出生)、长方 

体放置器BoxP1acer(所有的粒子从一个长方体内产生)、扇面 

放置器sectorP1acer(所有的粒子从一个指定中心点,半径范围 

和角度范围的扇面出生)、线段放置器SegmentPlacer(所有的 

粒子从一条线段上产生),以及多段放置器MultiSegmentPhacer 

(用户指定一系列的点,粒子沿着这些点定义的线段出生),如 

图3所示 

与矗 

图3放置器类的继承/派生关系 

2.5 OsgPa nicIe::ShOOter 

发射器(osgParticle::Shooter)在内部被ModularEmitter所 

用,是一个用于指定粒子的初始速度范围的抽象类。派生类 

RadialShooter类允许用户指定一个速度范围(米/秒)以及弧度 

值表示的方向。方向由两个角度指定:theta角——速度矢量 

与z轴的夹角,phi角——x轴与速度矢量投影到XY平面所得 

矢量的夹角,如图4所示 

图4发射器类的继承/派生关系 

2.6 osgParticle::Counter 

计数器(osgParticle::Counter):控制每一帧产生的粒子数。 

ConstantRateCounter用于指定每秒或每帧最少产生的粒子数: 

RandomRateCounter类允许用户指定每帧产生粒子的最大和最 

小数目,如图5所示。 

图5计数器类的继承/派生关系 

2.7 osgParticle::ParticleSystemUpdater 

粒子群更新器(osgParticle:ParticleSystemUpdater):用于 

自动更新粒子。将其置于场景中时,它会在拣选遍历中调用所 

有“存活”粒子的更新方法,如图6所示。 

…… 

GRAPHICS AND l啊^GE PROCESSING………………………….、-………… ……一 …… …,… ……… ……… 

图6粒子群更新器类的继承/派生关系 

2.8 osgParticle::ModularProgram 

标准编程器(osgParticle::ModularProgram)在单个粒子的生 

命周期中,用户可以使用ModularProgram实例控制粒子的位置, 

ModularProgram需要与Operator对象组合使用,如图7所示。 

图7标准编程器的继承/派生关系 

2.9 osgParticle::Operator 

操作器(osgPanicle::0perator)提供了控制粒子在其生命 

周期中的运动特性的方法。用户可以改变现有Operator类实例 

的参数,或者定义自己的Operator类。OSG提供的Operator类 

包括:AccelOperator(加速度)、AngularAccelOperator(角加速 

度)、FluidFrictionOperator(空气阻力或者流体操作)以及 

ForceOperator(压力),如图8所示。 

图8操作器类的继承/派生关系 

在OSG中除了以上粒子系统C++类,还包含有预定义的 

特效类: 

(1)osgParticle::ExplosionDebrisEffect(爆炸碎片); 

(2)osgParticle::ExplosionEffect(爆炸); 

(3)osgParticle::SmokeEffect(烟); 

(4)osgParticle::SmokeTrailEffect(尾焰轨迹); 

(5)osgParticle::FireEffect(火焰); 

(6)sgParticle::PrecipitationEffect(雨、雪)。 

3 开发方法 

在OSG中,采用粒子系统模拟特殊效果有两种方法:一 

是采用OSG中已经定义好的粒子系统模块;二是根据需要, 

自定义粒子系统。 . 

(1)预定义粒子系统的使用 

11创建预定义粒子系统模块对象,设置相应的参数。 

21作为子节点加到到场景节点中。从粒子系统的的关系 

继承图中.可以知道它们继承自osg::Node或者osg::Group节 

点,因此可以直接作为一个节点加入到场景中。 

(2)自定义粒子系统的方法与步骤 

1)创建粒子群(osgPartlcle::ParticleSystem),并将其加入 

到场景中。设置相应的属性,如材质、放射及光照。 

2)创建粒子(osgParticle::Particle),控制场景中每一个粒 

子的特性并关联到粒子群。设置粒子对应的特性,如大小、颜 

色、生命周期及重量等。 

3)创建粒子系统放射器(osgParticle::ModularEmitter),标 

准的放射器包括3个组成部分计数器(Counter)、放置器 

(Placer)、发射器(Shooter),设置相应的属性,如位置、形 

状、速度和方向等。 

4)创建粒子系统编程器对象(osgParticle::Program),控制粒 

子在声明周期内的运动。一个标准编程器对象包含各种操作器, 

如osgParticle::AccelOperator,osgParticle::FluidFrictionoperator等。 

5)创建粒子系统更新器(osgParticle::ParticleSystemUp— 

dater),用于管理每帧的粒子的属性,如位置、速度、方向等。 

通过上面的步骤,可以完成一般粒子特殊效果的仿真.用 

户也可根据需要调整各对象实例的创建顺序。各粒子特效对象 

的调用关系如图9所示。 

 r fr :】tI Elf.】 .… l‘ ’H {c 0I1『l 1

【 。 h¨_ I, 

Ll lJ № ;,  ・J 

j 1 

 J¨ l【mt0】 

十 

l 

¨ {…”一- -一 -{ 

j 拍J ’ }1lJ0 jd i” n I 

|¨ j jj J 

d 

。. 、 

_ 1。s—m - -” 一 

f1K rll【 ・j P 】-j】 、 r”口 ad l'、Ⅱ ,¨l ¨ H ( 】

图9粒子特效对象相互调用关系 

2010 .

11 

墨捆硝 仪

∥ 

… … ’吣 *…H …H …  ‘,一……1 

~ … … 

实用第一 智慧密集 

4开发实例 

基于Visual Studio 8.0和OSG2.8,利用粒子特效类编程实 

现了固定翼飞机螺旋桨引擎着火、冒烟效果。 

秘龟犍火球 

void createFire8aII(osg::MatrixTransform smokeNode) 

{ 

//创建粒子对象,设置其属性并交由粒子系统使用。 

osgParticle::ParticIe particleTempalte; 

lpatticIeTempalte.setShape(OsgParticle::Particle::QUAD) 

particleTemlpaIte.setLifeTlme(1.5) //单位:秒 

{particteTempaIte.setSizeRange(osgParticte::rangef(3.0f, 

Of))=//单位:米 

particleTempalte.setAIphaRange(osgParticle::rangef( O)): 

。≯JparticleTempatte.setColorRange(osgParticle::rangev4( 

osg::Vec4(1.Of, 2f,0.Of,1.of),//0。"…0 3f,0.4f, .Of 

osg::Vec4(0.1f,0.1f,O.1f,0)//0.95f,0.75f,0,1(1J1, 1) 

)) 

particleTempalte.setPos tion(os g=:Vec3(0.0f,0.0f,0 Of)) 

Ip;articleTempalte.setVelocity(osg::Vec3(0.0f,0. O.Of)): 

pa ele_l.=empalte.setMass(o.1 //单位:-T- ̄ 

。 

∞rticleTempalte.setRadius(0.2f); 

particleTempalte.setSizeInterpolator{new osgPartic}e L_ 

Linea Interpolator); 

particleTempaIte,setAIphalnterpolater(new osgParticle:: 

Linearlnterpotator): 

¨lparticleTempalte.setCoIo rInterpolator(new osgPaI.-七icIe:: 

Linearlnterpolator]l: 

- //仓U建并初始化粒子系统。 

osgParticte::Pa cIeSystem *particteSystem=new 

osgParticte::ParticleSystem 

0I ̄articleSystem->setDataVariance(osg::Node::STATIC); 

//设置材质,是否放射粒子,以及是否使用光照。 

pparticleSystem->setDefauItAttributes( smoke.叼b",true, 

false); 

osg::Geode geode new osg::Geode; 

geode->addDrawable《particleSystem); 

smokeNode->addChiId(geode 

■ll臌置为粒子系统的缺省粒子对象o 

particleSystem 一>setDefaultParticIeTemplate(parti- 

cleTemp ̄lIte); 

一。 删莛取放射极中缺省计数器的旬柄,调整每帧增加的新粒 

《 数目 

osgParticle::RandomRateCounte *partic}eGenerateRate 

=new OSgParticle::RandomRateCounter0 

p鑫rtieIeGenerateRate一>setRateRange(30,50); 

,,每秒新生成的粒子范围 

particIeGenerateRate一>setDataVariance(osg::Node::DY- 

NAMIC); 一 

//自定义一个放置器,这里创建并初始化--4"A放置器 

osgParticle::PointPlacer partictePlace ;new osg- 

与 

麓 

Particle::PointPlacer: 

particlePlacer一>setCenter《0sg::Vec3(0.Of,O.0fJ0.0f)); 

particlePIacer一>setDataVariance(osg::Node::DYNAMIC); 

,/自定义一个弧度发射器 

OsgParticle::RadiaIShooter particleShooter=new osg- 

Particle::RadialShooter; 

∥设置发射器的属性 

particleShooter一>setDataVa ance《Osg::NOde::DYNAM 

IC 

particleShooter->setThetaRange(一0.785398f.0.785398f); 

//弧度值,与Z轴夹角 

particIeShooter一>setPhiRange(一0.785398f,0.785398f) 

pafticleShooter一>set1nitialSpeedRange(5,7.5f);//- ̄位:米/秒 

//仓峰E标准放射极对象 

osgParticle::ModularEmitter*emitter=new osgPaft 

cle::ModularEmitter; 

emitter->setDataVariance(osg::Node::DYNAMIC); 

emitter一>setCullingActive(false); 

/将放射极对象与粒子系统关联。 

emitter">setParticleSystem(particleSystem); 

//设置计数器 

em ter一>setCOunter(parEicleGenerateRate): 

//设置放置器 

emitter->setPtacer(particleP!acer); 

,,设置发射器 

emitter->setShooter articleShooter); 

//把放射极添加为变换节点 

smokeNode一>addChild(emitter); 

//添加更新器,以实现每帧的粒子管理。 

OsgPar【icIe::PanicleSystemUpdater particleSys- 

temUpdater=new osgParticle::Pa icIeSystemUpdater; 

//将更新器与粒子系统对象关联。 

particleSystemUpdater,>addParticleSystem(parti, 

cleSystem): 

//将更新器节点添加到场景中。 

smokeNode一>addChild(particleSystemUpdater); 

//创建标准编程器对象并与粒子系统相关联。 

OsgParticle::ModularProgram particleMoveProgram= 

new osgParticie::ModularProgram; 

particleMoveProgram,>setParticleSystem《particieSys, 

tern); ’ 

//最后,将编程器添加到场景中。 

smokeNode一>addC d(particfeMOvePr0gram) 

) 

//e ̄J建浓烟 

void createDarkSmoke(osg::MatrixTransform smokeNode) 

( 

,/创建粒子对象,设置其属性并交由粒子系统使用。 

osgParticle::Particle particleTempalte; 

particIe。rempalte.setShape(osgParticle::Particle::QUAD); 

particleTempatte.setLifeTime(1 O)://单位:秒 

particleTempaIre.setSizeRange(osgParticle::rangef(11.0f, 

… 

GRAPHICS AND IMAGE PROCESSING .…x -

(osgParticte::rangef 

… 一…… … … …~ ;… …㈣ … …一‘H … … 

1 2.Of)):N单位:米particleS hooter->setln alSpeedRange(1 0,1 5): 

particleTempatte.setAIphaRange ∥单位:米 

(1,O)): 

particleTempalte setColorRange(osgParticle::rangev4( 

osg::Vec4(O.Of,0.Of,0.Of,0_5f),N(O.1f10.1f,O. O.5f) 

osg::Vec4(O 5f,0.5f,0.5f,1.5f)N0.95f,0.75f,0,1 

)): 

particleTempalte.setPosition(0sg::Vec3(0.Of,0.Of,0.O吡 

particleTempalte.setVelocity(osg::Vec3(O.Of 0。Of,O.Of)): 

particteTempalte.setMass(O.1 f):∥单位:千克 

particleTempalte.setRadius(O.2f): 

particleTempalte.setSizeInterpolator(new osgParticle:: 

LinearlnterDolatOr): 

particleTempalte.setAIphalnterpotator(new osgParticle:: 

LinearInterpOlat0r): 

particleTempalte.setColorInterpofator(new osgPatticle:: 

LinearInterpOIat0 r): 

∥创建并初始化粒子系统。 

osgParticle::ParticleSystem particteSystem=new 

osgParticle::ParticleSystem;

particleSystem一>setDataVariance(osg::Node::S IC): 

∥设置材质,是否放射粒子,以及是否使用光照。 

particleSystem一>setDefaultAttributes(”smoke。rgb’',false. 

false); 

osg::Geode geode=new osg::Geode; 

geode一>addDrawable(particleSystem): 

smokeNode一>addChlld(geOde) 

//设置为粒子系统的缺省粒子对象。

particleSystem 一>setDefaultParticleTemplate (parti-

cleTempalte); 

∥获取放射极中缺省计数器的旬柄,调整每帧增加的新粒 

//子数目 

osgParticle::RandomRateCounter particleGenerateRate 

=new osgParticle::RandomRateCounterO; 

particleGenerateRate一>setRateRange(30,50): 

//每秒新生成的粒子范围 

DarticIeGenerateRate一>setDataVariance(osg::Node::DY. 

NAMlC):

∥自定义一个放置器,这里我们创建并初始化一个点放置器 

osgParticle::PointPlacer particlePlacer new osg 

Pa rticle::PointPlacer: 

particlePlacer一>setCenter(osg::Vec3(O.Of,O.0f;0;0Bf)) 

particlePlacer一>setDataVariance(osg::Node::DYNAMlC:C) 

//自定义一个弧度发射器 

osgParticle::RadialShooter particleShooter 2 new osg- 

Pa cle::Rad_alShooter; 

∥设置发射器的属性 

particleShooter一>setDataVariance(osg::Node::DYNAM。

lC): 

particleShooter->setThetaRange(-O.1 f,O.1 f)= 

∥弧度值,与Z轴夹角0.392699f 

pa rclcleShooter一>setPhiRange(一O.1f,O;1f): 

//创建标准放射极对象 

osgParticle::ModularEmitter emitter=new osgPatti_ 

cle::ModularEmitter; 

emitter一>setDataVariance(Osg::NOde::DYNAMlC): 

emitter->setCullingActive(false); 

∥将放射极对象与粒子系统关联。 

emitter->setParticleSystem(particteSystem); 

∥设置计数器 

emitter一>setCOunter(particIeGenerateRate); 

//设置放置器 

emitter->setPlacer(particlePlacer); 

∥设置发射器 

emitter->setShooter(particleShooter}: 

//把放射极添加为变换节点 

smokeNode->addChild(emitter); 

//添加更新器,以实现每帧f约粒子管理。 

osgParticle::ParticleSystemUpdater particleSys- 

temUpdater=new 0sgParticle::ParticleSystemUpdater; 

∥将更新器与粒子系统对象关联。 

plarticleSystemUpdater ->addParticleSystem(parti- 

cleSystem); 

,/将更新器节点添加到场景中。 

smokeNode->addChild(particleSystemUpdater); 

osgPatticle::ModularProgram particleMoveProgram 

neW osgParticle::ModularProgf-am: 

particteMoveProgram->setParticleSystem(partiCleSys- 

tem): 

//最后,将编程器添Dull场景中。 

smokeNode->addChild(particleMoveProgram); 

} 

t jma.nIint argc,』CHAR*argv[]) 

{ 

//根节点 

osg::Group*rootNode=new osg::Group 

osgViewer::Viewer viewer; 

//毒戋入飞机模型 

osgl::Node*flightNode=osgDB::readNodeFile( cessna. 

OSg'1)= 

.f(!flightNode) 

{ 

std::cout<《“no flight”<<std::endl; 

return-1 I l

} 

//飞机变换节点 

osg::MatrixTransform flightTransform=neW osg::Ma 

trixTransform()= 

flightTransform一>addChild(flightNode); 

r0otNode->addChild(flightTransform); 

∥引擎烟雾 

osg::Matr1XTransform firebaIlAndSmoke=new osg:: 

蒜 69 

#一 

# 

t  I’ … 一 。H … … l 、 ………一 … 

实用第一/智慧密集 

MatrixTransform0; 

//仿真循环 

viewer.run() 

return O: 

createFireBal rebaI1AndSmoke); 

createDarkSmoke(fireballAndSmoke); 

fireballAndSmoke->setMatrix(osg::Matrix::rotate f— 

osg::Pl/2,1,O,O) 

osg::Matrix::translate(一8,一1 0,一3)): 

flightTransfOrm一>addChild(fireballAndSmoke); 

参考文献 

[1】http:fwww.openscenegraph.org. 

//设置场景数据 

viewer.setSceneData(rootNode): 

[2]http:#www.nps.navy.mil/cs/sullivan/osgTutorials 

(上接第35页) 

//根据档案号码提出具体的二进制文件即归档的电子数据 

setectblob cl 9 into:B DATA frOm dalist where c01=: 

SDACODE using NowDB; 

1 ‘l 

dw 1.modify(…C+st ring(LFlNDROW)+“.protect= 

end if 

next 

,,写入磁盘 

讦fileexists(“c:WiewDA.psr”)then 

end_f 

end.f 

filedelete(“c:WiewDA.psr“) 

end-f 

查是否可以进行档案编辑 

LFOR=0 

,/写入内存 

gf

writebtobtofile(”cAViewDA.ps ̄',B_D rA) 

_

select count( )into:LFOR from ostasystemvalue where 

c01=:SServerUser and c02= 档案编辑 using NowDB; 

if LFOR>0 thell 

//装载至数据窗口 

dw_1.setredraw(false) 

dw

_

cb new.visible=true 

cb

de1.visible=true 

1.dataobject=Hc:WiewDA.psr“ 

dw

l。setredraw(true) 

_

cb save.visible=true 

cb new.enabled=true 

cb save.enabIed=true 

//dw_1.modifyf datawindow.print.preview=Yes“) 

select c24 into:Stable frOm dalist where c01=:SDA- 

CODE using NowDB; 

_f LPPP>0 then 

for LFlNDROW=1 to LPPP 

If LFlNDROW<1 0 then 

//执行定位 

SWhere=mystrunit.S1 

_

If SWhere<> then 

dw 1.modiy(f“cO“+string(LFINDROW)+".pro- 

tect=…0‘) 

eIse 

dw

1.modify(”c”+string(LFINDROW)+".protect= 

_

_

for LF0R=I tO dw 1.rowcount() 

LFINDROW=dw

1.find(SWhere,1,dw_t.rowcount0) 

if LFtNDROW>1 then 

dw

1.selectrow(LFINDROW,true) 

_

0 } 

end.f 

next 

dw

l。scro Itorow(LFINDROW) 

_

end 

next 

end if 

else 

end if 

select c24 into:S1 from datist where cO1;:SDACODE 

usin}g NowDB 

cb,new.visible=false 

cb de1.visible=false 

cb save.vis_bIe=faIse 

cb new.enabIed=faIse 

。 

S1=UPPER(S1) 

select c02 into:S1 from ostasystemvaIue where c01=: 

S1 using NowDB; 

cb save enabl.ed=faIse 

阡lsnumber(S 1)then 

end if 

LPPP=tong(S1) 

if LPPP>0 then 

4结语 

本档案系统采用C/S模式,用PB9结合MS SQL Server 

2000设计开发实现,系统在扩展性方面也能比较自由地发挥, 

达到了实际应用的要求。 

{Or LFINDROW=1 to LPPP 

|f LFINDROW<1 0 then 

dw

_

1.mod_Ⅳ《 c0|I+string(LFINDROW)+“,pro- 

tect='l ‘) 

else 

与 


本文标签: 粒子 粒子系统 属性