日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Unity 3D 图形学 Shader之顶点与片段着色器(五)

發布時間:2024/1/8 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unity 3D 图形学 Shader之顶点与片段着色器(五) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

通過實現一個只有顏色屬性可調節的簡單材質效果更好的了解頂點與片段著色器

一、頂點著色器

? ?頂點著色器就是處理頂點的著色器,每個頂點都會執行一次頂點著色器。我們先認識下頂點函數的結構:

  • 頂點著色器函數的名稱,在上面我們已經指定了頂點著色器的名稱就是vert,所以這里我們必須要用vert作為名稱。
  • 其中float4 vertex是我們自己定義的一個四維向量,名字叫vertex(名字我們可以隨便起),僅僅定義一個四維向量并不能使它擁有我們模型的頂點信息,所以這里我們需要為它指定一個語義——POSITION,POSITION就是代表著模型的頂點位置信息。此時變量vertex就表示著我們模型的頂點位置。
  • 在頂色著色器中最主要的事情就是將頂點從模型坐標轉換到裁剪坐標(說白了就是將模型顯示在二維顯示器上時需要做的一些矩陣轉換)。不會矩陣轉換怎么辦,沒關系,Unity已經為我們準備好現成的命令了,只需調用UnityObjectToClipPos即可,后面括號中加上我們的頂點位置變量就可以了。
  • 然后呢,在后面片斷著色器中我們需要頂點著色器中的輸出結果,所以3中需要加上return來將轉換后的頂點返回,float4就是用來定義我們返回的是四維向量。
  • 經過變換后返回的頂點位置,我們也需要利用語義來標記一下,以便片斷著色器可以知道哪個是從頂點著色器輸出過來的頂點位置信息。所以我們在函數的后面加上: SV_POSITION。
  • 簡單地說POSITION語義是用于頂點著色器,用來指定模型的頂點位置,是在變換前的頂點的本地空間坐標。SV_POSITION語義則用于像素著色器,用來標識經過頂點著色器變換之后的頂點坐標。 關于語義,我們在補充下以下幾種,根據這些語義頂點著色器就可以知道誰是模型的頂點數據誰又是模型的法線數據 struct appdata{float4 vertex : POSITION; //頂點float4 tangent : TANGENT; //切線float3 normal : NORMAL; //法線float4 texcoord : TEXCOORD0; //UV1float4 texcoord1 : TEXCOORD1; //UV2float4 texcoord2 : TEXCOORD2; //UV3float4 texcoord3 : TEXCOORD3; //UV4fixed4 color : COLOR; //頂點色}; 在頂點著色器中處理頂點時,我們首先需要獲取到模型的頂點數據(比如頂點位置、法線信息、頂點顏色等等),那么這些數據都是直接存儲在模型中的,我們在Shader中只需要通過標識語義就可以自動獲得。

    此時我們的Shader還不能正常編譯,因為了除了頂點著色器外,還們需要一個片斷著色器。

    二、片段著色器:

    ? ? ? 片斷著色器也被稱作像素著色器,主要是處理最終顯示在屏幕上的像素結果。經過頂點著色器的處理,我們已經得到了最終顯示在屏幕上的頂點矩陣,內部會自動進行插值計算,以獲得當前模型的所有片斷像素,然后每個像素都會執行一次片斷著色器,得到最終每個像素的顏色值。

  • 片斷著色器的函數名,其中()中是空的,因為在這個簡單的示例中我們并不需要額外的數據傳過來,所以暫時為空。
  • 在Cg/HLSL中使用Properties中的變量前還需要在Cg/HLSL中再重新聲明一次,名稱要求一致。這是死規則,我們只能按照要求來執行。float、half、fixed,這三都是浮點數的表示,只是分別對應的精度不一樣,主要用此可以進行更進一步的優化。
  • 去們直接返回_Color,也就是直接返回我們在材質面板中定義的顏色,這也是我們這個小例子想要的效果。
  • 同樣,返回的值是個四維向量,我們用float4來表示,如果想優化的話就用fixed4來表示,關于精度問題我們后面會專門講解,這里不是重點。
  • SV_TARGET是系統值,表示該函數返回的是用于下一個階段輸出的顏色值,也就是我們最終輸出到顯示器上的值。
  • ? ? ? ? 頂點著色器與片斷著色器的執行并不是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 開始,然后緊跟結構體的名字,結構體范圍由{}定義,最后以分號結尾。
  • 使用“.”引用結構體中的成員變量和成員函數。
  • 于是,我們將原來的代碼利用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之顶点与片段着色器(五)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。