Unreal Engine 4 手绘风滤镜(Paint Filter)即 桑原滤镜(Kuwahara Filter)教程(下)
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Unreal Engine 4 手绘风滤镜(Paint Filter)即 桑原滤镜(Kuwahara Filter)教程(下),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5919字,纯文字阅读大概需要9分钟。
内容图文
本文是《Unreal Engine 4 手绘风滤镜(Paint Filter)即 桑原滤镜(Kuwahara Filter)教程》的下半部分,上半部分请见[《Unreal Engine 4 手绘风滤镜(Paint Filter)即 桑原滤镜(Kuwahara Filter)教程(上)》]https://blog.csdn.net/ttm2d/article/details/115272892)
作者|Tommy Tran May 1 2018 | 翻译 开发游戏的老王
文章目录
选择方差最低的核
添加如下代码获得方差最低的核
// 1
float3 FinalColor = MeanAndVariance[0].rgb;
float MinimumVariance = MeanAndVariance[0].a;
// 2
for (int i = 1; i < 4; i++)
{
if (MeanAndVariance[i].a < MinimumVariance)
{
FinalColor = MeanAndVariance[i].rgb;
MinimumVariance = MeanAndVariance[i].a;
}
}
return FinalColor;
解释一下上述代码:
- 创建了两个变量保存最终的颜色和最小方差(都是用第一个核的平均值和方差作为初始值)。
- 遍历其余的三个核,如果当前核的方差小于最小方差,就意味着找到了新的FinalColor和MinimumVariance。遍历完毕以后所输出FinalColor当热就是方差最低的核的平均值了。
然后找到 Materials\PostProcess文件夹,打开PP_Kuwahara,点击应用,再回到主编辑界面就可以看到效果了。
效果看起来不错,但是如果你贴近一看,会发现一些奇怪的“块状斑”。下图中我把它们高亮标注了一下:
这是使用轴向平行核(axis-aligned kernel)产生的副作用。一种除去这种块状斑的方法就是使用改进版的滤镜,我们称之为方向性桑原滤镜(Directional Kuwahara filter)。
方向性桑原滤镜(Directional Kuwahara filter)
这种滤镜和之前的很类似,只不过它平行于像素的局部走向。下面是个核大小为3×5的方向性桑原滤镜:
注:因为我们把一个核视为一个矩阵(matrix),所以以Height x Width的形式书写它的维度,而不是像往常一样写成Width x Height。下文中我们会继续介绍矩阵的知识。
核首先要计算出像素边缘的走向,然后将整个核旋转使其平行。
我们使用索贝尔算子(Sobel)进行卷积运算来获得局部走向。如果索贝尔这个词你听起来很熟悉,八成因为它是一非常经典的边缘检测技术。既然它是一种边缘检测技术,我们能用它来获取局部走向么?我们先来了解一下索贝尔的工作原理,你就明白了。
索贝尔是如何工作的
索贝尔使用2个核
Gx用于获取水平方向的梯度;Gy用于获取垂直方向的梯度。我们以下面3×3的灰度图为例:
首先,使用中间的像素核每个核进行卷积。
译者注:对应单元格中的值相乘再相加
如果把每对值都标记到一个2D平面上,那么我们就可以将这个向量所指的方向视为像素的边缘走向
然后我们可以对这个斜度值求反正切(arc tangent 或 atan),然后就可以用这个角度对核进行旋转了。
这就是我们利用索贝尔来计算像素局部走向的方法。
获取局部走向
打开Global.usf将下列代码添加到 GetPixelAngle()
:
float GradientX = 0;
float GradientY = 0;
float SobelX[9] = {-1, -2, -1, 0, 0, 0, 1, 2, 1};
float SobelY[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
int i = 0;
注意:
GetPixelAngle()
函数的}
一定不要写!!!前面文章讲过的知识点!
解释一下每个变量的意义:
- GradientX: 保存水平方向的斜度
- GradientY: 保存垂直方向的斜度
- SobelX: 将水平索贝尔核存储到一个数组中
- SobelY: 将垂直索贝尔核存储到一个数组中
- i: 用于访问SobelX和SobelY数组中的元素
接下来使用SobelX 和 SobelY 实施卷积,代码如下:
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
// 1
float2 Offset = float2(x, y) * TexelSize;
float3 PixelColor = SceneTextureLookup(UV + Offset, 14, false).rgb;
float PixelValue = dot(PixelColor, float3(0.3,0.59,0.11));
// 2
GradientX += PixelValue * SobelX[i];
GradientY += PixelValue * SobelY[i];
i++;
}
}
解释一下代码:
- 前2行获取采样像素颜色。第3行对该颜色去色转换成一维灰度值。相较于计算每个颜色通道的灰度这样做更简单。
- 对于每一个核,让它乘以对应格子里的像素灰度,然后把值累加到相应的斜度变量中(即GradientX和GradientY),然后
i++
继续处理下一个核元素。
直接把斜度值代入atan()
函数。将下面的代码添加到for
循环的下面:
return atan(GradientY / GradientX);
现在我们拥有了获取像素角度的函数,还需要研究一下如何旋转核。一种方法就是使用矩阵(matrix)。
注:实际上你也可以使用基本的三角方法来旋转,只不过我觉得这里是一个学习矩阵的好机会,因为它们实在是好用。
什么是矩阵
矩阵就是一个数字组成的二维数组。例如,下面是一个2×3的矩阵(2行 3列):
矩阵本身看起来没什么意思。但当你用一个向量和矩阵相乘的时候,就会体会到它的强大威力了。它可以让你很方便地进行旋转缩放等操作。如何使用矩阵来实现旋转呢?
在坐标系统中,每个维度都可以用向量表示。我们称之为基向量,它们定义了坐标轴的正方向。
下面是几个不同基向量的例子。红色箭头表示X方向,绿箭头表示Y方向。
我们可以使用基向量构建一个旋转矩阵,来旋转一个向量。简单地说,就是一个包含着旋转后基向量位置的矩阵。举个例子:你有一个向量(橙色箭头所示)(1, 1)。
假设我们想顺时针将它旋转90度。首先,我们要把基向量先旋转该角度。
然后,以新的基向量位置构造一个2×2的矩阵。第一列是红色箭头的位置,第二列是绿色箭头的位置
最后,使用橙色向量和旋转矩阵进行矩阵乘法。其结果就是橙色向量的新位置。
注:你没必要知道矩阵如何相乘,因为HLSL已经内置了相关函数。
惊喜不惊喜?更牛X的是你甚至可以使用上面的矩阵将任意二维向量顺时针旋转90度。对于滤镜来讲,这就意味着我们只需为每个像素构造一次旋转矩阵,就可以为整个核所使用。
接下来该使用旋转矩阵旋转核了。
旋转核
首先,修改GetKernelMeanAndVariance()
函数使其能够接受2×2的矩阵。这是因为,我们得在Kuwahara.usf 中构建这旋转矩阵并把它传入其中。将GetKernelMeanAndVariance()
改为:
float4 GetKernelMeanAndVariance(float2 UV, float4 Range, float2x2 RotationMatrix)
接着把里层for
循环的第一行改为:
float2 Offset = mul(float2(x, y) * TexelSize, RotationMatrix);
mul()
函数将使用偏移量和RotationMatrix进行矩阵乘法。这样就可以以当前像素旋转了。
接下来,我们要构造旋转矩阵。
构造旋转矩阵
我们使用正弦(sine)和 余弦(cosine)来构造旋转矩阵:
关闭 Global.usf并打开Kuwahara.usf,然后将下面的代码添加到变量列表的底端:
float Angle = GetPixelAngle(UV);
float2x2 RotationMatrix = float2x2(cos(Angle), -sin(Angle), sin(Angle), cos(Angle));
第一行计算出当前像素的角度,第二行使用该角度创建旋转矩阵。
最后,我们将RotationMatrix传给每一个核。把GetKernelMeanAndVariance()
改为如下形式:
GetKernelMeanAndVariance(UV, Range, RotationMatrix)
这就是方向性桑原滤镜的全部啦!关闭Kuwahara.usf并回到PP_Kuwahara,点击应用并关闭它。
下面就是原始的桑原滤镜和方向性桑原滤镜的对比。请尤其注意,方向性桑原滤镜不再有那些块状斑了。
注: 我们可以使用PPI_Kuwahara修改滤镜的大小。我建议将滤镜设为X半径大于Y半径。这将提高核沿边缘走向的大小,对于提高方向性有一定好处。
内容总结
以上是互联网集市为您收集整理的Unreal Engine 4 手绘风滤镜(Paint Filter)即 桑原滤镜(Kuwahara Filter)教程(下)全部内容,希望文章能够帮你解决Unreal Engine 4 手绘风滤镜(Paint Filter)即 桑原滤镜(Kuwahara Filter)教程(下)所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。