OpenSceneGraph源碼分析——OSG狀態機如何處理傳入的渲染狀態(StateSet)數據

當前位置:osg/State.cpp第376行,osg::State::apply(const StateSet*)
在結束對OSG渲染後臺的研究之前,我們還是看一下OSG狀態機是如何處理傳入的渲染狀態(StateSet)數據的,即State::apply函數的工作流程:

1、使用State::applyModeList函數,接收渲染狀態中的模式數據(使用StateSet::setMode設置),並通過applyMode函數(State頭文件,1132行)予以執行,真正的執行代碼可以濃縮爲以下兩行:
  1. if (enabled) glEnable(mode);
  2. else glDisable(mode);
複製代碼
這裏enabled變量是由是否設置了StateAttribute::ON決定的,而mode就是我們通常使用setMode函數設置的OpenGL枚舉量,例如GL_NORMALIZE。

2、使用State::applyAttributeList函數,接收渲染狀態中的屬性數據(即StateAttribute的派生類對象,使用StateSet::setAttribute設置),並通過applyAttribute函數(State頭文件,1151行)予以執行,它的執行代碼事實上甚至可以濃縮爲一行:
  1. attribute->apply(*this);
複製代碼
變量attribute就是我們設置的StateAttribute對象,而this指的是State類對象。換句話說,此時State類將執行的工作移交給了各個StateAttribute派生類來完成。因此,如果我們希望自己編寫一個新的渲染屬性類(例如,它可以同時完成霧效和圖像融合的工作),只要將虛函數StateAttribute::apply(State&)重寫就可以實現它與OSG渲染後臺的接口了,十分簡單。

State類保存了兩個映射表_modeMap和_attributeMap,用於記錄當前渲染狀態的執行情況,場景繪製的過程中會通過applyModeList和applyAttributeList不斷更新這兩個映射表,以標識那些需要被更新的以及更新完畢的渲染狀態。

3、隨後,針對紋理相關的渲染屬性和模式進行與上兩步相似的處理,之所以將紋理(Texture及其派生類)與通常的渲染狀態區分開,是因爲一個StateSet中可以設置多個同類型的紋理對象(例如多個Texture2D對象),它們分別加載到不同的紋理單元上,以實現多重紋理的效果;而普通的渲染屬性在StateSet中不具備這種特性。

4、使用State::applyUniformList將着色器所使用的Uniform變量傳遞下去(事實上是傳遞給OSG內部的GLSL預編譯器Program:: PerContextProgram處理了),這是實現GLSL與OSG系統交互的重要途徑,它的實現代碼就在這裏。

好了,還記得我們在第十八日給出的“場景圖形,攝像機,圖形設備,渲染器和場景視圖的關係圖”嗎,現在我們也許可以將它完善起來,形成OSG的渲染後臺全景了。
附圖1

OSG渲染後臺與用戶層的接口是攝像機類(Camera)。場景中至少有一個主攝像機,它關聯了一個圖形設備(GraphicsContext,通常是窗口),以及一個渲染器(Renderer);我們可以在場景樹中(或者別的視圖View中,對於複合視景器而言)添加更多的攝像機,它們可以關聯相同的或者其它的圖形設備,但都會配有單獨的渲染器,用以保存該攝像機的篩選設置、顯示器設置等信息。

場景篩選和繪製的工作由渲染器來完成,而圖形設備GraphicsContext則負責根據不同時機的選擇,調用渲染器的相關函數。例如在單線程模式中,ViewerBase::renderingTraversals函數依次執行Renderer::cull和Renderer::draw函數(後者通過GraphicsContext::runOperations調用),而在多線程模型中調用者的關係將更加錯綜複雜。

OSG渲染後臺的調度中心是場景視圖(SceneView),它負責保存和執行篩選訪問器(CullVisitor)。CullVisitor負責遍歷並裁減場景,同時在遍歷過程中構建對於場景繪製至關重要的渲染樹和狀態樹;生成的狀態樹以StateGraph爲根節點和各級子節點(其中保存場景樹的渲染狀態StateSet數據),以RenderLeaf爲末端葉節點的內容(其中保存場景樹中的幾何體Drawable對象);渲染樹則以RenderStage爲根節點,RenderBin爲各級子節點,根據渲染順序和方法的設定,狀態樹中的節點和渲染葉(RenderLeaf)被記錄到RenderStage和各級RenderBin中;SceneView負責保存和維護狀態樹和渲染樹。

繪製場景時,渲染樹中的各級節點將取出保存的渲染葉數據,傳遞給OSG狀態機(State)。後者是OpenGL狀態機制的封裝和實現,也是場景繪製的核心元件。狀態機取得渲染葉中的幾何數據之後,再向根部遍歷狀態樹,取得該幾何體繪製相關的所有渲染狀態設置,並親自或者交由StateAttribute派生類完成渲染狀態的實際設定,以及場景元素的實際繪製工作。此時用到的就已經是我們耳熟能詳的各種OpenGL函數了。

那麼,有關OSG場景繪製部分的介紹,就此告終吧。希望這洋洋灑灑的漢字,在您解讀OSG源代碼時能起到些許的幫助。

當前位置:osgViewer/ViewerBase.cpp第243行,osgViewer:: ViewerBase::startThreading()
上一次我們討論startThreading函數還是在第十六日的時候,隨後我們在假定使用單線程方式運行的前提下,深入到OSG的渲染後臺中,一起瀏覽和討論了場景篩選的流程和執行函數,狀態樹與渲染樹的構建,遍歷渲染樹實現場景繪製的過程,遍歷狀態樹收集對象渲染狀態的過程,OSG狀態機及其與OpenGL狀態機的關係,渲染後臺與Drawable幾何體和StateAttribute渲染屬性的關係。這其中,場景視圖類SceneView可謂是整個渲染後臺的指揮部分,而攝像機的渲染器(Renderer)和圖形設備(GraphicsContext)分別負責執行場景篩選(CULL)和繪製(DRAW)的操作。

雖然我們還有三種不同的線程處理方式要討論(它們分別是CullDrawThreadPerContext,DrawThreadPerContext和CullThreadPerCameraDrawThreadPerContext),但總體上講,渲染後臺的處理函數和處理流程不會再有大的變化,線程模型的選擇也許可以概括爲一個決定誰來調用SceneView::cull,以及誰來調用SceneView::draw的過程,但是其流程卻遠沒有這麼簡單,完成的效果也是大相徑庭。

那麼首先摘錄第十六日的一部分文字。
startThreading函數一開始,先要完成這樣幾個工作:
  • 1、執行ViewerBase::releaseContext,首先釋放渲染上下文;
  • 2、如果用戶沒有設置線程模型,則使用ViewerBase::suggestBestThreadingModel自動進行判斷;
  • 3、使用Viewer::getContexts函數獲取當前所有的圖形設備(GraphicsContext);
  • 4、使用Viewer::getCameras函數獲取當前所有的攝像機(主攝像機和所有從攝像機)。

這之後,對於單線程模式(SingleThreaded)來說,將直接退出函數的執行,而對於其它三種多線程的運行模式而言,一切纔剛剛開始。
發佈了82 篇原創文章 · 獲贊 138 · 訪問量 79萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章