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