閱前提示
記錄Unity Shader 學習筆記魂贬,拿起這塊磚甫窟,砸開Shader的門跟继。
適合人群:Shader 初學者
閱讀方式:目錄順序閱讀
知道的越多种冬,不知道的越多。希望我的文章對你有所幫助
獲取更多:我的博客
Shader
渲染管線:
? 渲染流程主要分為三個階段舔糖,由CPU與GPU完成
1.應用階段(cpu部分)
此階段主要是處理數(shù)據(jù)(硬盤——>內存)娱两,設置渲染狀態(tài),DrawCall
加載數(shù)據(jù)到顯存
設置渲染狀態(tài)
DrawCall
? 將準備的數(shù)據(jù)(點金吗,線十兢,面,材質摇庙,等信息打包傳遞給GPU【圖元數(shù)據(jù)】旱物,通知GPU開始渲染)
2.幾何階段(gpu)
該階段主要做的事情是坐標轉換,將空間坐標轉換為屏幕坐標卫袒。
細分下來有如下幾個過程:
頂點著色器(Vertex)
? 實現(xiàn)頂點的空間變化宵呛,頂點著色。
曲面著色器(Tessellation)
? 細分圖元
幾何著色器(Geometry)
? 執(zhí)行逐圖元著色操作夕凝,或用于產(chǎn)生更多圖元
剪裁(Clipping)
? 剪裁不在攝像機內的頂點宝穗,剔除某些三角圖元的面片。
屏幕映射(ScreenMapping)
? 將圖元坐標轉到屏幕坐標新中
3.光柵化階段(gpu)
設置三角面码秉,計算每個像素的顏色逮矛,最終呈現(xiàn)。
三角形設置
? 上階段輸出的三角網(wǎng)格頂點數(shù)據(jù)转砖,計算三角網(wǎng)格须鼎。
三角形遍歷
? 找尋被三角網(wǎng)格覆蓋的像素(片元)
片元著色器
? 可編程,完成重要的渲染技術府蔗。
逐片元操作
? 稱為輸出合并階段晋控,此階段片元將進行很多測試工作。
? 模板測試:用于限制渲染區(qū)域礁竞,或一些高級用法 渲染陰影糖荒,輪廓渲染
? 深度測試:用于判斷遮擋
? 混合:用于處理透明,半透明
數(shù)學
向量/矢量v(x,y,z)
模
點積
? v1 在v2上的投影長度 (>0 方向相同 反之)
叉積
向量叉積來計算垂直于該平面的向量
矩陣
單位矩陣
置換矩陣
逆矩陣
高斯-若爾當消元法
? 逆矩陣的求法
正交矩陣
矩陣變換
基礎變化矩陣
法線變換
若一個點T 的變換矩陣為M,則法線N的變換矩陣為M的逆轉置矩陣
Unity Shader
四種Shader
-
Standard Surface Shader 表面著色器模板(Unity后臺轉換為頂點/片元著色器)
Shader "Custom/NewSurfaceShader" { Properties {} SubShader { Tags { "RenderType"="Opaque" } //[RenderSetup] CGPROGRAM ENDCG } FallBack "Diffuse" }
-
Unlit Shader 頂點/片元著色器
Shader "Unlit/NewUnlitShader" { Properties{} SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM ENDCG } } }
Image Effect Shader 屏幕后處理效果模板
Compute Shader 常規(guī)渲染流水線無關的計算
第一個Shader
Shader "Unlit/SimpleShader"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v:POSITION):SV_POSITION
{
return UnityObjectToClipPos(v);//對頂點坐標進行了裁剪空間轉換
}
fixed4 frag():SV_TARGET
{
return fixed4(1,1,0,1);
}
ENDCG
}
}
}
vertex
? 聲明了哪個函數(shù)包含頂點著色器代碼
? 頂點著色器:逐頂點執(zhí)行
fragment
? 聲明了哪個函數(shù)包含了片元著色器代碼
? 片元著色器:逐片元執(zhí)行
頂點與片元的關系
? 在渲染管線中,GPU執(zhí)行的開始便是頂點作色器综看,它的輸入來自CPU,主要工作是 坐標轉換和逐頂點光照品腹,且可以為后續(xù)階段輸出數(shù)據(jù)
? 片元著色器作用于光柵化階段,三角形遍歷階段會檢查每個像素是否被三角網(wǎng)絡覆蓋红碑,如果被覆蓋的話就會生成一個片元舞吭。
光照
漫反射
_LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal,worldLight))
半蘭伯特模型(Half Lambert)
_LightColor0.rgb * _Color.rgb * (dot(worldNormal,worldLight)*0.5 + 0.5)
目的在于把[-1,1]映射到[0,1],模型的背光面也會有明暗變化析珊。因為普通漫反射會與0做比較羡鸥。
高光反射光照模型(Phong)
Clight是入射光的顏色和強度,材質的高光反射系數(shù)Mspecular忠寻,視角方向V和反射方向R點乘的結果的光系數(shù)Mgloss次方
fixed3 reflectDir = normalize(reflect(worldLight,worldNormal));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld,v.vertex));
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(viewDir,reflectDir)),_Gloss);
Blinn
紋理
屬性:_MainTex ("Texture", 2D) = "white" {}
聲明:
sampler2D _MainTex;
float4 _MainTex_ST;
// float2 uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
float2 uv = TRANSFORM_TEX(v.texcoord,_MainTex);//縮放與平移確定最終紋理坐標
float3 TexelsValue = tex2D(_MainTex,uv);//通過紋理坐標采樣紋素值
紋理名_ST 代表紋理屬性 ST(scale,translation)
X_ST.xy 代表縮放惧浴,X_ST.zw 代表平移
法線紋理
凹凸映射有兩種方式:
? 高度紋理:顏色深淺表示凹凸
? 法線紋理:通過法線擾動方向確定凹凸
因為法線分量范圍[-1,1] 像素的分量范圍[0,1]
模型空間的法線紋理
? 實現(xiàn)簡單,直白奕剃,計算少衷旅。但得到的是絕對法線信息,僅可用于創(chuàng)建時的模型纵朋。
切線空間的法線紋理
? 自由度高柿顶,記錄的是相對法線信息,適用于不同的網(wǎng)格操软。
? 可進行UV動畫
? 可壓縮嘁锯。切線空間下法線紋理的Z方向總是正方向,因此可靠XY方向推導Z方向寺鸥。
切線空間下的代碼:
//把模型空間下切線方向桩盲,副切線方向秘血,法線方向 排列得到模型空間到切線空間的旋轉矩陣
//副切線由其他兩個做叉乘得到友酱,乘w 是為了區(qū)分方向(因為有兩個)
// float3 binormal = cross(normalize(v.normal),normalize(v.tangent.xyz))*v.tangent.w;
// float3x3 rotation = float3x3(v.tangent.xyz,binormal,v.normal);
// TANGENT_SPACE_ROTATION;//內置宏,效果同上
TANGENT_SPACE_ROTATION;
//計算切線空間下的光線與視角
o.lightDir = mul(rotation,ObjSpaceLightDir(v.vertex)); //模型——>切線
o.viewDir = mul(rotation,ObjSpaceViewDir(v.vertex));
//計算切線空間下的法線坐標
fixed3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentViewDir = normalize(i.viewDir);
fixed4 packedNormal = tex2D(_BumpMap,i.uv.zw);
fixed3 tangentNromalDir;
tangentNromalDir = UnpackNormal(packedNormal);//法線紋理設置成 normal map 后使用此方法
// tangentNromalDir.xy = (packedNormal*2 - 1)*_BumpScale;
tangentNromalDir.xy *= _BumpScale;
tangentNromalDir.z =
sqrt(1-saturate(dot(tangentNromalDir.xy,tangentNromalDir.xy)));//根據(jù)xy推導z
漸變紋理
通過漸變紋理使漫反射輪廓鮮明兼丰。
原理是由半蘭伯特而來,利用HalfLambert 使法線和光線的點積的范圍變?yōu)榱薣0,1]笆载,從而在UV上以halfLambert為坐標采樣扑馁。
fixed halfLambert = 0.5*(dot(worldNormalDir,worldLightDir)) + 0.5;
fixed3 diffuseColor = tex2D(_RampTex,float2(halfLambert,halfLambert)).rgb * _Color.rgb;
fixed3 diffuse = _LightColor0.rgb * diffuseColor;
遮罩紋理
遮罩紋理為我們提供了更為精確(像素級)地控制模型表面的各種性質。
例如:可以通過一張遮罩紋理的R通道的值來控制高光反射的強弱凉驻。
fixed specularMask = tex2D(_SpecularMark,uv).r * _SpecularScale;
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(i.worldNormal,blinnDir)),_Gloss) * specularMask;
所以腻要,通過增加遮罩紋理,我們可以控制更多的表面屬性涝登。
透明
透明度測試
判斷一個片元透明度滿不滿足條件從而進行舍去雄家,保留的片元將于普通的不透明物體一起處理。無其他特殊開關胀滚,即物體要不就是純透明趟济,要不就是不透明乱投。
clip(texColor.a - _Cutoff);
//當 a - 定義的系數(shù) < 0 是將執(zhí)行 discard 指令剔除該片元
透明度混合
真正意義上的半透明。它會將當前片元顏色于存儲在顏色緩沖中點顏色進行混合顷编。但此混合會關閉深度寫入戚炫,所以必須要注意渲染順序。