淺談Unity中的Shader
一、Shader基礎(chǔ)知識
1.1、什么是Shader
在講什么是Shader之前我們先看看下面兩段代碼
這兩段代碼實現(xiàn)的功能都是提取 2D 圖像上每個像素點的顏色值,第一段代碼是用c++寫的,在cup上面運行,它需要循環(huán)遍歷每個像素點,第二段代碼是CG代碼,在GPU上面運行,它只需要一行代碼就能實現(xiàn)同樣的功能。GPU是專門用來進行圖形處理的,而Shader,就是GPU執(zhí)行的一段針對3D對象進行操作的程序。
維基百科上對shader的解釋是這樣
Shader(著色器)應(yīng)用于計算機圖形學(xué)領(lǐng)域,指一組供計算機圖形資源在執(zhí)行渲染任務(wù)時使用的指令,用于計算圖像的顏色或明暗。但近來,它也能用于處理一些特殊效果,或者視頻后處理。通俗地說,著色器告訴電腦如何用特有的一種方法去繪制物體。
程序員將著色器應(yīng)用于圖形處理器(GPU)的可編程流水線,來實現(xiàn)三維應(yīng)用程序。這樣的圖形處理器有別于傳統(tǒng)的固定流水線處理器,為GPU編程帶來更高的靈活性和適應(yīng)性。以前固有的流水線只能進行一些幾何變換和像素灰度計算。現(xiàn)在可編程流水線還能處理所有像素、頂點、紋理的位置、色調(diào)、飽和度、明度、對比度并實時地繪制圖像。著色器還能產(chǎn)生如模糊、高光、有體積光源、失焦、卡通渲染、色調(diào)分離、畸變、凹凸貼圖、邊緣檢測、運動檢測等效果。
1.2、OpenGL的渲染流程
知道了什么是shader,我們再來了解一下shader的種類,首先我先介紹一下OpenGL的渲染流程
上圖便是OpenGL的渲染流程,將這個流程簡化之后是這樣的
頂點變換 → 圖元裝配和光柵化 → 片元紋理映射和著色 → 寫入幀緩存
在頂點變換和片元著色這兩步時,我們就可以對其編程,進行各種操作,其他的部分我們是沒法進行編程的。我們的shader就是作用于頂點變換和片元著色這兩個部分的。
1.3、shader的種類
知道了shader起作用的地點,我們現(xiàn)在可以了解一下shader的種類了。
shader按管線分類一般分為固定渲染管線與可編程渲染管線。固定渲染管線就是功能固定的管線,比如物體表面光的折射,反射的算法是固定無法修改的,我們只能對這些功能進行配置,比如開啟或關(guān)閉反射效果,霧化效果等。因為這種管線功能固定,無法在程序上對物體細(xì)節(jié)的表現(xiàn)給予更多更自由的控制,無法達到更多我們想要的畫面效果。所以現(xiàn)在的顯卡都是可編程渲染管線,也就是曾經(jīng)那些我們固定無法修改的部門現(xiàn)在可以編程去修改,自由度高了之后,我們也就能實現(xiàn)更多自己想要的特效了。
1.4、shader的開發(fā)語言
知道了shader的種類,我在來說說shader的開發(fā)語言
HLSL: 主要用于Direct3D。平臺:windows。
GLSL: 主要用于OpenGL。 平臺:移動平臺(iOS,安卓),mac(only use when you target Mac OS X or OpenGL ES 2.0)
CG:與DirectX 9.0以上以及OpenGL 完全兼容。運行時或事先編譯成GPU匯編代碼。CG比HLSL、GLSL支持更多的平臺,Unity Shader采用CG/HLSL作為開發(fā)語言。
二、Unity中Shader知識介紹
2.1、shader在GPU的渲染流程
進入GPU運算首先進行的是Vertex Processor頂點處理器,這個部分就需要我們使用Vertex Shader頂點著色器,頂點著色器運算的結(jié)果會交給Pixel Processor像素處理器,也就是片段處理器,在這個部分我需要為像素處理編寫Pixel Shader像素著色器程序,這部分計算完后就輸出了最終我們可以用于在屏幕上的顏色信息,我們把它叫做Frame Buffer幀緩沖。幀緩沖存儲的是計算機依次顯示所要的數(shù)據(jù)。
2.2、Unity中shader的類型
①Fixed function shader :屬于固定渲染管線 Shader, 基本用于高級Shader在老顯卡無法顯示時的回滾。使用的是ShaderLab語言,語法與微軟的FX files 或者NVIDIA的 CgFX類似。
②Vertex and Fragment Shader:最強大的Shader類型,屬于可編程渲染管線. 使用的是CG/HLSL語法。
③Surface Shader:Unity3d推崇的Shader類型,使用Unity預(yù)制的光照模型來進行光照運算。使用的也是CG/HLSL語法。
我們先了解一下這三種shader的異同點。
相同點:
①都必須從唯一一個根Shader開始
②Properties參數(shù)部分,作用及語法完全相同
③具體功能都在SubShader里(Subshader會自上而下運行第一個硬件能支持的)
④SubShader都可以打標(biāo)簽
⑤都可以回滾
⑥都可以處理基本的功能,例如光照漫反射(Diffuse)以及鏡面反射(Specular)。但是Vertex and Fragment和Surface都能實現(xiàn)Fixed function實現(xiàn)不了的高級功能,例如基于uv計算的效果等等。
不同點
①Fixed function shader以及Vertex and Fragment Shader在subshader下面還有pass{}結(jié)構(gòu),但是Surface Shader,已經(jīng)將具體內(nèi)容打包在光照模型了,不能加pass{}
②Fixed function shader每句代碼之后沒有分號“;”, 但是V&F shader以及Surface shader每句代碼之后都必須加分號“;”
③核心結(jié)構(gòu)不同
Fixed function shader的SunShader中的結(jié)構(gòu)為
Material{} …… SetTexture[_MainTex]{ …… }
Vertex and Fragment Shader的核心結(jié)構(gòu)為
CGPROGRAM #pragma vertex vert #pragma fragment frag …… #include "UnityCG.cginc" ENDCG
Surface Shader的核心結(jié)構(gòu)是
CGPROGRAM #pragma surface surf Lambert …… ENDCG
可以看到這三種shader的Subshader內(nèi)的編碼實現(xiàn)是不一樣的,這三種shader的結(jié)構(gòu)如下圖,他們的不同點都在SubShader里面
因為Unity推薦Surface Shader,所以文章直接分析Surface Shader的用法,其他兩種shader就不做過多介紹了。
三、Surface Shader語法
在Unity的項目面板中直接創(chuàng)建一個Stander surface shader,默認(rèn)生成的代碼如下
Shader "Custom/DiffuseShader" { Properties { _Color ("Color", Color) = (1,1,1,1) //設(shè)置一個默認(rèn)的顏色值 _MainTex ("Albedo (RGB)", 2D) = "white" {} //默認(rèn)的白色紋理 _Glossiness ("Smoothness", Range(0,1)) = 0.5 //默認(rèn)的光澤度 _Metallic ("Metallic", Range(0,1)) = 0.0 //金屬光澤度 } SubShader { Tags { "RenderType"="Opaque"} LOD 200 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 fixed4 _Color; sampler2D _MainTex; half _Glossiness; half _Metallic; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
接下來我來介紹一下這段代碼
Properties {}
Properties{}是定義著色器屬性的,在這里定義的屬性將被作為輸入提供給所有的子著色器。屬性定義的格式如下
_Name(“Display Name”, type) = defaultValue[{options}]
_Name代表的是屬性名,如Color,MainTex,Glossiness ,Metallic 等
”Display Name”則是在Inspector中顯示的名字
type代表屬性:
Color - 一種顏色,由RGBA(紅綠藍和透明度)四個量來定義;
2D - 一張2的階數(shù)大小(256,512之類)的貼圖。這張貼圖將在采樣后被轉(zhuǎn)為對應(yīng)基于模型UV的每個像素的顏色,最終被顯示出來;
Rect - 一個非2階數(shù)大小的貼圖;
Cube - 即Cube map texture(立方體紋理),簡單說就是6張有聯(lián)系的2D貼圖的組合,主要用來做反射效果(比如天空盒和動態(tài)反射),也會被轉(zhuǎn)換為對應(yīng)點的采樣;
Range(min, max) - 一個介于最小值和最大值之間的浮點數(shù),一般用來當(dāng)作調(diào)整Shader某些特性的參數(shù)(比如透明度渲染的截止值可以是從0至1的值等);
Float - 任意一個浮點數(shù);
Vector - 一個四維數(shù);
這段默認(rèn)Properties在Inspector中的顯示效果如下
SubShader{}
Tags :tags標(biāo)簽是三種類型的shader都具有的標(biāo)簽,它決定了硬件什么調(diào)用該子著色器
Tags標(biāo)簽里面默認(rèn)的“RenderType”=”O(jiān)paque”,是告訴系統(tǒng)應(yīng)該在渲染非透明物體時調(diào)用這個SubShader
“RenderType”=”Transparent”表示在渲染含有透明效果的物體時調(diào)用該Sunshader,
Tags里面還有許多其他的我們可選的標(biāo)簽
①.”Queue”:定義渲染順序。預(yù)制的值有這些
”Background”。值為1000。比如用于天空盒。
”Geometry”。值為2000。大部分物體在這個隊列。不透明的物體也在這里。
”AlphaTest”。值為2450。已進行AlphaTest的物體在這個隊列。
”Transparent”。值為3000。透明物體。
”O(jiān)verlay”。值為4000。比如鏡頭光暈。
用戶可以定義任意值,比如”Queue”=”Geometry+10”
②“RenderType”:定義渲染類型。預(yù)制的值有這些
”O(jiān)paque”:絕大部分不透明的物體都使用這個;
”Transparent”:絕大部分透明的物體、包括粒子特效都使用這個;
”Background”:天空盒都使用這個;
”O(jiān)verlay”:GUI、鏡頭光暈都使用這個;
③”ForceNoShadowCasting”:定義物體是否有陰影效果
“true”。表示有陰影
“false”。表示沒有陰影
LOD:Level of Detail的縮寫,它表示著色器的細(xì)節(jié)層次效果。在某些硬件比較差的系統(tǒng)上,我們可以設(shè)置一個低一點的值,減少細(xì)節(jié)的顯示。Unity內(nèi)置shader的LOD值如下
- VertexLit kind of shaders = 100
- Decal, Reflective VertexLit = 150
- Diffuse = 200
- Diffuse Detail, Reflective Bumped Unlit, Reflective Bumped VertexLit = 250
- Bumped, Specular = 300
- Bumped Specular = 400
- Parallax = 500
- Parallax Specular = 600
從CGPROGRAM 到ENDCG這一部分就這這個shader的核心內(nèi)容了
#pragma surface surf Standard fullforwardshadows
這段編譯指令聲明了我們要寫一個Surface Shader,并指定了光照模型。它的寫法是這樣的
#pragma surface surfaceFunction lightModel [optionalparams]
surface - 聲明的是一個表面著色器surfaceFunction - 著色器代碼的方法的名字lightModel - 使用的光照模型。
這段代碼默認(rèn)的surfaceFunction為surf,我們可以在源碼的底部看到在這兒聲明了的surf函數(shù)。默認(rèn)的lightModel為Standard。
下面我先介紹一下lightModel光照模型
- Lambert:該光照模型能很好的表示粗糙表面的光照,但不能表現(xiàn)出鏡面反射高光
- Toon:最近在游戲中常用的風(fēng)格之一即是Toon shading(又稱 cel shading).這是一種非逼真渲染風(fēng)格,通過改變了光在一個模型上反射實際情況來給人以手繪的感覺
- BlinnPhong:仿真鏡面反射材料
- Standard:Unity5中默認(rèn)的光照模式是Standard, 其引入了 物理渲染 (PBR), 但是與其它光照模型沒有什么不同。相比于朗伯反射, PBR提供了一個更加逼真的光線物體作用模型,PBR考慮了材料的物理屬性, 比如能量守恒以及光的散射
接下來的這段代碼
fixed4 _Color; sampler2D _MainTex; half _Glossiness; half _Metallic;
我們可以發(fā)現(xiàn) _Color,_MainTex,_Glossiness,_Metallic都shader屬性的聲明,在上面的Properties 中已經(jīng)聲明過了這些屬性,但是在這段CG程序,要想訪問在Properties中所定義的變量的話,必須使用和之前變量相同的名字再次進行聲明,其實就是鏈接在上面properties中聲明的屬性。
我再來介紹一下shader中常用的數(shù)據(jù)類型
3種基本數(shù)值類型:float、half和fixed。
這3種基本數(shù)值類型可以再組成vector和matrix,比如half3是由3個half組成、float4x4是由16個float組成。float:32位高精度浮點數(shù)。
half:16位中精度浮點數(shù)。范圍是[-6萬, +6萬],能精確到十進制的小數(shù)點后3.3位。
fixed:11位低精度浮點數(shù)。范圍是[-2, 2],精度是1/256。
Sampler2D:2D紋理屬性
接下來就是Input結(jié)構(gòu)體
struct Input { float2 uv_MainTex; };
這個結(jié)構(gòu)體和surf函數(shù)中的另一個參數(shù)inout結(jié)構(gòu)體是相對的,一個代表輸入,一個代表輸出。我們可以這樣理解這兩個結(jié)構(gòu)體,你定義輸入數(shù)據(jù)結(jié)構(gòu)(Inputs Struct)、編寫自己的Surface函數(shù)處理輸入、最終輸出修改過后的SurfaceOutput。Input其實是需要我們?nèi)ザx的結(jié)構(gòu),所以我們可以把所需要參與計算的數(shù)據(jù)都放到這個Input結(jié)構(gòu)中,傳入surf函數(shù)使用
默認(rèn)的Input結(jié)構(gòu)體中有一個uv_MainTex參數(shù),代表了紋理的UV值,我們便可以在surf函數(shù)中直接使用這個參數(shù)了。
知道了Input的結(jié)構(gòu)體,我們在來看看Output的結(jié)構(gòu)體
struct SurfaceOutput { fixed3 Albedo; // diffuse color 漫反射的顏色值。 fixed3 Normal; // tangent space normal, if written 法線坐標(biāo) fixed3 Emission; //自發(fā)光顏色 half Specular; // specular power in 0..1 range 鏡面反射系數(shù) fixed Gloss; // specular intensity 光澤系數(shù) fixed Alpha; // alpha for transparencies 透明度系數(shù) };
現(xiàn)在我們在來看看surf函數(shù)里面的內(nèi)容,就已經(jīng)能夠看懂了
我們現(xiàn)在在來看看surf函數(shù)里面的代碼,就能知道里面是什么意思了
void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; //將物體顯示的漫反射顏色設(shè)置成在紋理的顏色值 o.Metallic = _Metallic; //將物體顯示的金屬光澤設(shè)置成在properties中定義的光澤 o.Smoothness = _Glossiness; //設(shè)置物體顯示的光滑度 o.Alpha = c.a; //設(shè)置物體顯示的透明度 }
以上就是淺談Unity中的Shader的詳細(xì)內(nèi)容,更多關(guān)于Unity Shader的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#調(diào)用SQL?Server中有參數(shù)的存儲過程
這篇文章介紹了C#調(diào)用SQL?Server中有參數(shù)存儲過程的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03如何在datatable中使用groupby進行分組統(tǒng)計
如何在datatable中進行分組,并且計算分組后每組的數(shù)量,考慮了一下,可以使用LINQ來實現(xiàn)datatable分組,需要的朋友可以參考下2015-08-08C#使用NPOI讀取excel轉(zhuǎn)為DataSet
這篇文章主要為大家詳細(xì)介紹了C#使用NPOI讀取excel轉(zhuǎn)為DataSet,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02C# wpf Brush轉(zhuǎn)Hex字符串的實例代碼
這篇文章主要介紹了C# wpf Brush轉(zhuǎn)Hex字符串的實例代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01