????????虛擬現實中有很多效果,如雨效、雪效、霧效等,這些都可以通過粒子系統來實現。一個真實的粒子系統的模式能使三維場景達到更好的效果。
????????本章對OSG粒子系統的使用以及生成自定義粒子系統的方法進行了詳細介紹最后還附帶說明了陰影的使用方法。在實時的場景中,陰影是非常重要的,是一個很大的范疇,筆者也沒有深入研究,因此,這里只是簡單介紹一下。
粒子系統
????????粒子系統是一個非常復雜的粒子模擬過程。在 OSG中專門定義了新的名字空間 osgParticle 來處理粒子系統的模擬。
????????osgParticle 能夠高效地模擬粒子系統,生成非常真實的效果。在OSG 預定義的粒子系統中,大部分的粒子系統模擬都采用的是 Billboard 與色彩融合技術生成粒子。Billboard 技術前面已經講到過雖然它還存在很多問題,但是總體來說,效果還是非常不錯的。色彩融合技術就是在渲染的過程中將各種顏色,如頂點顏色、光照顏色、質顏色和紋理顏色等按照 Alpha 值按一定的比例進行融合,以達到真實的效果。在本書自定義的粒子系統示例中,會向讀者展示一個爆炸的效果,當然只是演示一個簡單的技術,如果深入的話,還需要重新定義模塊。
粒子系統的主要模塊
????????當打開粒子系統的文檔時,讀者會發現里面包含很多類,但很多類都是內部操作,在模擬一個粒子系統時,只需要使用其中的一部分就可以完成很好的模擬效果,具體使用的類如圖11-1 所示。
11-1子系統成塊
????????對于一個普通的粒子系統的模擬,可以用圖 11-1 來顯示主要模塊。通過圖 11-1 讓讀者明白一個粒子系統所需要的模塊。下面分別介紹這些模塊。
- 放射極(osgParticle::Emitter):一個標準放射極(osgParticle::ModularEmitter)包括一個計數器、一個放置器和一個發射器,它為用戶控制粒子系統中多個元素提供了一個標準機制。
- 粒子系統(osgParticle::ParticleSystem):維護并管理一系列粒子的生成、更新染和銷毀。粒子系統類繼承自Drawable類,用于控制粒子的渲染,因此與其他 Drawable對象的渲染類似,控制其渲染屬性StateAttribute 即可。OSG提供了一個方便的函數以允許用戶控制3個常用的渲染狀態屬性,方法setDefaultAttributes可用于指定材質(或指定為NULL以禁用材質)、允許/禁止附加的圖像融合及允許/禁止光照。
- 粒子(osgParticle::Particle):粒子系統的基本單元。粒子類同時具有物理屬性和圖像屬性,它的形狀可以是任意的點(PONT)、四邊形(QUAD)、四邊形帶(QUADTRIPSTRIP)、六角形(HEXAGON)或線(LINE)。每個粒子都有自己的生命周期,生命周期也就是每個粒子可以存活的秒數,生命周期為負數的粒子可以存活無限長時間。所有的粒子都具有大小(SIZE)、Alpha值和顏色(COLOR)屬性,每組粒子都可以指定其最大和最小值。為了便于粒子生命周期的管理,粒子系統通過改變生命周期的最大和最小值來控制單個粒子的渲染,它會根據已經消耗的時間在最小和最大值之間進行線性插值。
- 放置器(osgParticle::Placer):設置粒子的初始位置。用戶可以使用預定義的放置器或定義自己的放置器,已經定義的放置器包括點放置器 PointPlacer(所有的粒子從同一點出生)、扇面放置器SectorPlacer(所有的粒子從一個指定中心點、半徑范圍和角度范圍的扇面出生)以及多段放置器MultiSegmentPlacer(用戶指定一系列的點,粒子沿著這些點定義的線段出生)。
- 發射器 (osgParticle::Shooter):指定粒子的初始速度。RadialShooter 類允許用戶指定一個速度范圍(米/秒)以及弧度值表示的方向,方向由兩個角度指定(theta角是與Z軸的夾角,phi角是與XY平面的夾角)。
- 計數器 (osgParticle::Counter):控制每一產生的粒子數。RandomRateCounter 類允許用戶指定每幀產生粒子的最大和最小數。
- 粒子系統更新器(osgParticle::ParticleSystemUpdater):用于自動更新粒子,將其置于場景中時,它會在揀選遍歷中調用所有“存活”粒子的更新方法。
- 標準編程器(osgParticle::ModularProgram):在單個粒子的生命周期中,用戶可以使用ModularProgram實例控制粒子的位置,ModularProgram需要與Operator對象組合使用。
- 操作器(osgParticle::Operator):提供了控制粒子在其生命周期中的運動特性的方法。用戶可以改變現有 Operator 類實例的參數或定義自己的 Opcrator 類。OSG提供的Operator類包括AccelOperator(加速度)、AngularAccelOperator(角加速度)、FluidFrictionOperator (空氣阻力或流體操作)以及ForceOperator(壓力)。
????????在OSG中除了這些粒子系統的主要模塊以外,還包含其他的已經定義好的模塊,如osgParticle::ExplosionDebrisEfect(爆炸碎片)、osgParticle::ExplosionEffect (爆炸模擬)、osgParticle::SmokeEfect(煙霧模擬)和 osgParticle::FireEffect(火光模擬)。
????????還有一個比較重要的類osgParticle::PrecipitationEfect,它是OSG定義的新類,用來模擬一些在OSG中已經定義好的粒子系統,如雨效和雪效,使用方法很簡單,可以直接加入到場景中。
???????粒子系統的模擬過程
????????下面將介紹如何模擬一個真實的粒子系統。對于模擬粒子系統的過程可以分為兩種,一是OSG中已經定義好的粒子系統模塊,二是根據需要自定義粒子系統。預定義粒子系統模塊模擬過程如下;
????????(1) 創建預定義粒子系統模塊對象,設置相應的參數。
????????(2) 作為子節點加到場景節點中。從上面列舉的子系統的關系繼承圖中可以看出,它們繼承自osg::Node或osg::Group 節點,因此可以直接作為一個節點加入到場景中。
????????自定義粒子系統模擬過程如下:
????????(1)創建粒子系統(osgParticle::ParticleSystem),并將其加入到場景中,設置相應的屬性,如材質、放射及光照。
????????(2)創建粒子模板(osgParticle::Particle),控制場景中每一個粒子的特性并關聯到粒子系統,設置粒子模板對應的特性,如大小、顏色、生命周期及重量等。
????????(3)創建粒子系統放射器(osgParticle::ModularEmitter),標準的放射器包括計數器(Counter)、放置器(Placer)和發射器(Shooter)3 部分,設置相應的屬性,如位置、形狀、速度和方向等。
????????(4)創建粒子系統編程器對象(osgParticle::Program),控制粒子在聲明周期內的運動。一個標準編程器對象包含各種操作器,如osgParticle::AccelOperator和osgParticle;:FluidFrictionOperator等。
????????(5)創建粒子系統更新器(osgParticle::ParticleSystemUpdater),用于管理每一幀的粒子的屬性如位置、速度和方向等。
????????通過上面的步驟,可以完成一個簡單的粒子系統的模擬。對于一般的需要而言是沒有任何問題的。如果需要更高要求的,可以從shader 開始編寫屬于自己的粒子系統。
???????霧效模示例
????????霧效其實并不是一種粒子系統,只是一種狀態屬性,放在這里來演示,因為它本身很像一種粒子系統。
????????霧效的管理主要是由osg::Fog來控制染的。osg::Fog類直接繼承自osg::StateAttribute類繼承關系圖如圖11-2所示。
圖11-2 osg::Fog 的繼承關系圖
????????從繼承關系圖中可以看到,它繼承自osg::StateAttribute類,因此它同樣可以通過設置狀態模式來控制霧效的開啟或關閉,代碼如下:
- root->getOrCreateStateSet()->setAttributeAndModes(fog.get(),osg::StateAttribute::ON);?
????????在OSG中,霧效有兩種模式,可以通過下面的方式來獲取或設置:
- void?setMode(Mode?mode)??
- Mode?getMode()?const??
- enum?Mode??
- {??
- ????LINEAR?=?GL_LINEAR,//?線性務??
- ????EXP?=?GL_EXP,?//全局霧??
- ????EXP2?=?GL_EXP2//?全局霧??
- };?
????????霧的坐標源也有兩種,可以通過下面的方式來設置或獲取:
- void?setFogCoordinateSource(GLint?source)??
- GLint?getFogCoordinateSource()?const??
- enum?FogCoordinateSource??
- {??
- ????FOG_COORDINATE?=?GL_FOG_COORDINATE,//霧坐標??
- ????FRAGMENTDEPTH?=?GL_FRAGMENT_DEPTH//?眼坐標??
- };?
????????霧的坐標源在使用固定管道的頂點處理時,霧效的值可以是眼坐標系中的y坐標值,也可以是經過插值的霧坐標,這是由霧的標源是設置成GL_FRAGMENT_DEPTH還是GL_FOG_COORDINATE決定的,在可編程管線中應用比較多。
????????霧效的特性還有顏色、濃度和起始位置等,可以調用下列類的成員函數來設置相應的特性:
- void?setDensity(float?density)//?設置濃度??
- float?getDensity()?const??
- void?setStart(float?start)//?設置起點??
- float?getStart()?const??
- void?setEnd(float?end)//?設置終點??
- float?getEnd()?const??
- void?setColor(const?Vec4?&color)?//?設置霧的顏色??
- const?Vec4?&getColor()?const??
????????霧效的特性已經都講了,解釋了霧效可能需要設置所有特性,下面來看一個簡單的示例。
代碼如程序清單11-1 所示。
// 創建霧效
osg::ref_ptr<osg::Fog> createFog(bool m_Linear)
{// 創建Fog對象osg::ref_ptr<osg::Fog> fog = new osg::Fog();// 設置顏色fog->setColor(osg::Vec4(1.0, 1.0, 1.0, 1.0));// 設置濃度fog->setDensity(0.01);// 設置霧效模式為線性霧if (!m_Linear){fog->setMode(osg::Fog::LINEAR);}else// 設置霧效模式為全局零{fog->setMode(osg::Fog::EXP);}// 設置霧效近點濃度fog->setStart(5.0);// 設置霧效遠點濃度fog->setEnd(2000.0);return fog.get();
}void fog_11_1(const string &strDataFolder)
{osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;traits->x = 40;traits->y = 40;traits->width = 600;traits->height = 480;traits->windowDecoration = true;traits->doubleBuffer = true;traits->sharedContext = 0;osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());osg::ref_ptr<osg::Camera> camera = viewer->getCamera();camera->setGraphicsContext(gc.get());camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;camera->setDrawBuffer(buffer);camera->setReadBuffer(buffer);osg::ref_ptr<osg::Group> root = new osg::Group();// 讀取模型string strDataPath = strDataFolder + "lz.osg";osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(strDataPath);root->addChild(node.get());// 啟用霧效root->getOrCreateStateSet()->setAttributeAndModes(createFog(false), osg::StateAttribute::ON);// 優化場景數據osgUtil::Optimizer optimize;optimize.optimize(root.get());viewer->setSceneData(root.get());viewer->realize();viewer->run();
}
????????運行程序,截圖如圖11-3所示
圖11-3 霧效模擬示例截圖