Unity Shader入门精要 阅读笔记十七

前言

这里讲边缘检测这一节中的笔记内容。边缘检测这里更多的用到了图像学的一些基本内容,包括卷积核,索贝尔算子等等一些内容。整个流程还是比较清晰的。

边缘检测算法

获取到屏幕渲染的颜色缓冲之后,将屏幕中的像素用索贝尔算子进行卷积操作,卷积的结果就是当前像素的一个导数。这里说导数其实可能比较难懂,说变化程度应该会好理解一些。如果这里颜色突变的厉害那么他的变化程度就会大一些,如果颜色变化比较平坦,那么变换程度就会小一些,计算的结果也会小一些。

这里需要将一些索贝尔算子和图像卷积操作。

[ 1 2 1 0 0 0 1 2 1 ] = G x
[ 1 0 1 2 0 2 1 0 1 ] = G y

大家在看书写代码的时候可能会有疑问,正文中写的Gx有值的部分明明是在列上面,第二列为0,其他列有值,Gy为第二行为0.但是在代码中Gx与Gy似乎反了。
在图像处理(冈萨雷斯)这本书里是这样描述Gx与Gy的:
算子与像素相乘,第三行与第一行间的差距接近与x方向上的微分,第三列与第一列间的差接近y方向的微分。(P107,Edition 2);

同时在编码中还有一个问题,不是说卷积操作的时候需要先将算子转180度,然后再对于相乘相加吗?怎么编码的时候又直接对应相乘相机没有旋转了?

答:(图像处理(冈萨雷斯)P92,Edi 2)中提到,线性空间滤波与频率域中的卷积概念类似,因此线性空间滤波也称为“掩膜与图像的卷积”。“滤波掩膜”也被称为“卷积模板”,“卷积核”。

也就是说图像与算子的卷积实际上是一种线性空间滤波,而这种滤波的表达形式就是对应相乘相加。

这里给出mxn大小的线性滤波掩膜的表达式:

g ( x , y ) = s = a a t = b b w ( 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和循环语句。

猜你喜欢

转载自blog.csdn.net/u011618339/article/details/79449556