方法其实都差不多,就是用两个过滤器,分别处理两个分量

Sobel算子

先说Sobel算子
 
GX为水平过滤器,GY为垂直过滤器,垂直过滤器就是水平过滤器旋转90度。
过滤器为3x3的矩阵,将与图像作平面卷积。
如果不存在边则两个点颜色很接近,过滤器返回一个较小的值,否则就可判断出边缘的存在。
当前点为中间点

具体计算如下:

求出图像的每一个像素的横向及纵向灰度值通过以下公式结合,来计算该点灰度的大小
 

本shdaer将G值作为颜色输出

Roberts算子

Roberts算子与之相似
过滤器是2x2的矩阵
过滤器如下:
 
当前点为左上角的点
具体计算如下:

Canny算子

过滤器是2x2的矩阵

以sobel为例:
看看shader的实现

在frag函数中

float3 lum = float3(0.2125,0.7154,0.0721);
转化为luminance亮度值的变量

获取当前点的周围的点,并与luminance点积,求出亮度值(黑白图)
float mc00 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(1,1)/_Size).rgb, lum);
float mc10 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(0,1)/_Size).rgb, lum);
float mc20 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(-1,1)/_Size).rgb, lum);
。。。。。。。        
由于CG函数tex2DSize函数(获取图片长宽的像素数)在unity中不能用,我也不知道用什么函数来替代它,就弄了个外部变量_Size方便调节。
如果有什么函数能代替tex2DSize函数各位看官一定要告诉我。

根据过滤器矩阵求出GX水平和GY垂直的灰度值
float GX = -1 * mc00 + mc20 + -2 * mc01 + 2 * mc21 - mc02 + mc22;

float GY = mc00 + 2 * mc10 + mc20 - mc02 - 2 * mc12 - mc22;

G = sqrt(GX*GX+GY*GY);
标准灰度公式
G = abs(GX)+abs(GY);

近似灰度公式

c = length(float2(GX,GY));
length的内部算法就是灰度公式的算法,欧几里得长度

float length(float3 v)
{
  return sqrt(dot(v,v));
}

c即是最终输出

让我们看看效果:

可以看见对于这种简单的卡通,三种算法都非常清晰,sobel和roberts稍好一点。

再看看复杂一些的图片

Roberts的纹路非常清晰,有一些噪声

Sobel比Roberts还要清晰,噪声相对少些

canny已经没法看了,噪声太多,边缘判断的不清楚

综上,从结果来看,sobel算子的实现效果最好

下面给出sobel的shader:

Shader "Custom/sobel" {
Properties {
_MainTex ("MainTex", 2D) = "white" {}
_Size("Size", range(1,2048)) = 256//size }
SubShader {
pass{
Tags{"LightMode"="ForwardBase" }
Cull off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc" float _Size;
sampler2D _MainTex;
float4 _MainTex_ST;
struct v2f {
float4 pos:SV_POSITION;
float2 uv_MainTex:TEXCOORD0; }; v2f vert (appdata_full v) {
v2f o;
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
o.uv_MainTex = TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
float4 frag(v2f i):COLOR
{
float3 lum = float3(0.2125,0.7154,0.0721);//转化为luminance亮度值
//获取当前点的周围的点
//并与luminance点积,求出亮度值(黑白图)
float mc00 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(1,1)/_Size).rgb, lum);
float mc10 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(0,1)/_Size).rgb, lum);
float mc20 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(-1,1)/_Size).rgb, lum);
float mc01 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(1,0)/_Size).rgb, lum);
float mc11mc = dot(tex2D (_MainTex, i.uv_MainTex).rgb, lum);
float mc21 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(-1,0)/_Size).rgb, lum);
float mc02 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(1,-1)/_Size).rgb, lum);
float mc12 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(0,-1)/_Size).rgb, lum);
float mc22 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(-1,-1)/_Size).rgb, lum);
//根据过滤器矩阵求出GX水平和GY垂直的灰度值
float GX = -1 * mc00 + mc20 + -2 * mc01 + 2 * mc21 - mc02 + mc22;
float GY = mc00 + 2 * mc10 + mc20 - mc02 - 2 * mc12 - mc22;
// float G = sqrt(GX*GX+GY*GY);//标准灰度公式
float G = abs(GX)+abs(GY);//近似灰度公式
// float th = atan(GY/GX);//灰度方向
float4 c = 0;
// c = G>th?1:0;
// c = G/th*2;
c = length(float2(GX,GY));//length的内部算法就是灰度公式的算法,欧几里得长度 return c;
}
ENDCG
}// }
}

-----------------------by  wolf96

