LINK:
http://cg.cnblogs.com/default.aspx?page=6&paging=1 http://bbs.vrchina.net/viewthread.php?tid=3472osgFX - 開發(fā)者簡明手冊
Marco Jez
2003年9月
osgFX是一個(gè)OpenSceneGraph的附加庫,是一個(gè)用于實(shí)現(xiàn)一致、完備、可重用的特殊效果的構(gòu)架工具,其效果可以添加到OSG的節(jié)點(diǎn)中。它同時(shí)還包含了一系列預(yù)定義好的特殊效果。
osgFX概述
所謂“特效”指的是裝載于單個(gè)對象中的一系列可視的屬性和行為。要實(shí)現(xiàn)一個(gè)真正可用的特效,相應(yīng)的特效類應(yīng)當(dāng)具備一個(gè)公有的接口,以修改各種配置和微調(diào)量。
特效也可以被理解成是提出問題(對象應(yīng)當(dāng)是什么樣子)與解決問題(應(yīng)當(dāng)設(shè)置哪些屬性和其它調(diào)節(jié)量)之間的“橋梁”。從C++代碼來看,特效具現(xiàn)了osgFX::Effect類的實(shí)例。或者說,是這個(gè)類的派生類的實(shí)例,因?yàn)閛sgFX::Effect直接派生自osg::Node,因此它是抽象類。
對于OSG而言,特效就是一個(gè)Node節(jié)點(diǎn)。它與其它節(jié)點(diǎn)類的特性完全相同,因此可以關(guān)聯(lián)到場景圖形中的任意位置。
特效功能圖如圖1所示。
Effect類是一個(gè)多子節(jié)點(diǎn)的組節(jié)點(diǎn)。它使用addChild()方法和其它節(jié)點(diǎn)關(guān)聯(lián)。
在特效類中設(shè)置的可視屬性將被關(guān)聯(lián)到它的子節(jié)點(diǎn)上,與此相類似,Transform節(jié)點(diǎn)也會將坐標(biāo)變換的信息應(yīng)用到其子節(jié)點(diǎn)上。Effect中的各種屬性不會在其子節(jié)點(diǎn)以外生效。
如果用戶想要將某一種特效應(yīng)用到自己的圖形子樹上,那么需要遵循下面的步驟:
1、創(chuàng)建所需特效的實(shí)例,例如,osgFX::Scribe;
2、必要的話,使用特效類的方法設(shè)置特效屬性;
3、調(diào)用Effect::addChild()方法,將圖形子樹與特效節(jié)點(diǎn)相關(guān)聯(lián);
4、將特效節(jié)點(diǎn)與場景圖形關(guān)聯(lián)。
下面的例子中使用了刻線(scribe)特效:
osg::ref_ptr<osg::Node> my_node = osgDB::readNodeFile(“cow.osg”);
osg::ref_ptr<osgFX::Scribe> scribe_fx = new osgFX::Scribe;
scribe_fx->addChild(my_node.get());
scribe_fx->setEnabled(true);
root->addChild(scribe_fx.get());
代碼執(zhí)行的結(jié)果如圖2所示。
深入學(xué)習(xí):技法和通道
技法就是實(shí)現(xiàn)特效的某一種可能方法。
由于圖形硬件設(shè)備種類繁多,OpenGL也在不斷擴(kuò)展,因此不太可能用一種通用的方法來實(shí)現(xiàn)復(fù)雜的效果:針對不同的硬件和OpenGL環(huán)境,用戶需要采用不同的實(shí)現(xiàn)手段來實(shí)現(xiàn)某個(gè)特效。
一種特效的產(chǎn)生往往可以采用一種或幾種技法,每一種技法都采用不同的方式來嘗試實(shí)現(xiàn)相同的效果。
缺省情況下,Effect類使用私有的StateAttribute對象來實(shí)時(shí)演算和驗(yàn)證各種技法,并選擇最好的一種。
特效的開發(fā)者可以自行定義各種技法的優(yōu)先級,從而要求OSG首先驗(yàn)證用戶所選的技法。
Effect類會選擇在實(shí)時(shí)的所有活動渲染設(shè)備中,可通過驗(yàn)證的優(yōu)先級最高的技法,以為己用。
如果需要的話,用戶可以在任何時(shí)刻重載這一缺省特性。
Effect類的技法功能圖表如圖3所示。
多通道渲染的意思是,每次都使用不同的可視屬性,多次繪制同一對象后,合并所有通道獲得的最終圖像。
某些技法可能需要不止一個(gè)通道來實(shí)現(xiàn)所需的輸出結(jié)果。
技法類為每個(gè)渲染通道都創(chuàng)建一個(gè)StateSet對象,然后交由osgFX管理多通道的渲染工作。
Effect類的通道功能圖表如圖4所示。
擴(kuò)展osgFX
創(chuàng)建一個(gè)新的特效的基本步驟如下。
1、特效都是從osgFX::Effect派生而來的,因此用戶可以自由創(chuàng)建自己的派生類,例如命名它為TestFx。
2、具現(xiàn)抽象方法,例如effectName(),effectDescription()等,可能需要用到META_Effect宏。
3、向系統(tǒng)注冊新的特效類,即創(chuàng)建一個(gè)Registry::Proxy的靜態(tài)實(shí)例:
osgFX::Registry::Proxy proxy(new TestFx);
4、具現(xiàn)保護(hù)成員中的抽象方法define_techniques(),以便創(chuàng)建所需的特效技法。
為了實(shí)現(xiàn)某個(gè)技法,用戶需要編寫一個(gè)繼承自osgFX::Technique的類;且這個(gè)類應(yīng)當(dāng)是私有的。
在用戶特效類的define_techniques()方法中,創(chuàng)建上述用戶技法類的實(shí)例,并使用Effect::addTechnique()按照優(yōu)先級降序的順序?qū)⑵涮砑拥教匦ь愔小?
為新建的技法提供一個(gè)驗(yàn)證手段。最簡單(但不是最靈活的)的方法是重載Technique::getRequiredExtensions()方法,并指定這個(gè)技法所需的OpenGL擴(kuò)展函數(shù)。
具現(xiàn)Technique::define_passes()方法,以便創(chuàng)建渲染通道。
渲染通道的內(nèi)部實(shí)現(xiàn),是將其作為一個(gè)Group對象與一個(gè)StateSet相關(guān)聯(lián)。特效類的子節(jié)點(diǎn)在運(yùn)行時(shí)將自動被添加到通道節(jié)點(diǎn)上。
技法類的define_passes()方法為每個(gè)渲染通道創(chuàng)建了一個(gè)StateSet對象,并調(diào)用Technique::addPass()將其添加到技法類中。通道節(jié)點(diǎn)將自動生成并連接到渲染狀態(tài)之上。
以下為創(chuàng)建一個(gè)特效類所需的基本代碼:
Class TestFX (public)
{
……
META_Effect(……);
bool define_techniques()
{
addTechnique(new FirstTechnique);
// 也可以繼續(xù)添加別的技法實(shí)例。
}
……
}
Class FirstTechnique (private)
{
……
void getRequiredExtensions(……) const
{
// 指定所需的GL擴(kuò)展功能。
}
void define_passes()
{
osg::ref_ptr<osg::StateSet> ss1 = new osg::StateSet;
// 添加渲染屬性到ss1之后……
addPass(ss1.get());
osg::ref_ptr<osg::StateSet> ss2 = new osg::StateSet;
// 添加渲染屬性到ss2之后……
addPass(ss2.get());
}
……
}
總結(jié):
1、繼承osgFX::Effect并創(chuàng)建特效類(例如TestFx),為其添加名字和描述信息,并使用注冊代理(registry proxy)注冊到系統(tǒng)中;
2、為用戶所需的每個(gè)技法創(chuàng)建私有類,并定義它們的驗(yàn)證手段;
3、在TestFx::define_techniques()中創(chuàng)建一個(gè)技法類的實(shí)例,并調(diào)用addTechnique()將其添加到特效中;
4、在每個(gè)技法類的define_passes()方法中,創(chuàng)建一個(gè)或多個(gè)StateSet對象(每個(gè)渲染通道創(chuàng)建一個(gè)),并調(diào)用addPass()將其添加到技法中。例子程序
osgfxbrowser的效果如圖5~8所示。對于目前已提供的特效,簡介如下:
刻線(Scribe)
這是一個(gè)雙通道的特效;第一個(gè)通道以通常的方式渲染圖形,而第二個(gè)通道使用線框模式,用戶設(shè)置好光照和材質(zhì)之后,即可使用指定的顏色進(jìn)行渲染。這個(gè)特效中使用了PolygonOffset渲染屬性類來避免多邊形斑駁(Z-fighting)的現(xiàn)象,它所需的OpenGL版本至少為1.1。各向異性光照(Anisotropic Lighting)
這種特效使用單一通道,它使用了一種各向異性的光照來替代OpenGL的標(biāo)準(zhǔn)光照模型。幾何體頂點(diǎn)的顏色在這里不是直接進(jìn)行計(jì)算的,而是紋理映射到用戶指定的光照圖板的結(jié)果。這里需要使用頂點(diǎn)著色器(vertex program)來計(jì)算紋理坐標(biāo)S和T的值:S = N · H;T = N · L。(其中的數(shù)學(xué)運(yùn)算為點(diǎn)乘)這里N表示頂點(diǎn)的法線,L表示光到頂點(diǎn)的向量,H表示中間向量。這種特效很好地演示了State::getInitialViewMatrix()方法的使用,它可以直接獲取視口的初始矩陣并實(shí)現(xiàn)直接與視口相關(guān)的特效,而不需要任何假借的工作。
該特效需要ARB_vertex_program擴(kuò)展的支持。
卡通渲染(Cartoon)
這種特效實(shí)現(xiàn)了一種名為卡通著色(Cel-Shading)的技法,從而產(chǎn)生一種卡通式的(非真實(shí)感的)的渲染效果。它需要兩個(gè)通道支持;第一個(gè)用于繪制實(shí)體表面,第二個(gè)用于繪制輪廓線。該特效需要使用頂點(diǎn)著色器來設(shè)置紋理坐標(biāo),以便在運(yùn)行時(shí)生成的紋理單元0上實(shí)現(xiàn)一種尖銳的光照效果。
該特效需要ARB_vertex_program擴(kuò)展或者OpenGL著色語言的支持。
基于立方映射圖的鏡面高光(Cubemap-based Specular Highlights)
這種特效在片斷層級(fragment level)上(而不是OpenGL通常的頂點(diǎn)層級)應(yīng)用了鏡面高光,它使用了立方映射圖和反射紋理生成(reflective texgen)的技術(shù)。首先要計(jì)算出紋理矩陣以實(shí)現(xiàn)立方映射圖的自動旋轉(zhuǎn);這樣無論從觀察的方向和光照位置上來說,鏡面光的效果都將是始終不變的。用戶可以選擇使用何種光照來計(jì)算紋理矩陣。
該特效需要GL_ARB_texture_env_add擴(kuò)展以及任意一種立方映射圖擴(kuò)展(GL_EXT_texture_cube_map,GL_ARB_texture_cube_map,或者OpenGL 1.3)的支持。
凹凸貼圖(Bump Mapping)
這種特效可以創(chuàng)建一種凹凸不平的表面效果。其子節(jié)點(diǎn)必須使用兩種紋理,其一是漫反射顏色,另一個(gè)是法線貼圖(可以使用nVIDIA的法線貼圖生成器或者其它工具,根據(jù)高度圖自動生成)。此外,還需要?jiǎng)?chuàng)建正切空間(tangent-space)的基向量并將其關(guān)聯(lián)到每個(gè)Geometry幾何體上;這一步驟可以調(diào)用BumpMapping::prepareChildren()方法來迅速完成。注意Geometry對象的漫反射顏色和法線貼圖紋理都必須提前定義好對應(yīng)的UV貼圖。
該特效推薦使用一種運(yùn)用了ARB頂點(diǎn)和片斷著色器的技法,另外還定義了一種不使用片斷著色器的技法。后者無法處理環(huán)境和鏡面組件的運(yùn)算,因此在運(yùn)行時(shí)很受限制。