效果
原理
通过卷积的Sobel算子
获取图像灰度图
的梯度值
,梯度值越大的越有可能是边缘
通过横向算子分别计算过上面两张像素图的梯度值,得到结果如下
最后我们取他们的绝对值, 分别 187 和 1,由结果可以很明显的看到了他们的区别,梯度值越大,说明该像素越有可能是边缘!
源码
Shader "Unlit/EdgeDetection"
{
Properties
{
[PerRendererData] _MainTex ("Texture", 2D) = "white" { }
_EdgeOnly ("Edge Only", Float) = 1.0
_EdgeColor ("EdgeColor", Color) = (0, 0, 0, 1)
_BackgroundColor ("BackgroundColor", Color) = (1, 1, 1, 1)
}
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "IgnoreProjector" = "True" "CanUseSpriteAtlas" = "True" "PreviewType" = "Plane" }
ZWrite Off Blend SrcAlpha OneMinusSrcAlpha Cull Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct appdata
{
float4 vertex: POSITION;
float4 color: COLOR;
float2 uv: TEXCOORD0;
};
struct v2f
{
half2 uv[9]: TEXCOORD0;
float4 vertex: SV_POSITION;
float4 color: COLOR;
};
sampler2D _MainTex;
half4 _MainTex_TexelSize;//用于访问纹理中对应的每个纹素的大小
fixed _EdgeOnly;
fixed4 _EdgeColor;
fixed4 _BackgroundColor;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.color = v.color;
half2 uv = v.uv;
//偏移像素点
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);//原UV坐标
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;
}
//计算算子梯度值
half Sobel(v2f i)
{
//横向Sobel算子
const half Gx[9] = {
- 1, -2, -1,
0, 0, 0,
1, 2, 1
};
//纵向Sobel算子
const half Gy[9] = {
- 1, 0, 1,
- 2, 0, 2,
- 1, 0, 1
};
half edgeX = 0;
half edgeY = 0;
for (int it = 0; it < 9; it ++)
{
//图像采样后置灰
half texColor = Luminance(tex2D(_MainTex, i.uv[it]));
//统计灰度图不同算子下梯度值
edgeX += texColor * Gx[it];
edgeY += texColor * Gy[it];
}
half edge = 1 - abs(edgeX) - abs(edgeY);
//梯度值越大,越可能是边缘
return edge;
}
fixed4 frag(v2f i): SV_Target
{
half edge = Sobel(i);
fixed4 col = tex2D(_MainTex, i.uv[4]);
//图片原色+线框描边
fixed4 withEdgeColor = lerp(_EdgeColor, col, edge);
//背景色+线框描边
fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);
fixed4 FinalResult = lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly) ;
FinalResult.a = i.color.a * col.a;
return FinalResult;
}
ENDCG
}
}
}