Unity Shader入門精要 閱讀筆記十七
阿新 • • 發佈:2019-02-09
前言
這裡講邊緣檢測這一節中的筆記內容。邊緣檢測這裡更多的用到了影象學的一些基本內容,包括卷積核,索貝爾運算元等等一些內容。整個流程還是比較清晰的。
邊緣檢測演算法
獲取到螢幕渲染的顏色緩衝之後,將螢幕中的畫素用索貝爾運算元進行卷積操作,卷積的結果就是當前畫素的一個導數。這裡說導數其實可能比較難懂,說變化程度應該會好理解一些。如果這裡顏色突變的厲害那麼他的變化程度就會大一些,如果顏色變化比較平坦,那麼變換程度就會小一些,計算的結果也會小一些。
這裡需要將一些索貝爾運算元和影象卷積操作。
大家在看書寫程式碼的時候可能會有疑問,正文中寫的Gx有值的部分明明是在列上面,第二列為0,其他列有值,Gy為第二行為0.但是在程式碼中Gx與Gy似乎反了。
在影象處理(岡薩雷斯)這本書裡是這樣描述Gx與Gy的:
運算元與畫素相乘,第三行與第一行間的差距接近與x方向上的微分,第三列與第一列間的差接近y方向的微分。(P107,Edition 2);
同時在編碼中還有一個問題,不是說卷積操作的時候需要先將運算元轉180度,然後再對於相乘相加嗎?怎麼編碼的時候又直接對應相乘相機沒有旋轉了?
答:(影象處理(岡薩雷斯)P92,Edi 2)中提到,線性空間濾波與頻率域中的卷積概念類似,因此線性空間濾波也稱為“掩膜與影象的卷積”。“濾波掩膜”也被稱為“卷積模板”,“卷積核”。
也就是說影象與運算元的卷積實際上是一種線性空間濾波,而這種濾波的表達形式就是對應相乘相加。
這裡給出mxn大小的線性濾波掩膜的表示式:
看到這裡應該知道為什麼要這樣來計算了吧。
邊緣檢測
邊緣檢測程式碼上一波:
Shader "Custom/8/8.1.2" {//邊緣檢測
Properties{
_MainTex("Main Texture",2D) = "white"{}
_EdgeColor("Edge Color" ,color) = (1,1,1,1)
_BackgroundColor("Background",Color) = (1,1,1,1)
_EdgeOnly("edgeOnly",float) = 1
}
SubShader{
Pass{
ZTest Always
ZWrite Off
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler _MainTex;
float4 _MainTex_TexelSize;//紋素
float4 _EdgeColor;
float4 _BackgroundColor;
float _EdgeOnly;
struct v2f {
float4 pos : SV_POSITION;
float2 uv[9] : TEXCOORD0;
};
fixed luminance(fixed3 color)
{
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
half sobel(v2f i) {
//const half Gx[9] = { -1,-2,-1,
// 0,0,0,
// 1,2,1 };
//const half Gy[9] = { -1,0,1,
// -2,0,2,
// -1,0,1 };
const half Gx[9] = { -1,-2,-1,
0,0,0,
1,2,1 };
const half Gy[9] = { -1,0,1,
-2,0,2,
-1,0,1 };
float4 texColor;
float edgex = 0;
float edgey = 0;
for (int it = 0; it < 9; it++)
{
texColor = luminance(tex2D(_MainTex, i.uv[it]));
edgex += texColor * Gx[it];
edgey += texColor * Gy[it];
}
return 1 - abs(edgex) - abs(edgey);
}
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//求紋素
float2 uv = v.texcoord;
o.uv[0] = uv + _MainTex_TexelSize.xy * half2(-1, -1); //左上
o.uv[1] = uv + _MainTex_TexelSize.xy * half2(0, -1); //左上
o.uv[2] = uv + _MainTex_TexelSize.xy * half2(1, -1); //左上
o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1, 0); //左上
o.uv[4] = uv + _MainTex_TexelSize.xy * half2(0, 0);
o.uv[5] = uv + _MainTex_TexelSize.xy * half2(1, 0); //左上
o.uv[6] = uv + _MainTex_TexelSize.xy * half2(-1, 1); //左上
o.uv[7] = uv + _MainTex_TexelSize.xy * half2(0, 1); //左上
o.uv[8] = uv + _MainTex_TexelSize.xy * half2(1, 1); //左上
return o;
}
fixed4 frag(v2f i) :SV_Target{
half edge = sobel(i);
fixed4 withEdgeColor = lerp(_EdgeColor, tex2D(_MainTex, i.uv[4]), edge);
fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);
return lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly);
}
ENDCG
}
}
FallBack Off
}
shader程式碼沒有難的地方,唯一需要說的是shader中要避免if和迴圈語句。