unity3d shader之Roberts,Sobel,Canny 三种边缘检测方法的更多相关文章

  1. 于Unity3D动态创建对象和创建Prefab三种方式的原型对象

    于Unity3D动态创建对象和创建Prefab三种方式的原型对象 u3d在动态创建的对象,需要使用prefab 和创建时 MonoBehaviour.Instantiate( GameObject o ...

  2. javase-常用三种遍历方法

    javase-常用三种遍历方法 import java.util.ArrayList; import java.util.Iterator; import java.util.List; public ...

  3. JS面向对象(3) -- Object类,静态属性,闭包,私有属性, call和apply的使用,继承的三种实现方法

    相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...

  4. Java中Map的三种遍历方法

    Map的三种遍历方法: 1. 使用keySet遍历,while循环: 2. 使用entrySet遍历,while循环: 3. 使用for循环遍历.   告诉您们一个小秘密: (下↓面是测试代码,最爱看 ...

  5. Jquery中each的三种遍历方法

    Jquery中each的三种遍历方法 $.post("urladdr", { "data" : "data" }, function(dat ...

  6. spring与mybatis三种整合方法

    spring与mybatis三种整合方法 本文主要介绍Spring与Mybatis三种常用整合方法,需要的整合架包是mybatis-spring.jar,可通过链接 http://code.googl ...

  7. C#使用DataSet Datatable更新数据库的三种实现方法

    本文以实例形式讲述了使用DataSet Datatable更新数据库的三种实现方法,包括CommandBuilder 方法.DataAdapter 更新数据源以及使用sql语句更新.分享给大家供大家参 ...

  8. struts2拦截器interceptor的三种配置方法

    1.struts2拦截器interceptor的三种配置方法 方法1. 普通配置法 <struts> <package name="struts2" extend ...

  9. selenium webdriver三种等待方法

    webdriver三种等待方法 1.使用WebDriverWait from selenium import webdriverfrom selenium.webdriver.common.by im ...

随机推荐

  1. Singleton设计模式 分类: 设计模式 2014-12-03 17:54 59人阅读 评论(0) 收藏

    实现方法: public class SingleTon<T> where T : class, new() {     protected SingleTon() { }     pri ...

  2. MVC 中的Areas支持

    在ASP.NET MVC 2中对于Area功能的增强,这样的增强是如何在同一个项目中更好地组织应用程序的? ASP.NET MVC 1.0时,如果我们要在一个项目中做自己网站的后台应用,而又保持URL ...

  3. c语言学习之基础知识点介绍(十):内存空间模型、地址解释及指针变量

    一.内存 /* 内存: 存在内存里的. 内存分了N多个小空间,每个小空间1个字节 每个小空间有它自己的地址.每个地址之间差1 int类型占用4个字节,等于占了4个空间(有4个地址),不需要记住4个地址 ...

  4. 深入理解shared pool共享池之library cache系列一

    结论 1,oradebug dump library_cache不同级别dump的library cache内容及粒度会有所区别,具体见测试开始部分2,本文测示基于oradebug dump libr ...

  5. Access restriction:The type JPEGCodec is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar

    解决方法: Project -> Properties -> libraries, 先remove掉JRE System Library,然后再Add Library重新加入. ===== ...

  6. 国内国外最好的java开发论坛及站点 [转]

    国内: www.chinajavaworld.com -论坛人很多,高手也多,不过好像都在潜水    www.cn-java.com -也很不错,文章很好,但是就是商业性浓了点.    www.chi ...

  7. ExecuteNonQuery()返回值

    查询某个表中是否有数据的时候,我用了ExecuteNonQuery(),并通过判断值是否大于0来判断数据的存在与否.结果与我所设想的很不一致,调试时才发现,其执行后返回的结果是-1,对此我很是不理解, ...

  8. 2016022605 - redis订阅发布

    Redis的实现消息队列功能,消息发布者发送的消息,消息接收者接收发布者的消息.由该消息传送的链路被称为通道.在Redis客户端可以订阅任何数目的通道. 案例:给出一个客户端订阅一个通道名为redis ...

  9. 2016021801 - Java内存区域学习笔记

    根据<深入理解java虚拟机>学习归纳整理学习笔记 程序计数器 用途:当前线程的字节码文件的行号指示器.(当前机场负责控制飞机降落的空管员:当前线程表示当前机场, 所执行的字节码等同于被等 ...

  10. Sublime text 3 快键方式汇总 及 主题应用

    Sublime Text 3 快捷键汇总 Sublime Text 3是款非常实用代码编辑神器,但是想要用任何一款软件,掌握一些快捷键还是很有必要的. 选择类 Ctrl+D 选中光标所占的文本,继续操 ...