基于Unity實(shí)現(xiàn)2D邊緣檢測(cè)
一、ShaderLab
1.Alpha值邊緣檢測(cè)

根據(jù)圖片的Alpha值邊緣判定,向內(nèi)擴(kuò)一段距離做邊緣,顏色設(shè)置未描邊顏色;
片元著色階段,向上下左右四個(gè)方向做檢測(cè),有一個(gè)點(diǎn)的透明度為0,判定為邊緣;
Shader "2DOutline"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_LineWidth("Width",Range(0,0.4)) = 1.0
_LineColor("LineColor",color) = (1,1,1,1)
_Intensity("Intensity",Range(1,10)) = 1.0
}
SubShader
{
Tags { "RenderType" = "Opaque" "Queue" = "Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _LineWidth;
float4 _LineColor;
fixed _Intensity;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
// 采樣周圍4個(gè)點(diǎn)
float2 up_uv = i.uv + float2(0, 1) * _LineWidth * 1 / 10 * _MainTex_ST.xy;
float2 down_uv = i.uv + float2(0,-1) * _LineWidth * 1 / 10 * _MainTex_ST.xy;
float2 left_uv = i.uv + float2(-1,0) * _LineWidth * 1 / 10 * _MainTex_ST.xy;
float2 right_uv = i.uv + float2(1,0) * _LineWidth * 1 / 10 * _MainTex_ST.xy;
// 如果有一個(gè)點(diǎn)透明度為0 說(shuō)明是邊緣
float w = tex2D(_MainTex,up_uv).a * tex2D(_MainTex,down_uv).a * tex2D(_MainTex,left_uv).a * tex2D(_MainTex,right_uv).a;
if (w == 0) {
col.rgb = lerp(_LineColor * _Intensity, col.rgb, w);
}
return col;
}
ENDCG
}
}
}
如果圖片內(nèi)容恰好鋪滿整張圖,沒(méi)有alpha值,方法不適用;下圖底部邊緣消失了;

2.卷積邊緣檢測(cè)
在屏幕后處理階段,使用卷積做邊緣檢測(cè);
卷積:根據(jù)像素周圍八個(gè)方向的像素的計(jì)算出新的像素值;
邊緣檢測(cè)卷積算子,都包含水平和豎直兩個(gè)方向的卷積核;
梯度公式:G = sqrt(Gx*Gx + Gy*Gy);
考慮性能問(wèn)題,使用:G = |Gx|+|Gy|;

頂點(diǎn)著色器計(jì)算卷積紋理采樣坐標(biāo),減少計(jì)算量(片元數(shù)量更多);
片元著色階段Sobel卷積計(jì)算,插值獲得片元像素顏色;
Sobel計(jì)算結(jié)果和梯度Gradient比較,大于梯度和EdgeColor做插值;
屏幕后效調(diào)用OnRenderImage接口;
Shader "EdgeDetection"
{
Properties{
_MainTex("Base (RGB)", 2D) = "white" {}
_EdgeColor("Edge Color", Color) = (0, 0, 0, 1)
//卷積梯度
_Gradient("Gradient",float) =0.0
}
SubShader{
Pass
{
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
uniform half4 _MainTex_TexelSize;
//fixed _EdgeOnly;
fixed4 _EdgeColor;
//fixed4 _BackgroundColor;
fixed _Gradient;
struct v2f {
float4 pos : SV_POSITION;
half2 uv[9] : TEXCOORD0;
};
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 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;
}
fixed luminance(fixed4 color) {
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
half Sobel(v2f i) {
const half Gx[9] = { -1, 0, 1,
-2, 0, 2,
-1, 0, 1};
const half Gy[9] = { -1, -2, -1,
0, 0, 0,
1, 2, 1};
half texColor;
half edgeX = 0;
half edgeY = 0;
for (int it = 0; it < 9; it++) {
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]);
if(edge> _Gradient)
col = lerp(_EdgeColor, tex2D(_MainTex, i.uv[4]), edge);
return col;
}
ENDCG
}
}
FallBack Off
}

二、ShaderGraph
抓取圖片緩沖,上下左右四個(gè)方位平移,乘以描邊顏色;
四張圖合并,減去原圖范圍的像素,只剩邊緣;
最后將原圖和邊緣合并(可插值使邊緣柔和);
升級(jí)項(xiàng)目到URP,修改projectsetting-graphic-pielinesettings;
導(dǎo)入ShaderGraph包,開始拖拖拽拽,真的香,效果好,速度快,思路清晰;


到此這篇關(guān)于基于Unity實(shí)現(xiàn)2D邊緣檢測(cè)的文章就介紹到這了,更多相關(guān)Unity邊緣檢測(cè)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#實(shí)現(xiàn)簡(jiǎn)單的Http請(qǐng)求實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)簡(jiǎn)單的Http請(qǐng)求的方法,以實(shí)例形式較為詳細(xì)的分析了C#實(shí)現(xiàn)Http請(qǐng)求的具體方法,需要的朋友可以參考下2015-01-01
C#中通過(guò)API實(shí)現(xiàn)的打印類 實(shí)例代碼
這篇文章介紹了,C#中通過(guò)API實(shí)現(xiàn)的打印類 實(shí)例代碼,有需要的朋友可以參考一下2013-08-08
C#托管內(nèi)存與非托管內(nèi)存之間的轉(zhuǎn)換的實(shí)例講解
在本篇文章里小編給大家整理了關(guān)于C#托管內(nèi)存與非托管內(nèi)存之間的轉(zhuǎn)換的實(shí)例以及相關(guān)知識(shí)點(diǎn),需要的朋友們學(xué)習(xí)下。2019-08-08
Unity代碼實(shí)現(xiàn)序列幀動(dòng)畫播放器
這篇文章主要為大家詳細(xì)介紹了Unity代碼實(shí)現(xiàn)序列幀動(dòng)畫播放器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02
C#實(shí)現(xiàn)跨進(jìn)程條件變量的示例代碼
C#提供的多進(jìn)程同步對(duì)象有互斥鎖和信號(hào)量,但是并沒(méi)有條件變量,雖然信號(hào)量條件變量一定程度可以等效,但是具體的使用還是會(huì)有區(qū)別,本文提供了一種條件變量的實(shí)現(xiàn)方法,可以用于進(jìn)程間的同步控制,需要的朋友可以參考下2024-07-07
C# OpenCvSharp實(shí)現(xiàn)圖片批量改名
這篇文章主要為大家詳細(xì)介紹了C#如何結(jié)合OpenCvSharp實(shí)現(xiàn)圖片批量改名功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03
從Request.Url中獲取根網(wǎng)址的簡(jiǎn)單操作
這篇文章主要介紹了從Request.Url中獲取根網(wǎng)址的簡(jiǎn)單操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01

