基于颜色的边缘检测的主要优点在于无需额外信息,只需要场景图本身,但是也有一定的缺点,如果两个对象的颜色差异不明显,即使有边界也检测不出来,可能出现一些瑕疵。如果我们想要纯正的边缘的效果的话,就需要用另一种更加准确的边缘检测方式。3D渲染相对于普通的二维图像处理的优势就在于我们还可以得到一些其他的信息,比如场景的深度,场景的法线,通过这两者,我们可以在当前采样点的周围像素点计算法线的差异以及深度的差异,如果超过一定的阈值,就认为是边界。
Roberts算子的卷积核
-1 | 0 |
0 | 1 |
0 | -1 |
1 | 0 |
本质上是计算左上角和右下角的差值,乘以右上角和左下角的差值,作为评估边缘的依据。
取对角方向的深度和法线值,比较他们之间的差值,如果超过某个阀值(可自由控制阀值),就认为他们之间存在一条边
Shader "Edge/EdgeEffectDepthNormal"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
CGINCLUDE
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv[5] : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_TexelSize;
sampler2D _CameraDepthNormalsTexture;
fixed4 _EdgeColor;
fixed4 _NonEdgeColor;
float _SampleRange;
float _NormalDiffThreshold;
float _DepthDiffThreshold;
float CheckEdge(fixed4 s1, fixed4 s2)
{
float2 normalDiff = abs(s1.xy - s2.xy);
float normalEdgeVal = (normalDiff.x + normalDiff.y) < _NormalDiffThreshold;
float s1Depth = DecodeFloatRG(s1.zw);
float s2Depth = DecodeFloatRG(s2.zw);
float depthEdgeVal = abs(s1Depth - s2Depth) < 0.1 * s1Depth * _DepthDiffThreshold;
return depthEdgeVal * normalEdgeVal;
}
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv[0] = v.uv + float2(-1, -1) * _MainTex_TexelSize * _SampleRange;
o.uv[1] = v.uv + float2( 1, -1) * _MainTex_TexelSize * _SampleRange;
o.uv[2] = v.uv + float2(-1, 1) * _MainTex_TexelSize * _SampleRange;
o.uv[3] = v.uv + float2( 1, 1) * _MainTex_TexelSize * _SampleRange;
o.uv[4] = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv[4]);
fixed4 s1 = tex2D(_CameraDepthNormalsTexture, i.uv[0]);
fixed4 s2 = tex2D(_CameraDepthNormalsTexture, i.uv[1]);
fixed4 s3 = tex2D(_CameraDepthNormalsTexture, i.uv[2]);
fixed4 s4 = tex2D(_CameraDepthNormalsTexture, i.uv[3]);
float result = 1.0;
result *= CheckEdge(s1, s4);
result *= CheckEdge(s2, s3);
col.rgb = lerp(_EdgeColor, _NonEdgeColor, result);
return col;
}
ENDCG
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
//Pass 0 Roberts Operator
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}