前言
周末通关了一个小游戏,流程很短,6个小时左右就通关,但是游戏的画风,视角,玩法都比较新奇,对了,游戏的名字也很奇特《12 Is Better Than 6》(12比6好是有什么梗吗?)。
游戏采用的是俯视角,人物在活着的时候基本只能看到个帽子,玩法类似很早玩的《夺宝奇兵》《盟军敢死队》《1937特种兵》,但是游戏是西部牛仔的背景,画风采用的黑白素描的风格。
游戏玩法虽然简单,主要是射击+暗杀,但是非常硬核。前期有左轮手枪的时候,需要开一枪,按一下右键转动左轮再开下一枪,上弹也是需要手动按。有几关过了很久才过去。后期拿到双左轮之后难度就大大降低啦。
游戏通关之后,留给我印象最深刻的还是游戏本身的渲染风格,这种类似铅笔描绘的风格看起来也挺舒服的。也不由得让我想尝试一下类似的效果,今天主要来玩一下基于后处理的边缘检测效果。
简介
边缘检测,在图像处理,计算机视觉中都是很重要的一个概念。在图像处理领域,由于输入只有一张图片,所以一般是将图片转成灰度,然后判断图片像素间的梯度来判断图像中的边界的;而在3D渲染领域,除了场景渲染结果图片外,我们还可以得到场景的深度以及法线等信息,让我们可以得到更加精确的边缘检测结果。边缘检测在渲染中虽然可能没有图像处理领域那样出名,但是也是可以用来实现一些特殊渲染风格,渲染效果,以及后处理AA等功能。
边缘检测的方式是使用一些边缘检测的算子对图像进行卷积操作,和之前玩过的高斯模糊,双边滤波类似,都是通过当前像素点及其周围像素点按照一定的规则权重计算得到结果。
本文的效果实际上也算是一种描边效果的实现,关于其他类型的描边效果,可以参考本人之前的blog-《Unity Shader-描边效果》。
基于图像的边缘检测
首先,我们看一下基于图像的边缘检测。也就是只在后处理阶段使用边缘检测算子针对图像的灰度计算梯度。我们能看到图像的边界,在于图像中的亮度等因素有明显差异,我们可以用梯度来表示这种边界的权重,梯度越大,边缘就越明显。在图像处理领域已经有了很成熟的边缘检测卷积方式,比如Roberts算子和Sobel算子。主要的思想就是使用横竖两个方向的两个矩阵对原图进行卷积运算,得到两个方向的亮度的梯度,两个算子如下(与常见的图像处理中定义可能稍微有一些区别,主要在于行矩阵和列矩阵的差异,表现的结果是一样的):
我们在Shader中同时包含两种边缘检测的算子,对比效果。Shader代码如下:
/********************************************************************
FileName: EdgeEffect.shader
Description: 后处理描边效果,使用Roberts和Sobel算子,可调强度&检测距离
history: 11:11:2018 by puppet_master
https://blog.youkuaiyun.com/puppet_master
*********************************************************************/
Shader "Edge/EdgeEffect"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
CGINCLUDE
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uvRoberts[5] : TEXCOORD0;
float2 uvSobel[9] : TEXCOORD5;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_TexelSize;
fixed4 _EdgeColor;
fixed4 _NonEdgeColor;
float _EdgePower;
float _SampleRange;
float Sobel(v2f i)
{
const float Gx[9] =
{
-1, -2, -1,
0, 0, 0,
1, 2, 1
};
const float Gy[9] =
{
1, 0, -1,
2, 0, -2,
1, 0, -1
};
float edgex, ed