边缘检测效果的shader
【特点【:支持调整力度,可选两种卷积算子的边缘检测
【原理【:使用卷积的方法,每次检测像素时都计算他垂直或水平方向的周围像素。根据实现定义好的权重,判定是否存在水平或垂直的边缘像素。如果值足够大小则认为是边缘像素,就进行特殊处理。
【笑狗图】:
Shader "Custom/sobel" {
Properties {
_MainTex ("贴图", 2D) = "white" {}
_Size("力度", range(0.00,2000)) = 40
_IsSobel("Sobel or Roberts or Canny", range(0.0,3.0)) = 0
}
SubShader {
pass{
Tags{"LightMode"="ForwardBase" }
Cull off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Size;
float _IsSobel;
sampler2D _MainTex;
//用于配合TRANSFORM_TEX
float4 _MainTex_ST;
struct v2f {
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
};
//顶点着色器
v2f vert (appdata_full v) {
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
//将模型顶点的uv和Tiling、Offset进行运算求得最终的uv
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
//边缘检测因子运算
float4 Get_Sobel(sampler2D mainTex, float2 uv){
//Sobel算子 x
float g_x[9] =
{-1, 0, 1,
-2, 0, 2,
-1, 0, 1};
//Sobel算子 y
float g_y[9] =
{1, 2, 1,
0, 0, 0,
-1,-2,-1};
//要输出的颜色
float GX = 0;
float GY = 0;
//灰度因子
float3 lum = float3(0.2125, 0.7154, 0.0721);
//遍历3*3 算子计算所有的像素]
int gCount = 0;
//0 1 2 该矩阵处理顺序,4是当前像素点
//3 4 5
//6 7 8
for (int i = -1; i < 2; i++)
{
for (int j = -1; j < 2; j++)
{
//获取包含当前像素与其周围在内的9个像素uv位置, uv+ (可调节的偏移)
float2 new_uv = float2(uv.x + j / _Size , uv.y + i / _Size);
//根据uv定位获取该uv位置的像素信息
float4 color = tex2D(mainTex, new_uv);
// luminance点积,求出亮度值(黑白图)
float _g = dot(color.rgb, lum);
//当前亮度值 分别乘以x、y的算子,得到边缘数据
GX += (_g * g_y[gCount]);
GY += (_g * g_x[gCount]);
gCount += 1;
}
}
//length的内部算法就是灰度公式的算法,欧几里得长度
float4 color = length(float2(GX, GY));
//过滤器
if(color.r < 0.5){
//区域区域涂黑
color = 0;
//透明测试,其余区域不显示
//clip(color.a - 1);
}
return color;
}
// Roberts算子
float4 Get_Roberts(sampler2D mainTex, float2 uv){
//Sobel算子 x
float g_x[4] =
{1, 0,
0, -1};
//Sobel算子 y
float g_y[4] =
{0, 1,
-1, 0};
//要输出的颜色
float GX = 0;
float GY = 0;
//灰度因子
float3 lum = float3(0.2125, 0.7154, 0.0721);
//遍历3*3 算子计算所有的像素]
int gCount = 0;
//0 1 2 该矩阵处理顺序,4是当前像素点
//3 4 5
//6 7 8
for (int i = -1; i < 1; i++)
{
for (int j = -1; j < 1; j++)
{
//获取包含当前像素与其周围在内的9个像素uv位置, uv+ (可调节的偏移)
float2 new_uv = float2(uv.x + j / _Size , uv.y + i / _Size);
//根据uv定位获取该uv位置的像素信息
float4 color = tex2D(mainTex, new_uv);
// luminance点积,求出亮度值(黑白图)
float _g = dot(color.rgb, lum);
//当前亮度值 分别乘以x、y的算子,得到边缘数据
GX += (_g * g_y[gCount]);
GY += (_g * g_x[gCount]);
gCount += 1;
}
}
//length的内部算法就是灰度公式的算法,欧几里得长度
float4 color = length(float2(GX, GY));
//过滤器
if(color.r < 0.5){
color = 0;
}
return color;
}
// Canny算子
float4 Get_Canny(sampler2D mainTex, float2 uv){
//Sobel算子 x
float g_x[4] =
{-1, 1,
-1, 1};
//Sobel算子 y
float g_y[4] =
{1, 1,
-1, -1};
//要输出的颜色
float GX = 0;
float GY = 0;
//灰度因子
float3 lum = float3(0.2125, 0.7154, 0.0721);
//遍历3*3 算子计算所有的像素]
int gCount = 0;
//0 1 2 该矩阵处理顺序,4是当前像素点
//3 4 5
//6 7 8
for (int i = -1; i < 1; i++)
{
for (int j = -1; j < 1; j++)
{
//获取包含当前像素与其周围在内的9个像素uv位置, uv+ (可调节的偏移)
float2 new_uv = float2(uv.x + j / _Size , uv.y + i / _Size);
//根据uv定位获取该uv位置的像素信息
float4 color = tex2D(mainTex, new_uv);
// luminance点积,求出亮度值(黑白图)
float _g = dot(color.rgb, lum);
//当前亮度值 分别乘以x、y的算子,得到边缘数据
GX += (_g * g_y[gCount]);
GY += (_g * g_x[gCount]);
gCount += 1;
}
}
//length的内部算法就是灰度公式的算法,欧几里得长度
float4 color = length(float2(GX, GY));
//过滤器
if(color.r < 0.5){
color = 0;
}
return color;
}
//片元着色器
float4 frag(v2f o):SV_Target
{
fixed4 outColor;
if(_IsSobel > 0 && _IsSobel < 1){
outColor = Get_Sobel(_MainTex, o.uv);
}
if(_IsSobel > 1 && _IsSobel < 2){
outColor = Get_Roberts(_MainTex, o.uv);
}
if(_IsSobel > 2 && _IsSobel < 3){
outColor = Get_Canny(_MainTex, o.uv);
}
return outColor;
}
ENDCG
}
}
}