1. 程式人生 > >WebGL------osg框架學習二

WebGL------osg框架學習二

  今天我們繼續來學習osg.js框架。上一篇我們介紹了DrawActor物件繪製操作類和Drawable可繪製物件類,我們大致知道了osg對Drawable可繪製物件的繪製流程管理。今天我們要繼續介紹StateBin狀態樹節點類。我們來看一下StateBin,他管理的是StateSet狀態,他將每個模型節點的StateSet狀態資訊(shaderLink,材質,depth等)包裝成樹節點,從而能夠將狀態節點遞迴組裝成一棵狀態樹。我們來看看StateBin的建構函式。

 1 /*
 2 狀態樹結點
 3 儲存一個StateSet,每個StateSet都有一個唯一ID
 4  */
 5 //let MemoryPool = require('../util/MemoryPool');
6 7 let StateBin = function () { 8 this._stateset = undefined; 9 this._parent = undefined;//StateBin 10 this._children = {};//屬性名為StateSet的ID,值為StateBin 11 //this._children._keys = [];//StateSet的ID 12 this._depth = 0;//樹的深度值 13 };

  首先我們可以看到StateBin的成員,this._stateset這就是模型節點的狀態資訊(shaderLink,材質,depth),this._parent該樹節點的父節點,this._children該樹節點的子節點,this._depth樹的深度。這是一個典型的樹節點類,熟悉資料結構的同學都明白如何遞迴構造一棵樹,鯽魚就不再囉嗦了。我們接下來看看StateBin的成員函式有哪些。

 1 StateBin.prototype = {
 2     getStateSet: function () {
 3         return this._stateset;
 4     },
 5     setStateSet: function (s) {
 6         this._stateset = s;
 7     },
 8     getParent: function () {
 9         return this._parent;
10     },
11     reset: function () {//重置資料,一般都是根節點呼叫
12
this._stateset = undefined; 13 this._parent = undefined; 14 15 //之所以遍歷是為了StateGraph不被析構以記憶體複用,而不是每次都重新建立 16 //然後是StateGraph的資料必須被清空,重新使用時不會出錯 17 // let keys = this._children.keys(); 18 // let l = keys.length; 19 // for (let i = 0; i < l; i++) { 20 // let key = keys[i]; 21 // let child = this._children[key]; 22 // child.reset(); 23 // //記憶體池 24 // //MemoryPool.StateBin.put(child); 25 // } 26 this._children = {}; 27 //this._children._keys.length = 0; 28 //this._children._keys = []; 29 this._depth = 0; 30 }, 31 addStateBinChild: function (bin) { 32 bin._parent = this; 33 bin._depth = this._depth + 1; 34 let id = bin._stateset.getID(); 35 this._children[id] = bin; 36 }, 37 addStateSetChild: function (stateset) {//新增子節點,以stateset的id為key,返回新建立或者已經存在的StateBin 38 let id = stateset.getID(); 39 let child = this._children[id]; 40 if (child) { 41 return child; 42 } else { 43 let sg = new StateBin(); 44 //let sg = MemoryPool.StateBin.get(); 45 sg._parent = this; 46 sg._depth = this._depth + 1; 47 sg._stateset = stateset; 48 this._children[id] = sg; 49 //children._keys.push(id); 50 return sg; 51 } 52 }, 53 removeStateBinChild: function (bin) { 54 let id = bin._stateset.getID(); 55 let cbin = this._children[id]; 56 if (cbin) { 57 cbin.parent = undefined; 58 delete this._children[id]; 59 } 60 }, 61 removeStateSetChild: function (stateset) { 62 let id = stateset.getID(); 63 let cbin = this._children[id]; 64 if (cbin) { 65 cbin.parent = undefined; 66 delete this._children[id]; 67 } 68 }, 69 removeChildren: function () { 70 this._children = {}; 71 }, 72 };

我們一個一個來看,getStateSet獲取當前樹節點的渲染狀態資訊this._stateset;setStateSet設定當前樹節點的渲染狀態資訊即修改this._stateset;getParent獲取當前樹節點的父節點;reset初始化節點資料,將節點屬性清空析構;addStateBinChild向當前樹節點中加入一個子節點;addStateSetChild如果當前樹節點存在id是stateset的id的子節點,就返回該子節點,如果不存在就建立一個stateset狀態的子節點並返回;removeStateBinChild刪除當前節點的確定id的某個子節點;removeStateSetChild刪除當前節點某個狀態是stateset的子節點;removeChildren刪除該樹節點的所有子節點。

  我們可以清楚的看到,成員函式基本都是對樹結構的操作,最後還有一個方法是對父狀態的遍歷繼承,我們來看一下。

 1 //父狀態的匹配
 2 StateBin.moveStateBin = function (glstate, preStateBin, curStateBin) {
 3     if (curStateBin === preStateBin) {//兩個相同什麼都不做
 4         return;
 5     }
 6 
 7     if (curStateBin === undefined) {
 8         //curStateBin已經到頂,彈出preStateBin的所有狀態
 9         do {
10             if (preStateBin._stateset !== undefined) {
11                 glstate.popStateSet();
12             }
13             preStateBin = preStateBin._parent;
14         } while (preStateBin);
15         return;
16     }
17 
18     if (preStateBin === undefined) {
19         //preStateBin已經到頂,壓入curStateBin的所有狀態
20         //從子節點往根節點遍歷獲取所有的狀態,但是推給glstate必須從根節點往子節點遍歷
21         //所以這裡先塞到一個stack裡面,然後再遍歷stack推給glstate
22         let stack = [];
23         do {
24             if (curStateBin._stateset !== undefined) {
25                 stack.push(curStateBin._stateset);
26             }
27             curStateBin = curStateBin._parent;
28         } while (curStateBin);
29 
30         let size = stack.length - 1;
31         for (let i = size; i >= 0; --i) {
32             glstate.pushStateSet(stack[i]);
33         }
34         return;
35     } else if (preStateBin._parent === curStateBin._parent) {
36         // first handle the typical case which is two glstate groups
37         // are neighbours.
38 
39         // glstate has changed so need to pop old glstate.
40         if (preStateBin._stateset !== undefined) {
41             glstate.popStateSet();
42         }
43         // and push new glstate.
44         if (curStateBin._stateset !== undefined) {
45             glstate.pushStateSet(curStateBin._stateset);
46         }
47         return;
48     }
49 
50     //先彈出狀態,保證preStateBin和curStateBin達到樹節點平級
51     //無法確定兩個樹節點誰的深度值更多,兩個都做一次迴圈
52     while (preStateBin._depth > curStateBin._depth) {
53         if (preStateBin._stateset !== undefined) {
54             glstate.popStateSet();
55         }
56         preStateBin = preStateBin._parent;
57     }
58 
59     // use return path to trace back steps to curStateBin.
60     let stack = [];
61     // need to pop back up to the same depth as the curr glstate group.
62     while (curStateBin._depth > preStateBin._depth) {
63         if (curStateBin._stateset !== undefined) {
64             stack.push(curStateBin._stateset);
65         }
66         curStateBin = curStateBin._parent;
67     }
68 
69     // now pop back up both parent paths until they agree.
70     // should be this to conform with above case where two StateBin
71     // nodes have the same parent
72     //繼續遍歷直到兩個樹節點相同
73     while (preStateBin !== curStateBin) {
74         if (preStateBin._stateset !== undefined) {//pre的從GLState中出棧
75             glstate.popStateSet();
76         }
77         preStateBin = preStateBin._parent;
78 
79         if (curStateBin._stateset !== undefined) {//當前的入棧,臨時儲存
80             stack.push(curStateBin._stateset);
81         }
82         curStateBin = curStateBin._parent;
83     }
84 
85     //遍歷結束後,從臨時棧中推入GLState裡
86     for (let i = stack.length - 1, l = 0; i >= l; --i) {
87         glstate.pushStateSet(stack[i]);
88     }
89 };

這段程式碼我們仔細來看一下。

第一件事比較當前節點狀態和前一個節點狀態,相同則直接返回。

1 if (curStateBin === preStateBin) {//兩個相同什麼都不做
2         return;
3     }

接下來如果前後節點狀態不同,就繼續下面的事情,我們來看下面接下來做了什麼事。接下來是判斷當前遍歷到的狀態節點是否已經是樹的葉子節點,如果是葉子節點就向樹根部遍歷,依次彈出上一級父節點直到遍歷到整棵樹的根節點。彈出是靠glstate這個引數來操作實現的注意一下。遍歷到根節點並彈出狀態後就直接返回了。

 1 if (curStateBin === undefined) {
 2         //curStateBin已經到頂,彈出preStateBin的所有狀態
 3         do {
 4             if (preStateBin._stateset !== undefined) {
 5                 glstate.popStateSet();
 6             }
 7             preStateBin = preStateBin._parent;
 8         } while (preStateBin);
 9         return;
10     }

我們再看看接下來還做了什麼操作,這個看註釋就能理解他的操作。

 1 if (preStateBin === undefined) {
 2         //preStateBin已經到頂,壓入curStateBin的所有狀態
 3         //從子節點往根節點遍歷獲取所有的狀態,但是推給glstate必須從根節點往子節點遍歷
 4         //所以這裡先塞到一個stack裡面,然後再遍歷stack推給glstate
 5         let stack = [];
 6         do {
 7             if (curStateBin._stateset !== undefined) {
 8                 stack.push(curStateBin._stateset);
 9             }
10             curStateBin = curStateBin._parent;
11         } while (curStateBin);
12 
13         let size = stack.length - 1;
14         for (let i = size; i >= 0; --i) {
15             glstate.pushStateSet(stack[i]);
16         }
17         return;
18     } else if (preStateBin._parent === curStateBin._parent) {
19         // first handle the typical case which is two glstate groups
20         // are neighbours.
21 
22         // glstate has changed so need to pop old glstate.
23         if (preStateBin._stateset !== undefined) {
24             glstate.popStateSet();
25         }
26         // and push new glstate.
27         if (curStateBin._stateset !== undefined) {
28             glstate.pushStateSet(curStateBin._stateset);
29         }
30         return;
31     }

隨後我們看看最後的操作。這波操作就是為了比較currStateBin和preStateBin這兩個樹節點的深度和對其向樹根部的操作。

 1 //先彈出狀態,保證preStateBin和curStateBin達到樹節點平級
 2     //無法確定兩個樹節點誰的深度值更多,兩個都做一次迴圈
 3     while (preStateBin._depth > curStateBin._depth) {
 4         if (preStateBin._stateset !== undefined) {
 5             glstate.popStateSet();
 6         }
 7         preStateBin = preStateBin._parent;
 8     }
 9 
10     // use return path to trace back steps to curStateBin.
11     let stack = [];
12     // need to pop back up to the same depth as the curr glstate group.
13     while (curStateBin._depth > preStateBin._depth) {
14         if (curStateBin._stateset !== undefined) {
15             stack.push(curStateBin._stateset);
16         }
17         curStateBin = curStateBin._parent;
18     }
19 
20     // now pop back up both parent paths until they agree.
21     // should be this to conform with above case where two StateBin
22     // nodes have the same parent
23     //繼續遍歷直到兩個樹節點相同
24     while (preStateBin !== curStateBin) {
25         if (preStateBin._stateset !== undefined) {//pre的從GLState中出棧
26             glstate.popStateSet();
27         }
28         preStateBin = preStateBin._parent;
29 
30         if (curStateBin._stateset !== undefined) {//當前的入棧,臨時儲存
31             stack.push(curStateBin._stateset);
32         }
33         curStateBin = curStateBin._parent;
34     }
35 
36     //遍歷結束後,從臨時棧中推入GLState裡
37     for (let i = stack.length - 1, l = 0; i >= l; --i) {
38         glstate.pushStateSet(stack[i]);
39     }

  StateBin渲染狀態樹是對osg的StateSet單個模型渲染狀態的管理資料結構,幾乎在整個DrawActor的過程中都要大量應用,他的重要性不言而喻,鯽魚也是一知半解的在學習這個資料結構,希望大家多提出寶貴的見解,多多斧正,謝謝同學們的支援關注。今天就到這裡,下週再見。本文系原創,引用請註明出處:https://www.cnblogs.com/ccentry/p/10224312.html