最簡單的頂點數據更新方法是預先獲取setVertexArray()所用的數組數據,並對其進行更新。但是對於開啟顯示列表支持的幾何體(這是默認的情況)來說,有一個問題需要特別需要引起注意,即顯示列表中的數據不能動態進行修改。任何對於頂點數據的修改都需要銷毀和重新建立相應的顯示列表。換句話說,由於Drawable::draw()函數在仿真循環中默認使用glcallList()直接調用已編譯的顯示列表,因而用於實現頂點綁定功能的drawImplementation()函數只有在構建顯示列表時被執行一次,以后將不再被調用,除非強制重新對其進行調用。
因此,當我們手動更新了幾何體的頂點數據,並且所使用的渲染方式為顯示列表(setUseDisplayList())時,需要調用dirtyDisplayList()來強制刷新與其關聯的顯示列表。
當需要更改的數據采用動畫的形式來表現的時候,這意味着每一幀都需要執行一次顯示列表的重建工作——這無疑是難以讓人接受的。此時我們多半會選擇慢速通道或者VBO的數據渲染方式,后者顯然更加高效和適宜,而對於配置較低的系統而言,使用glBegin/glEnd序列來進行動態對象的表達也是一種不錯的方式。
使用VBO來更新幾何對象后,需要使用Vec3Array::dirty()來將修改結果通知給VBO對象
關於DataVariance
場景數據動態更新的核心就是設置數據變度屬性DataVariance,它決定了OSG在多線程渲染的過程中的執行策略:只有所有DYNAMIC屬性的對象被渲染完畢之后,OSG才會開始執行下一幀的用戶更新操作;這樣有效地可以避免數據的過快更新造成當前的渲染動作出錯,以致系統崩潰。
所有派生自osg::Object的對象都可以設置數據變度的屬性,通常設置的時機在新建對象之時,例如:
osg::ref_ptr<osg::Group>
node = new
osg::Group;
node->setDataVariance(osg::Object::DYNAMIC);
...
這就意味着這個組節點可能在渲染過程中發生變動,可能變動的內容包括:成員屬性的改變,新增/替換/刪除子節點,或者使用事件/更新/裁減回調實現的自定義變動。
數據變度的設置對於渲染狀態和紋理的動態更新同樣有重要的意義。而對於osg::Drawable對象,設置為動態更新意味着它可能在渲染過程中發生頂點屬性或者圖元的變化;這其中尤為要提及osgText::Text,文字屬性的變化必須預先設置動態的數據變度屬性,否則可能造成系統運行過程中崩潰:
osg::ref_ptr<osgText::Text>
text = new
osgText::Text;
text->setDataVariance(osg::Object::DYNAMIC);
...
// 渲染過程中可以動態改變文字的位置和內容等屬性
{
text->setPosition( ... );
text->setText( ... );
}
數據變度不能被自動繼承。它事實上只有STATIC和DYNAMIC兩個可選值,而UNSPECIFIED意味着系統將自動根據初始化時的情況給對象設置數據變度的屬性。默認情況下,任何新建對象都是UNSPECIFIED的,如果系統在第一次執行渲染前發現它附帶有更新/事件/裁減回調對象的話,則會自動給它設置setDataVariance(osg::Object::DYNAMIC);因此,我們可以說過多的更新回調會造成系統的渲染效率在一定程度上降低,不過即使在非常極端的情況下,也不會低於單線程模式的效率。
OSG在setDataVariance()方法中提供了解決方法,該方法屬於osg::Object類,這是所有場景對象的基類。這可以設置為三個枚舉值之一:UNSPECIFIED(默認),STATIC與DYNAMIC。場景圖中的DYNAMIC對象必須在繪制遍歷的開始進行處理。也就是,渲染后端應確保所有節點以及被指定為DYNAMIC的場景對象在下一幀的更新與裁剪遍歷開始之前已完成繪制。然而,STATIC對象,在更新與繪制過程中會保持不變,從而會被稍后渲染且不會阻塞幀速率。
默認情況下,所有新分配的對象都被指定為UNSPECIFIED,包括節點,可繪制元素,狀態集以及屬性。這允許OSG預測數據變化。另一方面,我們總是可以重置該值並使其由下一幀開始工作,例如:
node->setDataVariance( osg::Object::DYNAMIC );
簡單來講,就是為了線程同步,避免一個正在渲染線程中被渲染的對象在仿真渲染線程中被修改而發生訪問沖突。
這也是我從前的理解,於是對所有可能會動態修改的東西,都設置了為DYNAMIC的,包括一些可能會動態添加、刪除子節點的組節點,也設置為DYNAMIC。
但是,今天翻看了一下這塊的代碼,發現在渲染中被優先渲染的“動態”drawable只有兩種情況:
1)Drawable本身被指定為DYNAMIC或被指定了回調
2)一個節點的StateSet是DYNAMIC的話,其子樹中的所有Drawable被視為“動態”
由此可見,只有需要動態修改StateSet或Drawable時,才需要設置其為DYNAMIC。
而對於節點設置DYNAMIC是沒有意義的。