Iron Scene Structure - Materials

?? Iron Scene Structure - Materials
* Graphics
? * [Materials](https://github.com/armory3d/armory/wiki/materials)
* Code
? * [Find objects in the scene](https://github.com/armory3d/armory/wiki/Find-objects-in-the-scene)
* Engine Development
? * [Render Path](https://github.com/armory3d/armory/wiki/renderpath)
Armory 只提供了三個(gè)設(shè)置材質(zhì)屬性的節(jié)點(diǎn),可以設(shè)置 `RGB`, `Value` 和 `Image Texture` 三種值。官方教程 Material parameters 演示了兩種材質(zhì)屬性修改方法,一是使用邏輯節(jié)點(diǎn),二是使用 Haxe 腳本,這種方式更靈活,可以實(shí)現(xiàn)更多功能。
armory_examples-22.06\material_params\Sources\arm\MyTrait.hx

示例場(chǎng)景中,只含有三個(gè) Cube,它們材質(zhì)設(shè)置如下:
1. Cube.001 設(shè)置了一個(gè)同名的 **RGB** 節(jié)點(diǎn)連接到 Diffuse BSDF 著色器;
2. Cube.002 設(shè)置了一個(gè)同名的 **Image Texture** 節(jié)點(diǎn)連接到 Diffuse BSDF 著色器;
3. Cube.003 設(shè)置了一個(gè)同名的 **Value** 節(jié)點(diǎn)連接到 Checker Texture,再接 Diffuse BSDF 著色器;
除了 Object 屬性可以留空使用 [owner] 對(duì)象,其它都需要提供相應(yīng)的值。材質(zhì)可以使用 `Material` 或者使用 `GetMaterialNode` 節(jié)點(diǎn)獲取。
使用邏輯節(jié)點(diǎn)對(duì)材質(zhì)進(jìn)行編程,這些材質(zhì)設(shè)置的重點(diǎn)是節(jié)點(diǎn)名稱,可以修改節(jié)點(diǎn)名稱,只需要在邏輯編輯器中使節(jié)點(diǎn)中 **Node** 屬性值與材質(zhì)設(shè)置的節(jié)點(diǎn)名稱一致。并且,要在材質(zhì)側(cè)欄面板中激活屬性才能使用:
? ? Logic Node Editor - Properties - Armory Material Node - Parameter
目前支持的材質(zhì)節(jié)點(diǎn)數(shù)量有限,除了以上三個(gè)之外,比如 Checker Texture 等等的程序化紋理就不支持。
示例中,在場(chǎng)景屬性面板附加了一個(gè) `NodeTree` 邏輯節(jié)點(diǎn)樹,和一個(gè) `MyTrait` 腳本擴(kuò)展。邏輯節(jié)點(diǎn)樹使用了鍵盤事件 Keyboard,鼠標(biāo)事件 Mouse,觸屏事件 Touch 等事件節(jié)點(diǎn),然后合并到 Merge 節(jié)點(diǎn),只要任何一個(gè)事件觸發(fā)就支執(zhí)行后續(xù)的材質(zhì)設(shè)置邏輯。
默認(rèn)啟用腳本擴(kuò)展,輪番切換紋理圖像。調(diào)用 `Time.time()` -> `kha.Scheduler.time()` 獲取到引擎當(dāng)前的已運(yùn)行時(shí)間作為變量。本來(lái)預(yù)期示例中 REB 和 Value 兩個(gè)材質(zhì)屬性也應(yīng)該生效,但是在 Links 回調(diào)函數(shù)中并沒(méi)有獲取到相應(yīng)參數(shù)的回調(diào)。而邏輯節(jié)點(diǎn)通過(guò) `UniformsManager` 設(shè)置的材質(zhì)屬性卻可以生效。多功能節(jié)點(diǎn)分組下,還有一個(gè) `Get Application Time` 節(jié)點(diǎn),還可以可以獲取前后兩幀的時(shí)間差:
Iron Uniforms `externalVec3Links` 這些變量是在外部初始化的,具體是 Armory Uniforms 負(fù)責(zé)。Iron Uniforms API 執(zhí)行時(shí)有一個(gè)邏輯,比如說(shuō) `setObjectConstant()` 方法中設(shè)置材質(zhì)顏色,參數(shù)類型條件是 c.type == "vec3",當(dāng)它從其中一個(gè)回調(diào)函數(shù)獲取到了值,那么就會(huì)忽略其它的回調(diào)函數(shù)。
因此,當(dāng)材質(zhì)節(jié)點(diǎn) RGB 可以直接提供數(shù)據(jù),那么 `externalVec3Links` 后續(xù)的回調(diào)函數(shù)就被跳過(guò)了。但是 RGB 節(jié)點(diǎn)本身又不能設(shè)置 null,除非可以將用戶的回調(diào)函數(shù)放到材質(zhì)節(jié)點(diǎn)的數(shù)據(jù)輸出之前。其中一個(gè)方法就是使用 `UniformsManager`。
為了在 Haxe 腳本中實(shí)現(xiàn)顏色的不斷更新隨機(jī)值,以下示例代碼的基礎(chǔ)上,增加了 color 變量,并通過(guò) `UniformsManager` 去注冊(cè)相應(yīng)的著色器常量 Links 函數(shù)。
掌握了通用的邏輯節(jié)點(diǎn)功能后,接下來(lái)就要使用它們來(lái)操作場(chǎng)景的對(duì)象,包括動(dòng)畫、導(dǎo)航、網(wǎng)絡(luò),以及對(duì)象材質(zhì),Trait 擴(kuò)展設(shè)置、物理系統(tǒng)、聲音,Canvas 2D 畫面繪畫,甚至是后期處理,Render Path 深入引擎渲染。
這些操作免不了需要翻開源代碼來(lái)操作,因?yàn)楣俜教峁┑?API 文檔實(shí)是不如直接打開源代碼來(lái)得更有效果,單看 API 文檔,雖然知道它存在目的是做什么,但依然會(huì)出現(xiàn)無(wú)法理解或琢磨出 API 應(yīng)該怎么使用的情況。當(dāng)然,這主要原因是圖形學(xué)知識(shí)點(diǎn)的缺失,通過(guò)代碼閱讀在一定程度上可以彌補(bǔ)。
就如邏輯節(jié)點(diǎn)中,設(shè)置燈光就需要使用 Iron LightObject 對(duì)象,這就涉及光照技術(shù),如果有良好的圖形學(xué)基礎(chǔ),那么很多功能函數(shù)基本上看一眼就知道可以用它來(lái)干什么了。比如說(shuō)以下代碼片段提供了注解,那么在掌握 GLSL 著色器編程基礎(chǔ)的條件下,很快就可以領(lǐng)悟,這些變量會(huì)傳遞到著色器程序中作為 [uniforms]。
比如,燈光對(duì)象 `LightObject` 的構(gòu)建器中可以看到燈光模型數(shù)據(jù)保存在 `LightData` 數(shù)據(jù)屬性,包括顏色、強(qiáng)度、類型等等。其中 **rp_shadowmap** 是一個(gè)編譯命令定義的符號(hào),定義在 khafile.js 腳本,通過(guò)它的前綴也可以知道它是 Render Path 相關(guān)的功能,這是定制游戲渲染引擎的技術(shù)細(xì)節(jié)。需要有相當(dāng)?shù)幕A(chǔ)才能完全掌握這些代碼存在的目的:
又難一點(diǎn)的是設(shè)置對(duì)象的材質(zhì),首先要了解模型由頂點(diǎn)構(gòu)成的面片組成,頂點(diǎn)除了 3D 空間坐標(biāo)之外,還有 UV 紋理映射坐標(biāo),紋理貼圖就是最基本的一種材質(zhì)屬性。面片又可以設(shè)置不同材質(zhì),這就產(chǎn)生了材質(zhì)插槽的概念,Material Slots,一個(gè)模型中可以有多個(gè)插槽,每個(gè)插槽設(shè)置一種材質(zhì),對(duì)應(yīng)應(yīng)于目標(biāo)面片。
給一個(gè)幾何對(duì)象設(shè)置材質(zhì)是相對(duì)容易的,只需要指定 MesshObject 或 DecalObject,再指定材質(zhì)對(duì)象。但是設(shè)置指定對(duì)象的材質(zhì)的參數(shù),操作步驟就顯得有點(diǎn)繁瑣。以下是材質(zhì)相關(guān)的節(jié)點(diǎn)介紹:
1. `MaterialNode` 節(jié)點(diǎn)只有一個(gè)屬性用于選定場(chǎng)景中的材質(zhì),輸出 `MaterialData`;
2. `GetMaterialNode` 節(jié)點(diǎn)輸出的是指定 `MaterialData`,材質(zhì)的數(shù)據(jù)模型;
3. Set Object Material `SetMaterialNode` 節(jié)點(diǎn)由新版本 `SetMaterialSlotNode` 取代。
4. `SetMaterialRgbParamNode` 設(shè)置對(duì)象材質(zhì)的顏色屬性,使用數(shù)據(jù)類型 `iron.math.Vec4`;
5. `SetMaterialValueParamNode` 設(shè)置對(duì)象材質(zhì)的屬性值,使用數(shù)據(jù)類型 `Null<kha.FastFloat>`;
6. `SetMaterialImageParamNode` 設(shè)置對(duì)象材質(zhì)的屬性值,使用數(shù)據(jù)類型 `Null<kha.FastFloat>`;
變更全局材質(zhì)設(shè)置,就修改 **Get Scene Root** 輸出的場(chǎng)景對(duì)象,否則使用節(jié)點(diǎn)對(duì)象列表中的對(duì)象。節(jié)點(diǎn)還有一個(gè)**Per Object**模式選擇,如果激活它,就表示不對(duì)全局修改材質(zhì)設(shè)置,只修改指定對(duì)象。
在設(shè)置參數(shù),`Object` 和 `Material` 分別指要操作的對(duì)象和其材質(zhì)對(duì)應(yīng)的 Solt,但是 `Node` 這個(gè)參數(shù)就讓人難以琢磨,是什么鬼?材質(zhì)編輯器中,可以給幾何體設(shè)置任意的材質(zhì)節(jié)點(diǎn),即對(duì)應(yīng)腳本中材質(zhì)屬性。每個(gè)材質(zhì)節(jié)點(diǎn)的標(biāo)題都會(huì)顯示節(jié)點(diǎn)的名稱,也可以在側(cè)欄面板中編輯和復(fù)制它。在邏輯節(jié)點(diǎn)編輯器中使用材質(zhì)屬性設(shè)置節(jié)點(diǎn)時(shí),就可以使用這個(gè)節(jié)點(diǎn)名稱。在編程中,`Node` 對(duì)應(yīng)的就是 `MaterialData` 屬性名稱。
材質(zhì)屬性設(shè)置都會(huì)使用到 `UniformsManager`,這是一個(gè)著色器 uniforms 常量管理工具,它是相當(dāng)兩種編程語(yǔ)言之間的橋梁,Haxe 代碼中的數(shù)據(jù)通過(guò)它傳遞給 GLSL 著色器中的 uniforms 常量。反過(guò)來(lái),也可以從著色器中獲取相關(guān)的值。暫且稱之為 **GLSL 常量管理器**。
GLSL 常量管理器和 `Uniforms` 類型搭配使用,注意這個(gè)工具類型有兩個(gè)定義。GLSL 常量管理器和 armory.object.Uniforms 和都需要調(diào)用 iron.object.Uniforms 提供的數(shù)據(jù)。后者使得了一系列數(shù)組存儲(chǔ)各種數(shù)據(jù)鏈接關(guān)系,這些靜態(tài)數(shù)組由 armory.object.Uniforms 進(jìn)行初始化以及管理。在這里可以看到 Haxe 的另一種怪異語(yǔ)法,數(shù)組元素是長(zhǎng)長(zhǎng)的映射路徑。另一種怪異的語(yǔ)法是構(gòu)造函數(shù)在只有一條語(yǔ)句的情況下,可以省略花括號(hào),這是一種偷懶語(yǔ)法: HaxeManual/AbstractArrayAccessOrder.hx
在首次遇到這種語(yǔ)法結(jié)構(gòu)時(shí),首選是搜索引擎查資料。但其實(shí)作為一個(gè)小眾編程語(yǔ)言,并沒(méi)有太多實(shí)用的信息,更別說(shuō)涉及到細(xì)節(jié)的 Haxe 教程。所以還是在官方的 Haxe Manual 中找資料,最好使用 MD 源文檔,搜索的效率更高。如果已經(jīng)安裝 [RunSnippet Sublime Text Plugin](https://github.com/jimboyeah/run-snippet),并且在 Sublime 中閱讀此文檔,那么直接將光標(biāo)放在 [Language Features] [Function Bindings] 這些關(guān)鍵字上,按 F9 就可以跳轉(zhuǎn)到主題內(nèi)容中,根據(jù)不用再去搜索。
Haxe 語(yǔ)言中的 Arrow Function 使用 -> 箭頭表示匿名函數(shù),如下所示:
Haxe 數(shù)組聲明中這種連續(xù)的 -> 會(huì)讓人有種誤解,學(xué)習(xí)過(guò)鏈表數(shù)據(jù)結(jié)構(gòu)的人可能直觀地認(rèn)為這就是鏈表結(jié)構(gòu),但其實(shí)它是函數(shù)原型的表達(dá)。以下程序演示了如何使用這種怪異的數(shù)組聲明表達(dá):
所謂 Links,就是一組材質(zhì)處理函數(shù)的參數(shù)到材質(zhì)屬性數(shù)據(jù)的映射關(guān)系。Iron Uniforms 提供材質(zhì)處理函數(shù),`UniformsManager` 或者 Armory Uniforms 提供映射關(guān)系,即 Links 靜態(tài)變量中保存的一組函數(shù),回調(diào)函數(shù)在 `register()` 方法中設(shè)置。并由 Iron Uniforms API 在處理的過(guò)程中進(jìn)行回調(diào),因此**Links** 也可以看作是一組回調(diào)函數(shù),它們用來(lái)向著色器注入數(shù)據(jù)。參考官方示例 [Material shaders]。
以上所描述的關(guān)系可能顯得有點(diǎn)亂,簡(jiǎn)化一下表達(dá)就是:Iron Uniforms 定義了一套材質(zhì)處理函數(shù)的規(guī)范,規(guī)范中提供一個(gè)回調(diào)接口,`UniformsManager` 或者 Armory Uniforms 都可以使用這個(gè)回調(diào)接口,向里面注冊(cè)回調(diào)函數(shù),函數(shù)的入口參數(shù)都一致使用: Object->MaterialData->String,至于返回什么數(shù)據(jù)就看是什么類型的 Links 集合。下表顯示了它們的對(duì)應(yīng)關(guān)系:
Iron Uniforms 有三對(duì)核心方法,它們?cè)诿恳粠紩?huì)被 `RenderPath` 調(diào)用以更新著色器的數(shù)據(jù),并且內(nèi)部還會(huì)對(duì)所有已經(jīng)登記的 Links 函數(shù)進(jìn)行回調(diào)。對(duì)于每一個(gè)材質(zhì)參數(shù),回調(diào)所有類型對(duì)應(yīng)的 Links 函數(shù),需要判斷 link 參數(shù)傳遞的值,是否與目標(biāo)的屬性值一致,然后再作處理。如果不一致,則返回 `null` 告訴引擎不需要理會(huì)當(dāng)前值。如果回調(diào)函數(shù)已經(jīng)返回一個(gè)值,那么同類的其它回調(diào)函數(shù)就會(huì)被跳過(guò)。
Armory 引擎的核心就是 [Render Path],這是一個(gè)可編程的設(shè)計(jì),用戶可以創(chuàng)建自己的渲染路徑,實(shí)現(xiàn)一個(gè) `RenderPathCreator` 類并將代碼放置到項(xiàng)目下的目錄中:
? ? Libraries\lib_name\Sources\celshade\renderpath\RenderPathCreator.hx
? ? Sources\arm\renderpath\RenderPathCreator.hx
關(guān)于驅(qū)動(dòng)接口可以參考:armsdk\armory\blender\arm\api.py
雖然 API 文檔中對(duì)材質(zhì)節(jié)點(diǎn)中的 Node 參數(shù)有說(shuō)明,但依然讓人有一種“材質(zhì)內(nèi)部有什么節(jié)點(diǎn)?”的疑惑。
? ? @input Node: Name of the parameter.
在設(shè)置參數(shù),`Object` 和 `Material` 分別指要操作的對(duì)象和其材質(zhì)對(duì)應(yīng)的 Solt,但是 `Node` 這個(gè)參數(shù)就讓人難以琢磨,是什么鬼?材質(zhì)編輯器中,可以給幾何體設(shè)置任意的材質(zhì)節(jié)點(diǎn),即對(duì)應(yīng)腳本中材質(zhì)屬性。每個(gè)材質(zhì)節(jié)點(diǎn)的標(biāo)題都會(huì)顯示節(jié)點(diǎn)的名稱,也可以在側(cè)欄面板中編輯和復(fù)制它。在邏輯節(jié)點(diǎn)編輯器中使用材質(zhì)屬性設(shè)置節(jié)點(diǎn)時(shí),就可以使用這個(gè)節(jié)點(diǎn)名稱。在編程中,`Node` 對(duì)應(yīng)的就是 `MaterialData` 屬性名稱。
這個(gè)參數(shù)是一個(gè)字符串值,會(huì)經(jīng)由 `UniformsManager` API 的 link 參數(shù)傳入,用于讀寫材質(zhì)
的相應(yīng)屬性數(shù)據(jù)。所以,材質(zhì)節(jié)點(diǎn)中的 **Node** 這個(gè)字符串參數(shù)就是 `MaterialData` 的屬性名稱。如果沒(méi)有理解這層關(guān)系,那么根本無(wú)法理解材質(zhì)屬性設(shè)置節(jié)點(diǎn)的使用。
GLSL 常量管理器中管理三種著色器常量,登記在相應(yīng)的多級(jí) Map 數(shù)據(jù)容器內(nèi),對(duì)象作為一級(jí)映射的 Key,材質(zhì)數(shù)據(jù)對(duì)象 `MaterialData` 作為二級(jí)映射的 Key,使用字符串作為第三級(jí)映射的 Key。這三種數(shù)據(jù)對(duì)應(yīng)有三個(gè)回調(diào)方法,它們由 Iron Uniforms 類型進(jìn)行回調(diào),以獲取相應(yīng)的材質(zhì)屬性數(shù)據(jù):
材質(zhì)節(jié)點(diǎn) Python 代碼片段參考:
另外,還需要掌握 `MeshObject` 對(duì)象的組織結(jié)構(gòu),materials 屬性就是材質(zhì)插槽,就是一個(gè)向量列表。向量列表中保存的 `MaterialData` 就是材質(zhì)的數(shù)據(jù)模型。`DecalObject` 裝飾器對(duì)象用于表面修飾,也使用到材質(zhì)的數(shù)據(jù)模型,但只有一個(gè)材質(zhì)。前綴 T 開頭命名的數(shù)據(jù)類型定義多數(shù)都在 SceneFormat.hx,這是 Iron 的場(chǎng)景文件 .arm 格式定義。也就是說(shuō),了解 Iron 對(duì)象的層次結(jié)構(gòu),還必須對(duì) .arm 場(chǎng)景文件格式有一定了解:
以材質(zhì)顏色、紋理屬性設(shè)置節(jié)點(diǎn)為例,`SetMaterialRgbParamNode`和`SetMaterialImageParamNode` 分別調(diào)用,`setVec3Value()` 和 `setTextureValue()` 方法將參數(shù)依 Links 映射集合聲明的?Object->MaterialData->String 順序傳入 `UniformsManager`,由它逐級(jí)取得材質(zhì)參數(shù)的屬性引用,并且將設(shè)置值存放到相應(yīng)的映射容器中。
比如紋理賦值方法中的 **entry** 就是一個(gè) `Map<String, kha.Image>` 容器,是材質(zhì)屬性映射關(guān)系。Iron Uniforms `setObjectConstants()` 方法處理這些紋理數(shù)據(jù),下表顯示了它們的對(duì)應(yīng)關(guān)系: