本文是《Unreal Engine 4 卡通轮廓线(Toon Outlines)教程》的第二部分:后期处理法(Post Process Outlines),书接上文《Unreal Engine 4 卡通轮廓线(Toon Outlines)教程》之 反转网格法

文章目录

  • 后期处理法(Post Process Outlines)
    • 理论部分(译者注,头疼可跳到下面的实操部分)
      • 什么是卷积(Convolution)?
      • 拉普拉斯边缘检测(Laplacian Edge Detection)
    • 实操部分(译者注)
      • 创建边缘检测算子(Laplacian Edge Detector)
      • 创建像素抽样函数(Sample Pixel Function)
        • 小结
      • 卷积
        • 小结
      • 使用阈值
      • 创建粗线
      • 添加到原始图像上

后期处理法(Post Process Outlines)

理论部分(译者注,头疼可跳到下面的实操部分)

使用边缘检测(edge detection)的方法也可以制作轮廓线。这种技术的关键就是检测一幅图像各个区域间的不连续性(discontinuity)。如下图所示不连续性有很多种类型:

译者注:法线的不连续性、深度的不连续性、颜色的不连续性以及亮度的不连续性


优点:

  • 可以非常方便地应用于整个场景
  • 恒定的性能损耗,因为着色器永远都是逐像素处理
  • 轮廓线的宽度在不同的距离都会保持相同,当然这也会成为一个缺点
  • 轮廓线不会和几何体重合,因为这是一种后期处理效果

缺点:

  • 需要多重边缘检测算子(edge detector)来获取所有的边缘,这将会影响一定的性能
  • 容易出现噪点,这意味着检测出的轮廓线边缘会有一定的不稳定性

什么是卷积(Convolution)?

所谓卷积就是在图像处理中,从两组数字产生一个单一数值结果的运算。首先,我们把一个由数字组成的网格(一般称之为核kernel)中心放置在每一个像素上。以下图为例,一个3x3的核沿着图像顶部的两行像素扫描:

译者注:核指的是3x3的网格

对于每一个像素,都用核实体去乘以它的相对应像素。(译者注:这句不好理解,看下面的例子就明白了)以嘴部左上角的像素为例(在这里为了简化运算,我们把图像转成了灰度图)

首先,我们以目标像素为中心放置核(这个核我们使用的就是上面的那个),然后让每个核元素乘以其覆盖的像素(灰度)值。

最后把所有的乘积加在一起,让这个乘积作为中心像素新的值。本例中,新的值就是0.5 + 0.5即1。下图就是每个像素卷积处理完毕以后的结果:

核的选用会决定我们得到的结果。本例中的核是用于边缘检测的,下面是其它一些类型的核:

译者注: 从左到右分别为锐化(Sharpen)、凹凸(Emboss)和均值模糊(Box Blur)

注意:你可能已经发现,在很多图像处理软件中,这些东西被称之为滤镜(Filter)。实际上,图像处理软件就是通过卷积来实现滤镜的。你甚至可以在Photoshop上自定义核来进行卷积运算。

我们一般使用拉普拉斯算子(Laplacian)进行边缘检测,即拉普拉斯边缘检测(Laplacian Edge Detection)。

拉普拉斯边缘检测(Laplacian Edge Detection)

那么,拉普拉斯边缘检测的核是什么样的呢?它就是我们刚刚在上一部分用到的那个!

之所以这种核能够用于边缘检测,是因为它计算的在斜率(slope)上的变化。一个区域的斜率变化越大,就越意味着它是边缘。

咱们用一维的拉普拉斯算子做个实验,以帮助你理解。一维的拉普拉斯算子如下:

首先,我们用肉眼找到一个图像的“边缘”像素(下图红色中心那个像素),把这个(一维拉普拉斯算子)核放到上面,然后它的计算卷积。


我们得到的结果是1,这标志着它的斜率变化很大,同时也意味着它很可能是一个“边缘”。

然后咱们再肉眼找一个非边缘像素(下图红色中心那个像素),同样计算一下它的卷积。


尽管像素的值不同(0.7,0.6,0.5),但是梯度是线性的。这表示着它的斜率没有变化,同时也意味着它不是边缘像素。

下面的图表就是用这个图像所有像素进行卷积后结果绘制的,我们可以看到所有的边缘像素的卷积结果都远离零点。

我去!好多的理论啊,不过别着急,下面就是有趣的部分啦。下个部分,我们将创建一个后处理材质并让它在深度缓冲区(depth buffer)进行拉普拉斯边缘检测。

实操部分(译者注)

创建边缘检测算子(Laplacian Edge Detector)

找到Maps文件夹并打开PostProcess地图,你将看到一个黑屏。这是因为这个地图使用了一个包含空的后处理材质(post process material)的Post Process Volume。

我们将编辑这个材质来实现边缘检测算子。第一个步骤是如何对相邻像素进行抽样。

我们使用一个纹理坐标(TextureCoordinate)来获取当前像素的位置。比如,如果当前像素在中间,那么它的返回值就是(0.5,0.5).这个二维向量,我们称之为UV。

想获取其它的像素,我们只需要给纹理坐标加上一个偏移量。在一个100x100(像素大小)的图像上,每个像素占大小为0.01的UV空间。要抽样它右边的像素,只需要在X周上加0.01。

然而,这里存在一个问题。因为图像的分辨率可能发生变化,使得像素大小也随之变化。如果把同样的偏移量(0.01,0)用在200x200像素的图像上,它抽样的像素就会是右数第2个。

为了解决这个问题,可以使用SceneTexelSize节点来返回像素大小。我们如下操作即可:


因为咱得抽样多个像素,那就不得不重复多次这个步骤:


很显然,这样很快就乱套了。好在我们可以使用材质函数来整理材质蓝图。

注意: 材质函数就类似于我们在蓝图和C++中使用的函数

下面的部分,我们将把节点复制到材质函数中,然后创建一个输入引脚来获取偏移量。

创建像素抽样函数(Sample Pixel Function)

首先找到Materials\PostProcess文件夹。单击Add New然后选择Materials & Textures\Material Function来创建材质函数。

将其更名为MF_GetPixelDepth并打开。这个蓝图当前只有一个FunctionOutput节点。后面,我们会把它连接到被采样像素的值。

首先,我们需要创建一个创建一个FunctionInput节点作为接收偏移量的Input节点。

后面我们使用这个函数的时候,这个节点将会成为输入引脚。

现在,我们得为这个输入做一些设置。选择FunctionInput节点,然后打开细节面板。调整如下设置:

  • InputName: Offset
  • InputType: Function Input Vector 2,因为深度缓冲区是一个2D图像,所以偏移量也必须是Vector 2
  • Use Preview Value as Default: Enabled。如果我们不提供输入值,那么函数将会使用Preview Value的值

接下来,我们得用偏移量乘以像素大小。然后通过添加下图中高亮的节点让结果和纹理坐标相加:

最后,使用所提供的UV对深度缓冲区进行抽样,添加一个SceneDepth节点并如下图所示连接:

小结
  1. 偏移量通过Vector2SceneTexelSize相乘而得来。它是UV空间上偏移量。
  2. 让偏移量和TextureCoordinate相加获得一个相距当前像素距离为(x,y)的像素。
  3. SceneDepth使用所提供的UV来抽样相应像素,然后把它输出。

这就是材质函数的全部了。点击Apply,然后关闭MF_GetPixelDepth

注意: 这时可能会看到一个报错only translucent or post process materials can read from scene depth,可以忽略它,因为这个函数将用于后处理材质,没有问题的。

接下来,就要用这个函数来进行深度缓冲上的卷积运算了。

卷积

首先,为每个像素创建偏移量节点。因为核的角上都是0,所以可以不用管它们。这样,我们对左、右、上、下4个像素创建就可以了。

打开PP_Outline材质,按如下所示创建4个Constant2Vector节点:

  • (-1, 0)
  • (1, 0)
  • (0, -1)
  • (0, 1)

然后对核中的5个像素进行抽样。创建5个MaterialFunctionCall节点,并把每一个都设为MF_GetPixelDepth。然后把每个偏移量连到对应的函数上。

于是,我们获得了每个像素的深度值。

接下来是乘法阶段。因为周围像素的乘数都是1,你可以不用乘它们;但必须对中心像素乘以-4(对应上图最下面的那个函数)。

然后,把所有的值相加。如下图所示,创建Add节点:


还记得上面图表中的像素值么?有些值会是负的。如果你在材质中使用负值,那么将会显示黑色,因为它们小于零。因此,我们得给计算它们的绝对值,把每个输入都变成正数。 添加一个Abs节点餐后如下所示连接:

小结
  • MF_GetPixelDepth节点会获得上、下、左、右、中间像素的深度值
  • 把它们分别和核中的值相乘,实际上我们只需要让中间的像素相乘
  • 计算它们的和
  • 获取和的绝对值以防止出现负数

点击Apply并回到主编辑器。这时,整个画面将会呈现线条!

目前还有一些问题。首先,有些“边”在只有一点深度差的地方(其实算不上边)。其次,应为背景是球体,所以呈现一圈一圈的。如果你只想对网格模型进行检测,这算不上是问题。但如果你要检测整个场景的边缘,这些环线就不行了。

我们可以通过阈值来修正这些问题。

使用阈值

首先,我们来修正那些微小深度差上产生的边缘。回到材质编辑器,如下操作。把阈值设为4.

后面,我们会把边缘检测的结果镰刀A上,这样当像素值大于4,它就会输出1.否则输出0(即非边)。

接下来,咱们处理背景上的线。如下所示操作,并将DepthCutoff 设为9000。


这样,如果当前像素的深度值大于9000,将会输出0(非边),否则会输出A<B引脚到的值。

最终连线如下所示:

现在,线条将仅出现在像素值大于4(阈值)且深度值小于9000(DepthCutoff)的地方。

点击Apply,然后回到主编辑器。那些细小的线和背景的线就消失了!

注意: 可以创建一个 PP_Outline材质的材质实例来控制阈值和DepthCutoff。

边缘检测运转良好,但是如果想要粗一些的线该怎么办?那我们就需要更大的核。

创建粗线

一般来讲,大的核对性能影响会更大一些。因为我们需要采样更多的像素。有没有方法能够使用大核但是却拥有3x3的核一样的性能呢?有,使用空洞卷积或膨胀卷积(dilated convolution)

在空洞卷积中,我们仅仅是把偏移量放得更远一点。让每个偏移量乘以一个名为膨胀率的标量。膨胀率决定了每个核元素之间的距离。

看到了吧,这样虽然放大了核的大小,但是像素抽样的数量没有变。

接下来,咱们实现以下空洞卷积。回到材质编辑器,创建一个名为DilationRate的ScalarParameter节点。将其设置为3,然后把每个偏移量乘以膨胀率。

这样每个周边抽样像素将会远离中心像素3个像素。

点击Apply回到主编辑器。你会发现,线比之前粗了好多。下面是多种膨胀率的效果比较:

除非你想要某种艺术效果,否则可能还是最初的效果比较好。在最后的部分,我们将为原始画面添加轮廓线。

添加到原始图像上

回到材质编辑器,创建如下连接。注意顺序很重要!

接下来如下连接:

如果alpha值设为0,则Lerp节点将会输出场景图像,否则会输出LineColor

点击Apply然后关闭PP_Outline材质,原始场景就会显示轮廓线啦!

