【Unity Shaders】使用CgInclude讓你的Shader模塊化――創建CgInclude文件存儲光照模型
來源:程序員人生 發布時間:2014-10-11 08:00:01 閱讀次數:3363次
本系列主要參考《Unity Shaders and Effects Cookbook》一書(感謝原書作者),同時會加上一點個人理解或拓展。
這里是本書所有的插圖。這里是本書所需的代碼和資源(當然你也可以從官網下載)。
========================================== 分割線 ==========================================
寫在前面
了解內置的CgInclude文件當然很好,但是如果我們想要創建自己的CgInclude文件來存儲光照模型和輔助函數又該怎么辦呢?
好消息是我們的確可以創建自己的CgInclude文件,壞消息是我們需要再了解一點代碼語法。好啦,那就開始吧!
準備工作
好消息是這次的準備工作終于有點不同了。。。壞消息是我不能再復制粘貼了。。。
- 首先,創建一個新的文本文件,例如MyCgInclude.txt。
- 然后,把文件后綴改為.cginc。當然,操作系統一般會給你一些警示信息,說這個文件將變得不可用,但相信我,我們這個是可用的。
- 將新的.cginc文件導入到我們的Unity項目中(注意,在我的項目里,它的位置在一個新的名為CgIncludes的文件夾下)。等編譯完成后,我們可以看到Unity把該文件當成一個CgInclude文件編譯好了。像下面這樣:

現在,我們已經做好準備可以創建自定義的CgInclude代碼啦。雙擊CgInclude文件,在MonoDevelop中打開它吧~
實現
打開CgInclude文件后,開始鍵入如下代碼。
- 首先,使用下面的預處理指令開始我們的CgInclude文件。這些聲明和#pragma、#include類似,在這里,我們想要去定義一個新的代碼集合,只要我們的Surface Shader在它的編譯指令里面包含了這個文件,這些代碼就可以執行了。在CgInclude文件的最開始鍵入如下代碼:
#ifndef MY_CG_INCLUDE
#define MY_CG_INCLUDE
- 然后,我們必須確保#ifndef或者#ifdef要有一個#endif來結束定義檢查。就和一個if語句需要兩個花括號一樣。在#define指令下面鍵入如下代碼:
#endif
- 接下來,我們就可以填充剩余部分了。鍵入如下代碼:
// Custom Build-in Variables
fixed4 _MyColor;
// Lighting models
inline fixed4 LightingHalfLambert (SurfaceOutput s, fixed3 lightDir, fixed atten) {
fixed diff = max (0, dot (s.Normal, lightDir));
diff = (diff + 0.5) * 0.5;
fixed4 c;
c.rgb = s.Albedo * _LightColor0.rgb * ((diff * _MyColor.rgb) * atten * 2);
c.a = s.Alpha;
return c;
}
- 下面是完整的MyCgInlcude.cginc文件:
#ifndef MY_CG_INCLUDE
#define MY_CG_INCLUDE
// Custom Build-in Variables
fixed4 _MyColor;
// Lighting models
inline fixed4 LightingHalfLambert (SurfaceOutput s, fixed3 lightDir, fixed atten) {
fixed diff = max (0, dot (s.Normal, lightDir));
diff = (diff + 0.5) * 0.5;
fixed4 c;
c.rgb = s.Albedo * _LightColor0.rgb * ((diff * _MyColor.rgb) * atten * 2);
c.a = s.Alpha;
return c;
}
#endif
上面相當于一個頭文件,但想要完整利用它還需要一些其他的步驟。我們需要告訴當前的Shader,我們想要使用自己的文件和代碼。
- 返回上一節所用的Shader。我們需要在塊中包含我們自己的CgInclude文件,就像C++中需要在開頭添加頭文件引用一樣。同時,之前我們的Shader使用內置的Lambert光照模型,但現在我們想要使用自定義的Half Lambert光照模型。因為我們已經包含了該CgInclude文件,我們可以直接在#pragma指令中指明這一模型:
CGPROGRAM
#include "../CgIncludes/MyCgInclude.cginc"
#pragma surface surf HalfLambert
解釋:這里需要指明.cginc文件的相對與該Shader的路徑。也就是說,如果它和Shader放在同一個文件夾下,那么直接寫名稱即可。但在我的項目中,Shader放在了Shaders文件夾下,而.cginc放在了CgIncludes文件夾下,因此需要上述寫法。
- 最后,還記得我們在CgInclude文件中聲明了一個_MyColor變量嗎?我們還需要在Shader的Properties中添加該屬性:
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_DesatValue ("Desaturate", Range(0, 1)) = 0.5
_MyColor ("My Color", Color) = (1, 1, 1, 1)
}
最后,返回Unity。如果出現編譯錯誤,說找不到.cginc文件,那么就是你的位置寫的有問題,重新看上面的解釋更改一下就可以嘍。
最后的結果如下所示。注意到,這里Unity已經使用了我們新的Half Lambert光照模型(和原來相比,就是提亮了背光面的亮度),并且添加了一個新的樣色樣本。左側為上一篇結果,右側為本篇結果。
解釋
當編寫Shader的時候,我們可以像使用C++中的頭文件一樣,使用#include預處理指令來包含其他代碼集合。這告訴Unity我們想要當前的Shader使用包含的這些文件中的代碼。我們這樣做實際上是在相應位置包含了Cg代碼片段。
一旦我們聲明了#include指令,Unity就可以在項目中找到該文件,然后Unity會在文件中查找定義的代碼片段。也就是指,我們使用#ifndef指令和#ifndef指令的地方。當我們聲明#ifndef指令時,我們就是在告訴Unity,如果沒有定義這個名字,那么就使用這個名字去定義一些東西!在本節中,我們是想要去#define MY_CG_INCLUDE。因此,如果Unity沒有找到一個名為MY_CG_INCLUDE的定義,它就會在編譯該CgInclude文件時創建它。而#ifndef就是告訴Unity,這是該定義在這里結束啦,下面的不用再找啦!
現在,你看到了自定義的CgInclude文件是多么強大(和C++中的頭文件類似),我們可以使用它們來存儲所有的自定義光照模型,以減少代碼的重復。其他好處,像靈活性等,你可以聯想C++頭文件來得出啦~
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