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