unity3d shader之Roberts,Sobel,Canny 三种边缘检测方法
方法其实都差不多,就是用两个过滤器,分别处理两个分量
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 三种边缘检测方法的更多相关文章
- 于Unity3D动态创建对象和创建Prefab三种方式的原型对象
		于Unity3D动态创建对象和创建Prefab三种方式的原型对象 u3d在动态创建的对象,需要使用prefab 和创建时 MonoBehaviour.Instantiate( GameObject o ... 
- javase-常用三种遍历方法
		javase-常用三种遍历方法 import java.util.ArrayList; import java.util.Iterator; import java.util.List; public ... 
- JS面向对象(3) -- Object类,静态属性,闭包,私有属性, call和apply的使用,继承的三种实现方法
		相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ... 
- Java中Map的三种遍历方法
		Map的三种遍历方法: 1. 使用keySet遍历,while循环: 2. 使用entrySet遍历,while循环: 3. 使用for循环遍历. 告诉您们一个小秘密: (下↓面是测试代码,最爱看 ... 
- Jquery中each的三种遍历方法
		Jquery中each的三种遍历方法 $.post("urladdr", { "data" : "data" }, function(dat ... 
- spring与mybatis三种整合方法
		spring与mybatis三种整合方法 本文主要介绍Spring与Mybatis三种常用整合方法,需要的整合架包是mybatis-spring.jar,可通过链接 http://code.googl ... 
- C#使用DataSet Datatable更新数据库的三种实现方法
		本文以实例形式讲述了使用DataSet Datatable更新数据库的三种实现方法,包括CommandBuilder 方法.DataAdapter 更新数据源以及使用sql语句更新.分享给大家供大家参 ... 
- struts2拦截器interceptor的三种配置方法
		1.struts2拦截器interceptor的三种配置方法 方法1. 普通配置法 <struts> <package name="struts2" extend ... 
- selenium webdriver三种等待方法
		webdriver三种等待方法 1.使用WebDriverWait from selenium import webdriverfrom selenium.webdriver.common.by im ... 
随机推荐
- P2P之UDP穿透NAT原理
			首先先介绍一些基本概念: NAT(Network Address Translators),网络地址转换:网络地址转换是在IP地址日益缺乏的情况下产生的,它的主要目的就 ... 
- 百度,人人,新浪,腾讯等分享Js代码
			<!-- Baidu Button BEGIN --> <div id="bdshare" class=" ... 
- C# Ref 与out 的区别
			在C#中,有四种传递参数方式: 1. 传值 (value) : 无额外修饰符 2. 传址(reference) : 需修饰符Ref,传入函数的参数必须先赋值 3. 输出参数(output): 需修饰符 ... 
- IMPDP hangs, session wait “wait for unread message on broadcast channel”
			昨晚有个朋友说加班在IMPDP数据, 在导入中途突然没有了进展,挂在那里不动了,impdp 窗口也没有报错, 一直等了1个多小时,说是impdp使用了parallel,怀疑是parallel参数出了问 ... 
- 2D动态光照
			对场景内所有点发出射线, 如果射线被某条边阻挡, 则射线停留在阻挡的边上, 如果射线顺利抵达终点, 则对射线偏移-0.001, +0.001角度, 再射出2条射线, 停留在后续的阻挡边上. 把最终的射 ... 
- boost::thread boost库线程
			一.boost::thread的创建 1.线程创建方法一: boost::shared_ptr<boost::thread> writeThread_; boost::function0& ... 
- 说说http请求
			为什么做web前端要了解http标准?因为浏览器要从服务端获取网页,网页也可能将信息再提交给服务器,这其中都有http的连接.web系统既然和http链接有瓜葛,你就必须去了解它.我将从一下几个方面讲 ... 
- ES 中文分词
			一.大名鼎鼎的中文插件IK的安装配置 1. 在插件目录中建立IK的目录 mkdir $ES_HOME/plugins/analysis-ik 2. 下载IK 的类库jar 文件到IK目录 cd $ES ... 
- createjs 下雪 实例
			demo: http://output.jsbin.com/davixatona <!DOCTYPE html> <html> <head> <meta c ... 
- java 优化
			代码优化,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没用,但是, ... 
