边缘检测
在屏幕后处理的Shader中 需要设置深度测试、剔除、深度写入 - ZTest Always 打开深度测试 - Cull Off 关闭剔除 - ZWrite Off 关闭深度写入 主要是避免它"挡住"后面的渲染物体
1.边缘检测是什么?
边缘检测效果,
是一种用于突出图像中的边缘,使物体的轮廓更加明显的图像处理技术
边缘检测的主要目的是找到图像中亮度变化显著的区域,这些区域通常对应于物体的边界
边缘检测相当于利用 Shader 代码自动给屏幕图像进行描边处理
## 2.基本原理
计算每个像素的灰度值,用灰度值结合卷积核进行卷积运算,得到该像素的梯度值
梯度值越大越靠近边界,越趋近于描边颜色
梯度值越小表明不是边界位置,越趋近于原始颜色
所以基本步骤如下:
- 得到当前像素以及其上下左右、左上左下、右上右下共9个像素的灰度值
- 用这9个灰度值和Sobel算子进行卷积计算得到梯度值G = abs(Gx) + abs(Gy)
- 最终颜色= lerp(原始颜色,描边颜色,梯度值)
3.一些关键的知识点
灰度值、卷积、卷积核、梯度值
1.)灰度值
由于人眼对不同颜色的敏感度不同,所以在计算平均值时不会直接使用算数平均(R+G+B)/3
在图形学中我们一般使用加权平均法来计算灰度值
例如:
下面是基于Rec. 709标准计算的灰度值(高清电视和许多数字图像格式中常用的标准)
灰度值L = 0.2126R + 0.7152G + 0.0722*B
2.)卷积
卷积是一种数学计算方式
我们首先通过一个比喻来理解卷积在边缘检测中的作用
它就像是要用一个放大镜(卷积核)在图片上移动,放大镜(卷积核)的作用是帮助我们看到图
片上的细微变化。当我们用这个放大镜(卷积核)扫描整张图片时,它能帮助我们发现图片上哪
些地方颜色变化突然,这些突然变化的地方往往就是物体的边缘了
3.)卷积核
从卷积的计算方式我们可以得知,其中卷积核(也被称为边缘检测因子) 是非常重要的一个元素,在图形学中,有三种常用的卷积核(边缘检测因子) 他们分别是: - Roberts 算子:由拉里·罗伯茨(Larry Roberts)于1965年提出 - Prewitt 算子:由约翰·普雷维特(John Prewitt)于1970年提出 - Sobel 算子:由欧文·索贝尔(Irwin Sobel)于1968年提出 他们各有千秋,但是在图形学中最常用的还是Sobel算子 因为它更适合高精度的边缘检测 
4.)梯度值
我们可以看到三种算子都包含了两个方向的卷积核
他们分别用来检测水平和竖直方向上的边缘信息
在边缘检测的卷积计算时,只需要对每个像素进行两次卷积计算即可
这样就可以得到两个方向的梯度值Gx 和Gy
而该像素的整体梯度值G = abs(Gx) + abs(Gy)
4.如何得到当前像素周围8个像素位置
Unity 提供给我们用于访问纹理对应的每个纹素(像素)的大小的变量 float4 纹理名_TexelSize
是一种类似于 纹理名_ST 的一种变量
其中xyzw的含义:(假设纹理宽高为1024 * 768) - x : 1 / 纹理宽度 = 1/1024 - y : 1 / 纹理高度 = 1/768 - y : 纹理宽度 = 1024 - y : 纹理高度 = 768
基于这个变量,我们可以进行uv坐标的偏移计算
在顶点着色器函数或者片元着色器函数中计算都行
但是建议在顶点着色器函数中计算,可以节约计算量
片元着色器中直接使用插值的结果也不会影响纹理坐标的计算结果

5.具体实现
1.)主要步骤
a.Shader部分
- 声明属性,进行属性映射
- 主纹理 _MainTex
- 描边用的颜色 _EgdeColor
- 注意:映射时使用内置的纹素变量
- 屏幕后处理标配
- ZTest Always
- Cull off
- ZWrite off
- 结构体相关
- 顶点
- uv数组 用于存储9个像素点的坐标
- 顶点着色器
- 顶点坐标转化
- 用uv坐标数组装载9个像素uv的坐标
- 片元着色器
- 利用卷积获取梯度值(可以声明一个Sobel算子计算函数,和一个灰度值计算函数)
- 利用梯度值在源颜色和边缘颜色之间进行插值得到最终的颜色
- FallBack off
1 | Shader "Unlit/EdgeDetection" |
b.C# 部分
- 继承屏幕后处理基类
- 声明边缘颜色变量,用于控制效果变化
- 重写UpdataProperty方法,用于设置材质球的颜色
1
2
3
4
5
6
7
8
9
10
11
12public class EdgeDetection : PostEffectBase
{
public Color edgeColor = Color.white;
protected override void UpadateProerty()
{
if (material)
{
material.SetColor("_EdgeColor", edgeColor);
}
}
}
6.纯色背景功能
1.)什么是纯色背景
在边缘描边时,有时只想保留描边的边缘线
不想要显示原图的背景色
比如:把整个背景变成白色、黑色、等等自定义颜色
而抛弃原本图片的颜色信息
效果就像是一张描边的图片
2.)具体实现
- 在描边的基础上进行修改
- 属性声明与映射
- 添加背景颜色程度的变量 _BackgroudExtent (0表示保留原始颜色,1表示纯色)
- 添加自定义背景颜色 _BackgroundColor (定义用于替换原始颜色的颜色)
- 修改片源着色器
- 利用插值运算,在 原始颜色 和 纯色背景颜色描边 之间通过背景颜色程度的变量 进行插值
- 在C#代码中
- 添加背景颜色程度的变量
- 添加背景颜色