一、一些基本感念
1.1 Shader和Material的基本概念
Shader(著色器)實際上就是一小段程序,它負責將輸入的Mesh(網格)以指定的方式和輸入的貼圖或者顏色等組合作用,然后輸出。繪圖單元可以依據這個輸出來將圖像繪制到屏幕上。輸入的貼圖或者顏色等,加上對應的Shader,以及對Shader的特定的參數設置,將這些內容(Shader及輸入參數)打包存儲在一起,得到的就是一個Material(材質)。之后,我們便可以將材質賦予合適的renderer(渲染器)來進行渲染(輸出)了。
Shader只是一段規定好輸入(顏色,貼圖等)和輸出(渲染器能夠讀懂的點和顏色的對應關系)的程序。而Shader開發者要做的就是根據輸入,進行計算變換,產生輸出而已。
二、Unity中Shader的三種基本類型
計算機圖形學中的渲染管線一般可以分為兩種類型:
1.固定功能渲染管線(fixed-functionrendering pipelines)
2.可編程渲染管線(programmablerendering pipelines)
Unity中Shader分為三種基本類型:
1.固定功能著色器(Fixed Function Shader)
2.表面著色器(Surface Shader)
3.頂點著色器&片段著色器(Vertex Shader & Fragment Shader)
固定功能著色器便是我們所說的固定功能渲染管線(fixed-functionrendering pipelines)的具體表現,而表面著色器、頂點著色器以及片段著色器便屬于可編程渲染管線。
2.1 固定功能著色器
Unity為Shader的書寫自帶的一層殼
2.2 表面著色器
Unity自己發揚光大的一項使Shader的書寫門檻降低和更易用的技術
2.3 頂點著色器和片段著色器
頂點著色器:產生紋理坐標,顏色,點大小,霧坐標,然后把它們傳遞給裁剪階段。
片段著色器:進行紋理查找,決定什么時候執行紋理查找,是否進行紋理查找,及把什么作為紋理坐標
2.4 區分Shader類型
沒有嵌套CG語言,也就是代碼段中沒有CGPROGARAM和ENDCG關鍵字的,就是固定功能著色器。
嵌套了CG語言,代碼段中有surf函數的,就是表面著色器。
嵌套了CG語言,代碼段中有#pragma vertex name和 #pragma fragment frag聲明的,就是頂點著色器&片段著色器。
三、 Shader賦給Material的方法
1.拖拽
2.在Material的Inspector面板中選擇
四、Shader的基本框架
Unity中Shader整體的框架寫法可以用如下的形式來概括:
Shader "name" { [Properties] SubShaders[Fallback] }
ps:所有用于這個著色器的代碼必須放置在之后的大括號中:{ }(稱為“塊”)。該名字應該是短且描述性的文字。它并不需要和shader文件名相同。而想要把著色器加入到Unity的子菜單里,名字需要用斜線(/)。
首先是一些屬性定義,用來指定這段代碼將有哪些輸入。接下來是一個或者多個的子著色器,在實際運行中,哪一個子著色器被使用是由運行的平臺所決定的。子著色器是代碼的主體,每一個子著色器中包含一個或者多個的Pass。在計算著色時,平臺先選擇最優先可以使用的著色器,然后依次運行其中的Pass,然后得到輸出的結果。最后指定一個Fallback,即備胎,用來處理所有SubShader都不能運行的情況。
ps:在實際進行表面著色器的開發時,我們就是直接在SubShader這個層次上寫代碼,系統會將把我們的代碼編譯成若干個合適的Pass。
ps:SubShader在UnityShader的代碼段中必須有且至少有一個,而properties和fallback對于追求簡單的Shader,是可以不寫出來的。而復雜一點的Shader,當然各種properties、fallback什么的肯定都有,甚至有多個SubShader,而每個SubShader中又有多個Pass。
五、Properties屬性相關
properties一般定義在著色器的起始部分,我們可以在Shader書寫的時候定義多種多樣的屬性,而使用Shader的時候可以直接在材質檢視面板(Material Inspector)里編輯這些屬性,取不同的值或者紋理。
ps:Properties塊內的語法都是單行的。每個屬性都是由內部名稱開始,后面括號中是顯示在檢視面板(Inspector)中的名字和該屬性的類型。等號后邊跟的是默認值。
5.1 Properties屬性相關代碼列舉
Properties { Property [Property ...] }
定義屬性塊,其中可包含多個屬性,其定義如下:
name ("display name", Range (min, max)) =number
定義浮點數屬性,在檢視器中可通過一個標注最大最小值的滑條來修改。
name ("display name", Color) =(number,number,number,number)
定義顏色屬性
name ("display name", 2D) = "name" {options }
定義2D紋理屬性
name ("display name", Rect) = "name"{ options }
定義長方形(非2次方)紋理屬性
name ("display name", Cube) = "name"{ options }
定義立方貼圖紋理屬性
name ("display name", Float) = number
定義浮點數屬性
name ("display name", Vector) =(number,number,number,number)
定義一個四元素的容器(相當于Vector4)屬性
5.2 一些細節
包含在著色器中的每一個屬性通過name索引(在Unity中, 通常使用下劃線來開始一個著色器屬性的名字)。屬性會將display name顯示在材質檢視器中,還可以通過在等符號后為每個屬性提供缺省值。
對于Range和Float類型的屬性只能是單精度值。
對于Color和Vector類型的屬性將包含4個由括號圍住的數描述。
對于紋理(2D, Rect, Cube) 缺省值既可以是一個空字符串也可以是某個內置的缺省紋理:"white", "black", "gray" or"bump"
隨后在著色器中,屬性值通過[name]來訪問。
5.3 紋理屬性選項
name ("display name", 2D) ="name" { options }
包含在紋理屬性的大括號中的選項Options是可選的。可能的選項有如下:
TexGen紋理生成類型。即紋理的自動生成紋理坐標時的模式,可以是ObjectLinear, EyeLinear,SphereMap, CubeReflect, CubeNormal的其中之一;這些模式和OpenGL紋理生成模式相對應。注意如果使用自定義頂點程序,那么紋理生成將被忽略。
LightmapMode 光照貼圖模式。如果我們給出這個選項,紋理將能被渲染器的光線貼圖屬性所影響。紋理不能被使用在材質中,而是取自渲染器的設定。這個我們以后會講到。
六、光照、材質與顏色
燈光和材質參數常常被用來控制內置的頂點光照。而Unity中的頂點光照也就是Direct3D/OpenGL標準的按每頂點計算的光照模型—— 光照打開時,光照受材質塊,顏色材質和平行高光命令的影響。
6.1 用于通道Pass中的代碼
這些代碼一般是寫在Pass{ }中的,細節如下:
Color Color
設定對象的純色。顏色即可以是括號中的四值(RGBA),也可以是被方框包圍的顏色屬性名。
Material { Material Block }
材質塊被用于定義對象的材質屬性。
Lighting On | Off
開啟光照,也就是定義材質塊中的設定是否有效。想要有效的話必須使用Lighting On命令開啟光照,而顏色則通過Color命令直接給出。
SeparateSpecular On | Off
開啟獨立鏡面反射。這個命令會添加高光光照到著色器通道的末尾,因此貼圖對高光沒有影響。只在光照開啟時有效。
ColorMaterial AmbientAndDiffuse | Emission
使用每頂點的顏色替代材質中的顏色集。AmbientAndDiffuse 替代材質的陰影光和漫反射值;Emission 替代 材質中的光發射值。
6.2 材質塊Material Block相關代碼
使用的地方是在SubShader中的一個Pass{ }中新開一個Material{ }塊,在這個Material{ }塊中進行這些語句的書寫。這些代碼包含了包含材質如何和光線產生作用的一些設置。這些屬性默認為值都被設定為黑色(也就是說不產生作用),一般情況下可以被忽略。
Diffuse Color(R,G,B,A)
漫反射顏色構成。這是對象的基本顏色。
Ambient Color(R,G,B,A)
環境色顏色構成.這是當對象被RenderSettings.中設定的環境色所照射時對象所表現的顏色。
Specular Color(R,G,B,A)
對象反射高光的顏色。(R,G,B,A)四個分量分別代表紅綠藍和Alpha,取值為0到1之間。
Shininess Number
加亮時的光澤度,在0和1之間。0的時候你會發現更大的高亮也看起來像漫反射光照,1的時候你會獲得一個細微的亮斑。
Emission Color
自發光顏色,也就是當不被任何光照所照到時,對象的顏色。(R,G,B,A)四個分量分別代表紅綠藍和Alpha,取值為0到1之間。
打在對象上的完整光照顏色最終是:
FinalColor=Ambient * RenderSettings ambientsetting + (Light Color * Diffuse + Light Color Specular) + Emission
最終顏色=環境光反射顏色 渲染設置環境設置 (燈光顏色漫反射顏色+燈光顏色*鏡面反射顏色)+自發光
ps:方程式的燈光部分(也就是帶括號的部分)對所有打在對象上的光線都是重復使用的。而我們在寫Shader的時候常常會將漫反射和環境光光保持一致(所有內置Unity著色器都是如此)
舉個栗子:
Shader "demo/Shader"
{
//properties
Properties
{
_Color ("主顏色", Color) = (2,2,1,0)
_SpecColor ("高光顏色", Color) = (2,2,2,1)
_Emission ("自發光顏色", Color) = (0,0,0,0)
_Shininess ("光澤度", Range (0.01, 1)) = 0.7
_MainTex ("基本紋理", 2D) = "white" {}
}
//subshader
SubShader
{
//pass
Pass
{
//material
Material
{
//可調節的漫反射光和環境光反射顏色
Diffuse [_Color]
Ambient [_Color]
//光澤度
Shininess [_Shininess]
//高光顏色
Specular [_SpecColor]
//自發光顏色
Emission [_Emission]
}
//開啟光照
Lighting On
//開啟獨立鏡面反射
SeparateSpecular On
//設置紋理并進行紋理混合
SetTexture [_MainTex]
{
Combine texture * primary DOUBLE, texture * primary
}
}
}
}
產物如下