Unity 3D 图形学 Shader之顶点与片段着色器(五)
通過實現一個只有顏色屬性可調節的簡單材質效果更好的了解頂點與片段著色器
一、頂點著色器
? ?頂點著色器就是處理頂點的著色器,每個頂點都會執行一次頂點著色器。我們先認識下頂點函數的結構:
此時我們的Shader還不能正常編譯,因為了除了頂點著色器外,還們需要一個片斷著色器。
二、片段著色器:
? ? ? 片斷著色器也被稱作像素著色器,主要是處理最終顯示在屏幕上的像素結果。經過頂點著色器的處理,我們已經得到了最終顯示在屏幕上的頂點矩陣,內部會自動進行插值計算,以獲得當前模型的所有片斷像素,然后每個像素都會執行一次片斷著色器,得到最終每個像素的顏色值。
? ? ? ? 頂點著色器與片斷著色器的執行并不是1:1的,舉個例子,一個三角面片,只有三個頂點,頂點著色器只需執行3次,而片斷著色器由最終的像素數來決定,執行幾百上千都是很正常的。所以從性能的角度來考慮,我們要盡量把計算放在頂點著色器中去執行。其次在片斷著色器中也要盡量的簡化算法,節省開支。
三、整合頂點與片段著色器
Shader "Unlit/MyFirstShader" {Properties{_Color("Color", Color) = (1,1,1,1)}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment fragfixed4 _Color;float4 vert ( float4 vertex : POSITION ) : SV_POSITION{return UnityObjectToClipPos(vertex);}fixed4 frag () : SV_Target{return _Color;}ENDCG}} }? ? ? ? 如上圖所示的代碼中,我們在應用程序階段把模型的頂點位置信息(float4 vertex:POSITION)傳輸到了幾何階段,然后在頂點著色器中利用UnityObjectToClipPos矩陣轉換把模型頂點坐標從本地轉換到齊次裁剪坐標中,并輸出轉換后的坐標(SV_POSITION)到光柵化階段中,最后在光柵化的片元著色器中我們給所有像素都返回了一個顏色值_Color。
? ? ? ?但是仔細思索我們會發現有些局限,如果我想在應用程序階段傳遞多個值呢,除了頂點位置還想傳遞頂點色、UV坐標等信息那要怎么辦呢?同時現在從幾何階段的頂點著色器輸出的只有頂點坐標(SV_POSITION),如果我也想傳遞其它值到片元著色器中又該怎么辦呢?
這個時候就該結構體(structure)出場了。
四、Struct
? ? ?結構體就像是一個組或者說是一個容器,我們可以在其中存放多個變量,然后在各個階段傳遞時我們就傳遞這個結構體就好了,這樣子就把結構體中我們定義的多個變量一同傳遞過去了。
? ? struct具體語法:
于是,我們將原來的代碼利用struct功能改進后如下:
注意,這里的fixed4 _Color;這條語句一定要定義在它用到的函數之前,否會報錯。雖然相較之前的版本會顯示的代碼多了不少,但是這樣做更加靈活方便。
Shader "Unlit/NewUnlitShader" {Properties{_Color("Color", Color) = (1,1,1,1)}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag fixed4 _Color;//POSITION語義是用于頂點著色器,用來指定模型的頂點位置,是在變換前的頂點的本地空間坐標。struct appdata{float4 vertex : POSITION;//模型頂點位置坐標 };//SV_POSITION語義則用于像素著色器,用來標識經過頂點著色器變換之后的頂點坐標。struct v2f{ float4 pos : SV_POSITION; }; //這是頂點著色器,頂點著色器是屬于幾何階段//appdata v 模型的頂點位置信息v2f vert (appdata v){v2f o;//矩陣轉換把模型頂點坐標從本地轉換到齊次裁剪坐標中,說白了就是將模型顯示在二維顯示器上時需要做的一些矩陣轉換o.pos = UnityObjectToClipPos(v.vertex);// return o;//輸出轉換后的頂點坐標(SV_POSITION)到片段著色器中}//這是片段著色器,片段著色器是屬于光柵化階段//SV_Target,是系統值,表示該函數返回的是用于下一個階段輸出的顏色值,也就是我們最終輸出到顯示器上的值。fixed4 frag () : SV_Target{ //直接返回我們在材質面板中定義的顏色,給所有像素都返回了一個顏色值_Colorreturn _Color; }ENDCG}} }效果如下:
4.1 、結構體中使用到多個變量
? 例如最后輸出模型的UV做為顏色信息來顯示:
Shader "Unlit/NewUnlitShader" {Properties{_Color("Color", Color) = (1,1,1,1)}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag fixed4 _Color;//POSITION語義是用于頂點著色器,用來指定模型的頂點位置,是在變換前的頂點的本地空間坐標。struct appdata{float4 vertex : POSITION;//模型頂點位置坐標float2 uv : TEXCOORD0;//添加了UV變量};//SV_POSITION語義則用于像素著色器,用來標識經過頂點著色器變換之后的頂點坐標。struct v2f{ float4 pos : SV_POSITION;float2 uv : TEXCOORD0; //添加了UV變量}; //這是頂點著色器,頂點著色器是屬于幾何階段//appdata v 模型的頂點位置信息v2f vert (appdata v){v2f o;//矩陣轉換把模型頂點坐標從本地轉換到齊次裁剪坐標中,說白了就是將模型顯示在二維顯示器上時需要做的一些矩陣轉換o.pos = UnityObjectToClipPos(v.vertex);//o.uv = v.uv; //添加了UV變量 return o;//輸出轉換后的頂點坐標(SV_POSITION)和模型的UV到片段著色器中}//這是片段著色器,片段著色器是屬于光柵化階段//SV_Target,是系統值,表示該函數返回的是用于下一個階段輸出的顏色值,也就是我們最終輸出到顯示器上的值。//v2f i 頂點著色器返回的模型頂點坐標與模型UVfixed4 frag (v2f i) : SV_Target{ //直接返回我們在材質面板中定義的顏色,給所有像素都返回了一個顏色值_Color//return _Color;return fixed4(i.uv,0,1);//傳過來的模型的UV,作為顏色信息顯示}ENDCG}} } 這里屬性中的_Color沒有使用到,先不管它。自定義函數
有的時候我們可能需要在頂點著色器或者片斷著色器中寫大量的代碼,這樣就會使得代碼不夠整潔,這個時候我們就可以使用自定義函數的方式,將部分代碼整合進去,使其看起來直觀易懂。
比如在上面的基礎上,我們想實現一個利用模型自身UV來產生棋盤格的效果(不用貼圖,而是程序生成的方式生成棋盤格),如下:
關鍵代碼如下:
Shader "Unlit/NewUnlitShader" {Properties{_Color("Color", Color) = (1,1,1,1)}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag fixed4 _Color;//POSITION語義是用于頂點著色器,用來指定模型的頂點位置,是在變換前的頂點的本地空間坐標。struct appdata{float4 vertex : POSITION;//模型頂點位置坐標float2 uv : TEXCOORD0;//添加了UV變量};//SV_POSITION語義則用于像素著色器,用來標識經過頂點著色器變換之后的頂點坐標。struct v2f{ float4 pos : SV_POSITION;float2 uv : TEXCOORD0; //添加了UV變量}; //這是頂點著色器,頂點著色器是屬于幾何階段//vert方法是自動調用的,類似Unity的Update//appdata v 模型的頂點位置信息v2f vert (appdata v){v2f o;//矩陣轉換把模型頂點坐標從本地轉換到齊次裁剪坐標中,說白了就是將模型顯示在二維顯示器上時需要做的一些矩陣轉換o.pos = UnityObjectToClipPos(v.vertex);//o.uv = v.uv; //添加了UV變量 return o;//輸出轉換后的頂點坐標(SV_POSITION)和模型的UV到片段著色器中}//checker是我們自己定義的函數//利用模型自身UV來生成棋盤效果的方法//checker返回的就是一個浮點數,所以你用float或者fixed都可以,//不過你得把checker函數放在frag函數的上面,要不然會報錯,原理是要先定義才能調用。fixed checker(float2 uv) {//UV取值范圍是0-1,repeatUV取值范圍是0-10,是float類型float2 repeatUV = uv*10;// floor(repeatUV)取值結果都變為整數類型//floor(repeatUV)/2 ,小數點后是0或者是0.5兩種情況 float2 c = floor(repeatUV)/2;//floor 對輸入參數向下取整//c.x+c.y的結果的小數點部分可能是0或0.5,//frac取得是小數部分,即取得是0.5或者是0//返回的checker是0.5*2=1或者是0*2=0,即checker等于1或者0float checker = frac(c.x+c.y)*2;//它返回標量或每個矢量中各分量的小數部分return checker;} //這是片段著色器,片段著色器是屬于光柵化階段//SV_Target,是系統值,表示該函數返回的是用于下一個階段輸出的顏色值,也就是我們最終輸出到顯示器上的值。//v2f i 頂點著色器返回的模型頂點坐標與模型UVfixed4 frag (v2f i) : SV_Target{ fixed col = checker(i.uv);//調用函數得到返回值return col;//col應該是一維的?但卻返回了fixed4,這是為什么呢?//因為shader會以這個數值構造一個fixed4類型的返回值,每個分量的值都一樣。//即返回的可能是(0,0,0,0)代表黑色,(1,1,1,1)代表白色}ENDCG}} }checker是我們自己定義的函數,內部就是利用模型自身UV來生成棋盤效果的方法,返回類型為fixed,然后在frag函數中,我們直接調用得到返回值,并最終輸出即可。
注意,這里的checker函數同樣也要在其用到之前進行聲明,和上面的變量聲明是一樣的,這一點與腳本程序代碼是有所不同的,ShaderLab中要先聲明再調用。?補充: 片段著色器中的紋理采樣
(貼紋理的過程)例如下面給Cube貼紋理
如果紋理貼圖大小跟Cube顯示區域不匹配怎么辦?
1. 紋理跟顯示區域相等? ?所有頂點一 一映射
2. 紋理大于顯示區域
第(1)種原則:因為不能一對一的映射?,所以采用等比例映射的原則,可以把圖片劃分成二維坐標系(UV坐標)
第(2)種原則:點擊圖片,看圖片的Filter Mode 屬性(同樣是UV坐標,單位長度相等)
3. 紋理小于顯示區域
如果紋理大小小于顯示區域出現馬賽克或鋸齒對應的解決方案可以把紋理的Filter Mode屬性,選擇Bilinear或Trilinear,可以減少鋸齒。
相關知識借鑒:千鋒視頻資源庫-Java視頻教程海量下載-千鋒教育
總結
以上是生活随笔為你收集整理的Unity 3D 图形学 Shader之顶点与片段着色器(五)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vs2017 开发工具颜色_2017年排
- 下一篇: Linux实训项目——第十一章:基础DN