最近,剛好在溫故練習Unity Shader相關的內容,所以小編這邊就考慮做一些關鍵知識點的匯總整理。那麼咱們就直接進入主題哈!
三大 Shader 程式語言(CG/HLSL/GLSL)
Shader Language的發展方向是設計出在便攜性方面可以和C++、Java等相比的高級語言,「賦予程式設計師靈活而方便的編程方式」,並「儘可能的控制渲染過程」同時「利用圖形硬體的並行性,提高算法效率」。
Shader Language目前主要有3種語言:基於 OpenGL 的 OpenGL Shading Language,簡稱 GLSL; 基於 DirectX 的 High Level Shading Language, 簡稱 HLSL; 基於 NVIDIA 公司的 C for Graphic,簡稱 Cg 語言。
渲染管線也稱為渲染流水線,是顯示晶片內部處理圖形信號相互獨立的並行處理單元。
Gpu流水線上一些可高度編程的階段,而由著色器編譯出來的最終代碼是會在Gpu上運行的;有一些特定類型的著色器,如頂點著色器,片元著色器等。依靠著色器我們可以控制流水線中的渲染細節,例如用頂點著色器來進行頂點變換及傳遞數據,用片元著色器來進行逐像素渲染。
Unity Shader != 真正的Shader
Unity Shader實際上指的就是一個ShaderLab文件。以.shader作為後綴的一種文件。在Unity shader裡面,我們可以做的事情遠多於一個傳統意義上的Shader。在傳統的shader中,我們僅可以編寫特定類型的Shader,例如頂點著色器,片元著色器等。在Unity Shader中,我們可以在同一個文件裡面同時包含需要的頂點著色器和片元著色器代碼。在傳統shader中,我們無法設置一些渲染設置,例如是否開啟混合,深度測試等,這些是開發者在另外的代碼中自行設置的。而Unity shader中,我們通過一行特定的指令就可以完成這些設置。在傳統shader中,我們需要編寫冗長的代碼設置著色器的輸入和輸出,要小心的處理這些輸入輸出的位置對應關係等。而在Unity shader中,我們只需要在特定語句塊中聲明一些屬性,就可以依靠材質來方便的改變這些屬性。而對於模型自帶的數據(如頂點,紋理坐標,法線等),Unity Shader也提供了直接訪問的方法,不需要開發者自行編碼來傳給著色器。
通常採用動態編譯的方式(Cg也支持靜態編譯方式),即在宿主程序運行時,利用Cg運行庫(Cg Runtimer Library)動態編譯Cg代碼。
Cg數據類型:
01 基礎功能類型函數
03 透明效果:只要一個片元的透明度不滿足條件(通常小於某個閾值),那麼就捨棄對應的片元。被捨棄的片元不會在進行任何的處理,也不會對顏色緩衝產生任何影響;否則,就會按照普通的不透明物體來處理,即進行深度測試,深度寫入等等。雖然簡單,但是很極端,要麼完全透明,要麼完全不透明。透明度混合:可以得到真正的半透明效果,它會使當前片元的透明度作為混合因子,與已經儲存在顏色緩衝中的顏色值進行混合麼,得到新的顏色。但是,透明度混合需要關閉深度寫入,這使得我們要非常小心物體的渲染順序。注意:透明度混合只關閉了深度寫入,但沒有關閉深度測試。這表示當使用透明度混合渲染一個片元時,還是會比較它的深度值與當前深度緩衝中的深度值,如果深度值距離攝像機更遠,那麼就不會在進行混合操作。比如一個不透明物體在透明物體前面,我們先渲染不透明物體,可以正常的擋住不透明物體。常見混合操作類型應用:
A //正常(Normal)透明度混合 Blend SrcAlpha OneMinusSrcAlphaB //柔和相加 Blend OneMinusDstColor OneC //正片疊底 Blend DstColor ZeroD //兩倍相乘 Blend DstColor SrcColorE //變暗 BlendOp min Blend One OneF //變亮 Blend OneMinusDstColor One Blend One OneMinusSrcColorG //線性減淡 Blend One One
在Unity中,表面著色器的關鍵代碼用Cg/HLSL語言編寫,然後嵌在ShaderLab的結構代碼中使用。使用表面著色器,用戶僅需要編寫最關鍵的表面函數,其餘周邊代碼將由Unity自動生成,包括適配各種光源類型、渲染實時陰影以及集成到前向/延遲渲染管線中等。
01 編寫表面著色器有幾個規則
表面著色器的實現代碼需要放在CGPROGRAM..ENDCG代碼塊中,而不是Pass結構中,它會自己編譯到各個Pass。
使用#pragma surface..命令來指明它是一個表面著色器。
A #pragma surface surfaceFunction lightModel [optionalparams]:Surface Shader和CG其他部分一樣,代碼也是要寫在CGPROGRAM和ENDCG之間。但區別是,它必須寫在SubShader內部,而不能寫在Pass內部。Surface Shader自己會自動生成所需的各個Pass。由上面的編譯格式可以看出,surfaceFunction和lightModel是必須指定的。
B surfaceFunction通常就是名為surf的函數(函數名可以任意),它的函數格式是固定的:
void surf (Input IN, inout SurfaceOutput o)
void surf (Input IN, inout SurfaceOutputStandard o)
void surf (Input IN, inout SurfaceOutputStandardSpecular o)
C lightModel也是必須指定的。由於Unity內置了一些光照函數——Lambert(diffuse)和Blinn-Phong(specular),因此這裡在默認情況下會使用內置的Lambert模型。當然我們也可以自定義。
D optionalparams包含了很多可用的指令類型,包括開啟、關閉一些狀態,設置生成的Pass類型,指定可選函數等。除了上述的surfaceFuntion和lightModel,我們還可以自定義兩種函數:vertex:VertexFunction和finalcolor:ColorFunction。也就是說,Surface Shader允許我們自定義四種函數。
02 計算函數匯總
//最重要的計算函數void surf (Input IN, inout SurfaceOutput o)void surf (Input IN, inout SurfaceOutputStandard o)void surf (Input IN, inout SurfaceOutputStandardSpecular o)
//頂點修改void vert (inout appdata_full v)void vert(inout appdata_full v, out Input o)
//unity老版本(新版本兼容)不包含GI的half4 Lighting (SurfaceOutput s, half3 lightDir, half atten)half4 Lighting (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
//unity新版本(包含GI,要自定義GI函數)half4 Lighting (SurfaceOutput s, UnityGI gi)half4 Lighting (SurfaceOutput s, half3 viewDir, UnityGI gi)
//延遲渲染half4 Lighting_Deferred (SurfaceOutput s, UnityGI gi, out half4 outDiffuseOcclusion, out half4 outSpecSmoothness, out half4 outNormal)
//遺留的延遲渲染half4 Lighting_PrePass (SurfaceOutput s, half4 light)
//自定義GIhalf4 Lighting_GI (SurfaceOutput s, UnityGIInput data, inout UnityGI gi);
//最終顏色修改void final(Input IN, SurfaceOutput o, inout fixed4 color)void final(Input IN, SurfaceOutputStandard o, inout fixed4 color)void final(Input IN, SurfaceOutputStandardSpecular o, inout fixed4 color)
兩個結構體就是指struct Input和SurfaceOutput。其中Input結構體是允許我們自定義的。如下表。這些變量只有在真正使用的時候才會被計算生成。在一個貼圖變量之前加上uv兩個字母,就代表提取它的uv值,例如uv_MainTex 。
另一個結構體是(SurfaceOutput、SurfaceOutputStandard和SurfaceOutputStandardSpecular)。我們也可以自定義這個結構體內的變量,自定義最少需要有4個成員變量:Albedo、Normal、Emission和Alpha,缺少一個都會報錯。關於它最難理解的也就是每個變量的具體含義以及工作機制(對像素顏色的影響)。
struct SurfaceOutput { fixed3 Albedo; fixed3 Normal; fixed3 Emission; half Specular; fixed Gloss; fixed Alpha;};Albedo:我們通常理解的對光源的反射率。它是通過在Fragment Shader中計算顏色疊加時,和一些變量(如vertex lights)相乘後,疊加到最後的顏色上的。(漫反射顏色)
Normal:即其對應的法線方向。只要是受法線影響的計算都會受到影響。
Emission:自發光。會在Fragment 最後輸出前(調用final函數前,如果定義了的話),使用下面的語句進行簡單的顏色疊加:c.rgb += o.Emission;
Specular:高光反射中的指數部分的係數。影響一些高光反射的計算。按目前的理解,也就是在光照模型裡會使用到(如果你沒有在光照函數等函數——包括Unity內置的光照函數,中使用它,這個變量就算設置了也沒用)。有時候,你只在surf函數裡設置了它,但也會影響最後的結果。這是因為,你可能使用了Unity內置的光照模型,如BlinnPhong,它會使用如下語句計算高光反射的強度(在Lighting.cginc裡):float spec = pow (nh, s.Specular*128.0) * s.Gloss;
Gloss:高光反射中的強度係數。和上面的Specular類似,一般在光照模型裡使用。
Alpha:通常理解的透明通道。在Fragment Shader中會直接使用下列方式賦值(如果開啟了透明通道的話):c.a = o.Alpha;
因為涉及的代碼量比較大,小編這邊直接在GitHub上分享通用效果測試的工程。
部分測試效果展示(雪融&火焰流動效果):
項目地址:
https://github.com/haili1234/BasicShaderDemo_01
AnimeKing是Unity社區的價值博主,如果你喜歡本文的話,歡迎點擊「閱讀原文」關注AnimeKing的問答專區。想要你的創意被更多人看見嗎?快快加入Unity社區,在社區內分享經驗,你也有機會獲得Unity官方推薦哦! 博客發布入口:unity.cn/articles - 「寫文章"
長按關注
第一時間了解Unity引擎動向,學習最新開發技巧
點擊「閱讀原文」,關注AnimeKing的問答專區