您的位置:首页 > 移动开发 > Unity3D

Unity Shader入门精要 阅读笔记十七

2018-03-05 18:51 330 查看

前言

这里讲边缘检测这一节中的笔记内容。边缘检测这里更多的用到了图像学的一些基本内容,包括卷积核,索贝尔算子等等一些内容。整个流程还是比较清晰的。

边缘检测算法

获取到屏幕渲染的颜色缓冲之后,将屏幕中的像素用索贝尔算子进行卷积操作,卷积的结果就是当前像素的一个导数。这里说导数其实可能比较难懂,说变化程度应该会好理解一些。如果这里颜色突变的厉害那么他的变化程度就会大一些,如果颜色变化比较平坦,那么变换程度就会小一些,计算的结果也会小一些。

这里需要将一些索贝尔算子和图像卷积操作。

⎡⎣⎢−101−202−101⎤⎦⎥=Gx[−1−2−1000121]=Gx

⎡⎣⎢−1−2−1000121⎤⎦⎥=Gy[−101−202−101]=Gy

大家在看书写代码的时候可能会有疑问,正文中写的Gx有值的部分明明是在列上面,第二列为0,其他列有值,Gy为第二行为0.但是在代码中Gx与Gy似乎反了。

在图像处理(冈萨雷斯)这本书里是这样描述Gx与Gy的:

算子与像素相乘,第三行与第一行间的差距接近与x方向上的微分,第三列与第一列间的差接近y方向的微分。(P107,Edition 2);

同时在编码中还有一个问题,不是说卷积操作的时候需要先将算子转180度,然后再对于相乘相加吗?怎么编码的时候又直接对应相乘相机没有旋转了?

答:(图像处理(冈萨雷斯)P92,Edi 2)中提到,线性空间滤波与频率域中的卷积概念类似,因此线性空间滤波也称为“掩膜与图像的卷积”。“滤波掩膜”也被称为“卷积模板”,“卷积核”。

也就是说图像与算子的卷积实际上是一种线性空间滤波,而这种滤波的表达形式就是对应相乘相加。

这里给出mxn大小的线性滤波掩膜的表达式:

g(x,y)=∑as=−a∑bt=−bw(s,t)f(x+s,y+t)g(x,y)=∑s=−aa∑t=−bbw(s,t)f(x+s,y+t)

看到这里应该知道为什么要这样来计算了吧。

边缘检测

边缘检测代码上一波:

Shader "Custom/8/8.1.2" {//边缘检测
Properties{
_MainTex("Main Texture",2D) = "white"{}
_EdgeColor("Edge Color",color) = (1,1,1,1)
_BackgroundColor("Background",Color) = (1,1,1,1)
_EdgeOnly("edgeOnly",float) = 1
}
SubShader{

Pass{

ZTest Always
ZWrite Off
Cull Off

CGPROGRAM

#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

sampler _MainTex;
float4 _MainTex_TexelSize;//纹素
float4 _EdgeColor;
float4 _BackgroundColor;
float _EdgeOnly;

struct v2f {
float4 pos : SV_POSITION;
float2 uv[9] : TEXCOORD0;
};

fixed luminance(fixed3 color)
{
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
half sobel(v2f i) {

//const half Gx[9] = { -1,-2,-1,
//  0,0,0,
//  1,2,1 };
//const half Gy[9] = { -1,0,1,
//  -2,0,2,
//  -1,0,1 };

const half Gx[9] = { -1,-2,-1,
0,0,0,
1,2,1 };
const half Gy[9] = { -1,0,1,
-2,0,2,
-1,0,1 };
float4 texColor;
float edgex = 0;
float edgey = 0;
for (int it = 0; it < 9; it++)
{
texColor = luminance(tex2D(_MainTex, i.uv[it]));
edgex += texColor * Gx[it];
edgey += texColor * Gy[it];
}
return 1 - abs(edgex) - abs(edgey);
}

v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//求纹素
float2 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;
}
fixed4 frag(v2f i) :SV_Target{
half edge = sobel(i);
fixed4 withEdgeColor = lerp(_EdgeColor, tex2D(_MainTex, i.uv[4]), edge);
fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);
return lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly);
}
ENDCG
}
}
FallBack Off
}


shader代码没有难的地方,唯一需要说的是shader中要避免if和循环语句。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: