3D引擎資料結構與glTF(2): Scene Graph
圖形學中的 Scene Graph
Scene Graph 中文常翻譯為“場景圖”,是一種常用的場景物件組織方式。我們把場景中的物件,按照一定的規則(通常是空間關係)組織成一棵樹,樹上的每個節點代表場景中的一個物件。每個節點都可以有零到多個子節點,但只有一個父節點。 每個節點都包含一個“空間的定義”,通過一個 4x4 的矩陣表示,也可以通過位置、旋轉、縮放三個分量來表示,但最終都要轉換成4x4的矩陣。
每個節點所定義的空間就叫做“Local Space”,每個節點的空間都是定義在父節點的 Local Space 之中的。在渲染某個節點中的 Mesh 模型的時候,需要計算 Model Matrix,也就是需要把頂點從 Local Space 轉換到 World Space 的矩陣,這時候就要使用矩陣乘法來計算:
nodeA.modelMatrix = nodeA.parent.modelMatrix * nodeA.localMatrix
請注意這裡請求了父節點的 Model Matrix:”nodeA.parent.modelMatrix”,也就是遞迴的去計算,直到 Scene Graph 的根節點。通常在程式碼實現中,會把這個 Model Matrix 進行 Cache 。
Scene Graph 的節點層次結構組成的空間關係就像我們高中物理學的相對運動。例如你在一列飛馳的火車上行走,那麼你的位置、運動都是在“火車”這個空間中的;而你最終在地球上的運動,要根據當前火車的運動來計算決定。
遊戲引擎中的 Scene Graph
Scene Graph 的概念在遊戲引擎中也被普遍使用,先來看一下 Unity3D引擎吧。在 Unity Editor 中我們可以直觀的從 Hierarchy 檢視中看到整個 Scene Graph 結構。當你移動一個 GameObject 時,它下面的所有子節點也會跟隨它一起移動。 從程式碼的角度看,場景節點中的父子關係並不由 GameObject 負責管理,而是由 Transform 元件去完成。 想要指定或者改變物件的父子關係就需要呼叫 Transform.SetParent(),這個函式的第二個引數為“bool worldPositionStays”,也就是在改變父節點時保持物件在世界空間中的位置不變。你可以思考一下,如果要你實現這個API,你怎麼寫?
在 Unreal Engine 4 中也有 Scene Graph 概念的實現,情況與 Unity3D 非常類似。Unreal Editor中的“World Outliner”檢視,也直觀的展現了當前場景的層次結構。 AActor 在場景中的層次結構是由 USceneComponent 組成父子關係來實現的,USceneComponent 起到管理 transform 和 transform 的層次結構的作用。
glTF 定義的 Scene Graph 資料
在glTF標準中也使用 Scene Graph 的概念。一個 glTF 資料可以包含零到多個 scene ,每個 scene 都是由 node 組成的一個樹形層次結構。
在前面一篇文章的glTF簡介中,我們講到了 glTF 的核心資料是由一個 JSON 格式的文字檔案定義的。下面我們就先看一下 glTF 中場景的定義:
{
"scenes": [
{
"name": "defaultScene",
"nodes": [0]
}
],
"scene": 0,
"nodes": [
{
"name": "root",
"children": [1]
},
{
"name": "box",
"mesh": 0
}
],
}
上面這段 JSON 資料是glTF檔案的一部分,它定義了一個 Object ,它有三個重要的欄位:
* scenes:是一個物件陣列,定義這個 glTF 資料中有幾個場景。上面這段 JSON 資料中只包含一個場景。每個場景是一個物件,包含兩個欄位:
* name:字串,場景的名稱;
* nodes:一個數組,通常只有一個元素,它指定這個場景的根節點;陣列的每個元素是下面的 nodes 陣列的索引;
* scene:這個欄位指定預設渲染哪個場景,是指定的上面的 scenes 陣列的 index;
* nodes:是一個物件陣列,包含所有的節點資料;上面這段 JOSN 資料中一共有兩個節點:root 和 box 。
其中,nodes 陣列是構成 Scene Graph 的核心資料,下面我們再看一下 nodes 中的單個節點的資料構成。一個節點可以擁有以下的可選欄位:
* name:名稱,字串;
* children:是一個數組,定義當前這個 node 包含哪些子節點;使用 nodes 陣列的 index;
* transform資料,可以是一個 “matrix” 欄位,定義一個4x4的矩陣;也可以由“translation”、“rotation”、“scale ”三個分量來定義。
* mesh:指定當前節點引用哪個 mesh 物件來渲染;通過 index 索引 glTF 資料中的 mesh 物件;後續文章還會細講;
* camera:指定當前節點引用哪個攝像機;通過 index 索引 glTF 資料中的 camera 物件;後續文章還會細講;
理解了 Scene Graph 的概念之後,再看上述的 glTF 場景資料定義就很直觀了。看到這兒,場景的資料結構應該清晰了,但是目前這個場景還沒包含任何實際的功能,這個我們要從下一篇文章開始講起。
閱讀原文 可以下載完整的glTF 樣例資料。
微信掃描關注公眾號: