Physics CG:sobel边缘检测

       接下来几篇主要围绕卷积具体能在图形渲染中实现什么效果,比如常用的边缘检测滤波器(卷积滤波器),这个滤波器不仅在学习卷积的时候基本都会讲到,而且我看过的国内外shader开发书籍中也都会提到,滤波器构成如下:

       三维图形学中边缘的意义我们之前讲过,不明白的可以回过去看下,这里我们在来看下二维图形中边缘的意义。二维图形中,因为图形中像素值的不同,会在连续像素差异较大的地方让我们肉眼形成一种边界的视觉,同时边界分为xy两轴组成,所以我们需要xy两个方向的滤波器进行卷积运算。

       贴下xy两个方向的滤波器(算子)的数值矩阵,如下:

       sobel滤波器

              -1      0       1               -1       -2        -1

       x =  -2      0       2        y =   0         0         0

              -1      0       1                1         2         1

       首先我说下sobel卷积核心公式:这两个算子需要和当前像素矩阵的亮度值做点乘,得到的xy轴两方向的值(也称梯度),计算其结合梯度值(G = sqrt(Gx^2 + Gy ^2);),然后将这个梯度值G作为主纹理颜色与边缘颜色混合的权重值即可。

       这里需要将RGB转YUV中取Y也就是明亮度(如果做视频直播的同学可能容易理解YUV编码方式的意义和应用,比如占用储存空间更小等)

       一般的亮度计算公式: Y(亮度)=(0.299*R)+(0.587*G)+(0.114*B)

       当然了还有如下:

       

      这里我们不用算子去点乘RGB是因为Y明亮度作为参数取梯度值更能准确表明连续像素之间的差异。那么对于xy轴两个算子,图形数学敏感一些的话,看得出来x轴算子处理的像素矩阵,如果x正负方向上明亮度差异很大,就能产生一个绝对值较大的梯度值,那么我们就认为这就是“边界”,y轴算子同理。

       接着就来shader实现,如下:

       

Shader "Unlit/EdgeSobelShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_EdgeColor("Edge Color",color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv[9] : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
			float4 _MainTex_TexelSize;
			float4 _EdgeColor;    /*边缘颜色值*/
			
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                float2 uv = TRANSFORM_TEX(v.uv, _MainTex);

				//从上到下从左到右依次计算卷积核对应的9个像素值
				o.uv[0] = uv + _MainTex_TexelSize.xy*float2(-1, 1);
				o.uv[1] = uv + _MainTex_TexelSize.xy*float2(0, 1);
				o.uv[2] = uv + _MainTex_TexelSize.xy*float2(1, 1);

				o.uv[3] = uv + _MainTex_TexelSize.xy*float2(-1, 0);
				o.uv[4] = uv;
				o.uv[5] = uv + _MainTex_TexelSize.xy*float2(1, 0);

				o.uv[6] = uv + _MainTex_TexelSize.xy*float2(-1, -1);
				o.uv[7] = uv + _MainTex_TexelSize.xy*float2(0, -1);
				o.uv[8] = uv + _MainTex_TexelSize.xy*float2(1, -1);

                return o;
            }

			//rgb提取明亮度
			//rgb转yuv公式
			float RgbToY(fixed4 col)
			{
				return 0.299 * col.r + 0.587 * col.g + 0.114 * col.b;
			}

            fixed4 frag (v2f i) : SV_Target
            { 
				float xSobel[9] = { -1,0,1,-2,0,2,-1,0,1 };    /*x轴sobel算子*/
				float ySobel[9] = { -1,-2,-1,0,0,0,1,2,1 };    /*y轴sobel算子*/
				//xy轴梯度和结合梯度
				float G = 0, Gx = 0, Gy = 0;
				for (int k = 0; k < 9; k++)
				{
					float y = RgbToY(tex2D(_MainTex, i.uv[k]));
					Gx += y * xSobel[k];
					Gy += y * ySobel[k];
				}
				//计算结合梯度
				G = sqrt(Gx*Gx + Gy*Gy);
				//主纹理采样
				fixed4 col = tex2D(_MainTex, i.uv[4]);
				//梯度混合
				col = lerp(col, _EdgeColor, G);
				return col;
            }
            ENDCG
        }
    }
}

       顺便看下效果,如下:

       

       好了,之后会找一些其他的卷积滤波器来实现一些效果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值