ETH官方钱包

切換
舊版
前往
大廳
主題

【學習筆記 #1】從0開始的Unity Shader學習

%%鼠 拒收病婿 | 2020-04-28 00:29:57 | 巴幣 20 | 人氣 2634

開始自學Shader,前一兩個月前讀的進度經過一點時間就完全忘記了
用自己的大腦去解讀Unity Shader就好像付費解鎖冰原DLC一樣,完全不同的世界。
姻緣下找到不錯的學習教材,開始有系統的學習,順便幫自己做個筆記,也許能幫到你。

先聲明自己的用詞都極為不專業,也許會用錯字或觀念誤導什麼的,還請包涵和指點。
(建議用網頁版看,手機格式會跑掉)


unlit的基本架構
Shader "Unlit/RE0Practice"
{
    Properties{}
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex v
            #pragma fragment f

            fixed4 v():SV_POSITION{
                return 0;
            }
            fixed4 f():SV_TARGET{
                return fixed4(1,1,1,1);
            }
            ENDCG
        }
    }
}

  • Shader "shader名稱"
  • Properties :宣告可以從外部輸入的變數的地方
  • SubShader: 一個shader可以有多個Subshader,用處大概就是A平臺主機只支援Subshader A,而B平臺主機只支援Subshader B,兩個SubShader就可以寫在同一個腳本裡,主機會自動去偵測支援哪個管道。
  • Pass: 寫我們要對這Shader做哪些處理的方法,一個Subshader可以有多個pass,但太多會降低效能
上面程式碼還有一些藍藍的字沒講解,別擔心!! 我第一次看也很怕,但其實很單純,我先一層一層講下來:

Properties:
變數型態有
  • Int
  • Float
  • Range
  • Color
  • Vector
  • Cube
  • 2D
  • 3D
宣告方式:
變數名稱 ("inspector顯示名稱", 變數型態) =初始值
亂打的範例:
Properties{
        myInt ("MYInt",int)=2
        v ("v",Vector)=(1,2,3,1)
        c ("c",Cube)=""{}
        d ("d",2D)="white"{}
        r ("myRange",Range(0,5))=2.5
    }

*注意: Vector型態一定是四維的
面板長這樣:


Subshader:
除了上面講的功能,他還有一些東西可以設定:
Cull  Back | Front | Off  剔除選擇的面
ZTest 沒用過
ZWrite On | Off  要不要寫深度
Blend 混和模式

Tags有好幾個可以查,但以新手來說,我只用過來設定透明材質
亂打得範例:
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha
        Cull off
        ZWrite On
        LOD 100

Pass:
先用CGPROGRAM 和 ENDCG包起來,宣告這是CG語法。
接下來一個pass基本要輸出2個東西! 就是 vertex和fragment 才能正常顯示在畫面上。

要用#pragma 來宣告接下來用來輸出的函數名稱,
以這個程式碼為例:
            CGPROGRAM
            #pragma vertex v
            #pragma fragment f

            fixed4 v():SV_POSITION{
                return 0;
            }
            fixed4 f():SV_TARGET{
                return fixed4(1,1,1,1);
            }
            ENDCG

格式:
#pragma vertex 函數名稱
#pragma fragment 函數名稱
然後實做 v()和 f(),輸出值的型態可以改,目前先以最簡單的fixed4為例子。

" : "右邊是輸出的目標,SV_POSITION跟POSITION在大部分的平臺都是相同的,但保守起見,輸出的目標都是SV開頭的那個比較好。
所以整段程式碼的意思是:
有個函數v負責放置vertex的位置,有個函數f負責給面上白色 (1,1,1,1)
因為點都放在0,所以看不到任何東西,但恭喜! 寫出了第一個可執行的shader

再改一下:
           fixed4 v(float4 vp:POSITION):SV_POSITION{
                return UnityObjectToClipPos(vp);
            }
            fixed4 f():SV_TARGET{
                return fixed4(1,1,1,1);
            }

給v函數 輸入物件的位置資訊vp,用UnityObjectToClipPos轉成世界座標的位置顯示。

一開始我很困惑到底是從哪裡輸入資料進來的,但好像是Unity 的MeshRender會負責這部分。

結果:


自訂型態:
剛開一個shader檔案裡面預設有a2v和v2f的資料型態,一開始真的會嚇到不知道怎麼改才好,但其實就算不用那些也能操作,但就是比較整齊的感覺吧

如果我程式碼改成:
            struct myData{
                float4 v:POSITION; //模型頂點座標
                float3 normal: NORMAL; //法線
                float4 texcoord:TEXCOORD0; //貼圖座標
            };
            struct myOutPut{
                float4 pos :SV_POSITION;
                fixed3 col : COLOR0;
            };

            myOutPut v(myData vp){
                myOutPut o;
                //vp.v+=0.5; 修改傳入的座標
                o.pos= UnityObjectToClipPos(vp.v);
                //o.pos+=20; 影響在物件在世界座標的位置
                o.col=vp.normal*0.5+0.2;
                return o;
            }
            fixed4 f(myOutPut d):SV_TARGET{
                return fixed4(d.col,1);
            }


結果:


補充:
有內建的資料結構可用,以下是用appdata_full
會輕鬆很多
//片段著色資料
            struct v2f{
                float4 pos : SV_POSITION;
                fixed4 color : COLOR0;
            };

            //讓頂點著色器與片段著色器互動
            v2f vert(appdata_full v){
                v2f o;
                o.pos= UnityObjectToClipPos(v.vertex);

                o.color=fixed4(v.texcoord.xy ,0.0 ,1);
                o.color=fixed4(v.texcoord1.xy ,0.0 ,1);

                return o;
            }
            
            fixed4 frag(v2f i):SV_Target{
                return i.color;
            }


接下來是胡亂實驗:
改成這樣但結果與上面相同
          struct myData{
                float4 v:POSITION; //模型頂點座標
                float3 normal: NORMAL; //法線
                float4 texcoord:TEXCOORD0; //貼圖座標
                
                fixed3 col : COLOR0;
            };
         
            myData v(myData vp){
                myData o;
                o.v= UnityObjectToClipPos(vp.v);
                o.col=vp.normal*0.5+0.5;
                return o;
            }
            fixed4 f(myData d):SV_TARGET{
                d.v=0;//無效
                //d.col=d.normal*0.5+0.5; //無效,無法取得normal
                return fixed4(d.col,1);
            }

但我發現在片段著色器 f() 裡面執行跟頂點有關的程式碼是無效的,或會取不到值。
所以我想會制定出一個偏向給fragment函數用的資料類別可能一來是頂點與片段間資料傳遞,二來是用來區分跟片段著色較無關係的變數吧??




先這樣,我直接跳過數學理論那段就來看程式碼了....在想下一個該繼續看實作的,還是回去看數學的基礎呢

然後我的書是跟老師借的,非常推薦!! 對新手超友善。
送禮物贊助創作者 !
0
留言

創作回應

樂小呈
我是理論派
董邏輯才能活用[e12]

這裡有一些Shader教學,但窩看不董
https://catlikecoding.com/unity/tutorials/
https://www.febucci.com/

加油!
2020-04-28 10:37:22
%%鼠 拒收病婿
感謝分享![e5]
不過我是實作派的,應該說沒有trial and error,我是學不起來的[e15]
2020-04-30 18:36:21
追蹤 創作集

作者相關創作

相關創作

更多創作