Unreal Engine 4 卡通轮廓线(Toon Outlines)教程 之 后期处理法(Post Process Outlines)相关推荐

  1. Unreal Engine 4 卡通轮廓线(Toon Outlines)教程 之 反转网格法(Inverted Mesh)

    原文|<Unreal Engine 4 Toon Outlines Tutorial> 作者|Tommy Tran Apr 2 2018 阅读时长|25分钟 内容难度|入门级 文章目录 开 ...

  2. 【学习笔记】Unreal Engine 4 虚幻引擎蓝图中级教程物理碰撞教程

    教程链接:Unreal Engine 4 虚幻引擎蓝图中级教程物理碰撞 想共享的小伙伴可以E-mail我:lxbcolorgalaxy@qq.com 目录 第一章 常见的物理现象及分析 1蓝图模板中的 ...

  3. Unreal Engine 4 卡通着色(Cel Shading)教程

    原文|<Unreal Engine 4 Cel Shading Tutorial> 作者|Tommy Tran Feb 27 2018 阅读时长|20分钟 内容难度|中等 文章目录 开始吧 ...

  4. ArcGIS Maps SDK for Unreal Engine通过UI方式显示地图教程

    前言 一个 UE 项目,可以包括多个关卡(level),关卡可以在菜单栏中新建(文件->新建关卡). 使用快捷键 Ctrl+S 可以保存当前关卡,你可以保存到上面提到的演示文件放置的位置,你可以 ...

  5. Unreal Engine 4(UE4)下载教程

    首先登陆到UE官方网站https://www.unrealengine.com  下载EpicGamesLauncherInstaller-2.1.3-2533468.msi文件 按照提示进行安装 如 ...

  6. Unreal Engine 4 手绘风滤镜(Paint Filter)即 桑原滤镜(Kuwahara Filter)教程(上)

    原文|<Unreal Engine 4 Paint Filter Tutorial> 作者|Tommy Tran May 1 2018 | 翻译 开发游戏的老王 阅读时长|25分钟 内容难 ...

  7. Unreal Engine 4 使用HLSL自定义着色器(Custom Shaders)教程(下)

    本文是<Unreal Engine 4 自定义着色器(Custom Shaders)教程>的下半部分,上半部分请见<Unreal Engine 4 自定义着色器(Custom Sha ...

  8. 虚幻引擎的数学知识学习教程 Math for Unreal Engine (Early Preview)

    通过做真实世界的 Unreal Engine项目来学习数学 你会学到什么 理解游戏开发对数学的基本需求 将数学直接应用到用例中,而不是钻研理论(用我们的示例项目进行实践) 正确编辑短视频,节省您的时间 ...

  9. ue5新手零基础学习教程 Unreal Engine 5 Beginner Tutorial - UE5 Starter Course

    ue5新手零基础学习教程 Unreal Engine 5 Beginner Tutorial - UE5 Starter Course! 教程大小解压后:4.96G 语言:英语+中英文字幕(机译)时长 ...

最新文章

  1. yum安装mysql后密码_Centos7:yum安装MySQL5.7后如何设置root密码
  2. 程序员能成为设计师吗
  3. generator自动生成mybatis配置和类信息
  4. CentOS No manual entry for man 没有 xx 的手册页条目
  5. c语言 结构成员 变量引用,C++结构体变量的引用
  6. Spring Boot中配置文件application.properties使用
  7. 5位随机数重复的概率 php_PHP产生不重复随机数的5个方法总结
  8. AI运动:阿里体育端智能最佳实践
  9. matlab里方框一个叉号,Word输入×叉号和方框打叉方法
  10. python判断按键是否按下_python – 如何检查键修饰符是否被按下(shift,ctrl,alt)?
  11. Redis 存入key乱码问题
  12. 高效能屌丝创业者的七项习惯
  13. 市场营销学4——市场调研与预测
  14. 榜首易主!在线票务大战胜负已定,透过中影这个小动作早已看穿一切
  15. Splunk 会议回顾: 大数据的关键是机器学习
  16. 强调实体融合的当下,元宇宙当仁不让地成为各色玩家关注的焦点
  17. 基于guided image filtering的图像去雾 opencv实现
  18. 解决华硕前置音频没声音,但后置音频有声音,没有Realtak音频管理器如何处理的问题
  19. 由浅入深玩转华为WLAN—23 Traffic-filter(ACL)在WLAN无线场景的应用
  20. Vue.js实现简单的按钮点击改变css样式

热门文章

  1. 负荷预测(BP神经网络)
  2. 【GMT43智能液晶模块】例程十四:MODBUS TCP实验——电源监控
  3. 石油测井专题(七)石油测井技术
  4. 联想劫持Edge浏览器主页
  5. 明月学Mybatis:小结(一)
  6. 苹果6s怎么会不支持电信版本?
  7. 淘宝店铺搜索工具(提升淘宝店铺排名人气)
  8. Windows造字功能详解
  9. elastix环境变量设置
  10. 高压功率放大器在微孔压电超声雾化研究中的应用