1. 程式人生 > >Unity Shader入門精要 閱讀筆記十七

Unity Shader入門精要 閱讀筆記十七

前言

這裡講邊緣檢測這一節中的筆記內容。邊緣檢測這裡更多的用到了影象學的一些基本內容,包括卷積核,索貝爾運算元等等一些內容。整個流程還是比較清晰的。

邊緣檢測演算法

獲取到螢幕渲染的顏色緩衝之後,將螢幕中的畫素用索貝爾運算元進行卷積操作,卷積的結果就是當前畫素的一個導數。這裡說導數其實可能比較難懂,說變化程度應該會好理解一些。如果這裡顏色突變的厲害那麼他的變化程度就會大一些,如果顏色變化比較平坦,那麼變換程度就會小一些,計算的結果也會小一些。

這裡需要將一些索貝爾運算元和影象卷積操作。

[121000121]=Gx

[101202101]=Gy

大家在看書寫程式碼的時候可能會有疑問,正文中寫的Gx有值的部分明明是在列上面,第二列為0,其他列有值,Gy為第二行為0.但是在程式碼中Gx與Gy似乎反了。
在影象處理(岡薩雷斯)這本書裡是這樣描述Gx與Gy的:
運算元與畫素相乘,第三行與第一行間的差距接近與x方向上的微分,第三列與第一列間的差接近y方向的微分。(P107,Edition 2);

同時在編碼中還有一個問題,不是說卷積操作的時候需要先將運算元轉180度,然後再對於相乘相加嗎?怎麼編碼的時候又直接對應相乘相機沒有旋轉了?

答:(影象處理(岡薩雷斯)P92,Edi 2)中提到,線性空間濾波與頻率域中的卷積概念類似,因此線性空間濾波也稱為“掩膜與影象的卷積”。“濾波掩膜”也被稱為“卷積模板”,“卷積核”。

也就是說影象與運算元的卷積實際上是一種線性空間濾波,而這種濾波的表達形式就是對應相乘相加。

這裡給出mxn大小的線性濾波掩膜的表示式:

g(x,y)=s=aat=bbw(s,t)f(x+s,y+t)
看到這裡應該知道為什麼要這樣來計算了吧。

邊緣檢測

邊緣檢測程式碼上一波:

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和迴圈語句。