描边技术总览和常见商业游戏中的描边方案(下)

Ashley

2021-12-1312876次浏览

0评论

7收藏

0点赞

分享

文章分为上下篇,为了更好的阅读感受,请按顺序阅读。上篇链接:

描边技术总览和常见商业游戏中的描边方案(上)


三、基于贴图的方式

1、画在纹理贴图上

提到基于贴图方式的描边,最容易想到的必然是直接画在贴图上。如上图红框的部分是百褶裙上的描边,只有很细的1个像素。

这种方式于美术来说能够有最高的自由度对线条进行调整,但是缺点就继承了纹理贴图的所有缺点。另外由于描边一般都只有零星几个像素,对贴图分辨率有着更高的要求,否则会产生难看的锯齿。如果要做到完全没有锯齿也就是描边贴脸的情况下也要做到清晰可见的边缘,那就是相当于描边的分辨率级别至少要跟显示器一样,这是不现实的事情。讲道理赛璐璐的风格化渲染追求的就是干净的画面感。要是在干净清晰的画面上出现模糊、有着严重锯齿的像素区域,对画面效果是会掉分的。

个人认为绘制在纹理贴图的方式适合用于以下情况:

  • 小地方上的描边,并且镜头不会拉近

  • 其他方案都行不通的复杂描边

这种方式应当作为最后手段来考虑,因为真的太容易产生锯齿了。如果到了非用不可的地步,有几个办法可以减少锯齿干:

1、尽量不要在贴图上出现斜向的描边,考虑改变uv分布让描边跟水平或垂直轴对齐

2、提高描边的分辨率。考虑使用一张专门的描边纹理贴图+专门的一套的uv,类似于蓝色协议中头发高光点的做法

2、 本村线/类本村线

下面是大名鼎鼎的本村线。首次被大家所认知应该是《罪恶装备Xrd》在GDC2015上由本村・C・純也带来的分享,本村线的名称也是来自于他的姓氏。下面是一张有着本村线经典特征的贴图。

其做法如下:

1、首先在贴图上绘制有水平、垂直轴对齐的黑色边界线

2、对需要进行描边的地方的顶点处创建在同一个位置上的重复顶点。假设原顶点为OriginalVertex、重复创建的位置相同的顶点为DummyVertex

3、将DummyVertex的UV埋进黑色边界线内

4、通过调整DummyVertex与OriginalVertex之间的UV分布关系,从而让OriginalVertex所在的三角面片能够被黑色边界线所覆盖得到

具体可见下面的GIF图或GDC2015的分享。下面是对动图的解释:下图中选中了分别两个OriginalVertex、两个DummyVertex共四个顶点,从模型上也印证了DummyVertex与OrignalVertex位置相同的这一个说法。接着调整DummyVertex与OrignalVertex的UV,从而让更多/更少的区域被黑色边界所覆盖

下面是对静图的文字描述。关注下图中的白色线框部分,左下角的UV分布中虽然只有三个顶点,实际上有四个顶点、其中的两个汇聚在同一个uv上。右边的模型虽然表面只有两个顶点,实际上也是四个顶点、它们在同一个位置上。

从上面看来,实际上本村线可以说是画死在纹理贴图方式的升级版。

它主要解决的是处理线条粗细的问题。如果是直接绘制在纹理贴图上的粗细线条,那么就会不可避免的出现斜向的像素,一旦将贴图放大就会出现明显的锯齿,要减少锯齿需要使用更高分辨率的贴图。而本村线使用的是等粗细且水平、垂直对齐的黑色边界,无论怎么放大都不会有锯齿。线条的粗细是通过调整uv来做到的,与贴图无关。

但是缺点也是很明显的,本村线对模型的UV分布要求特别高、会增大美术制作的工序成本。

  • 问题1:如果没有太多的预算制作本村线,但是又想达到本村线的效果,有什么办法吗?

可以考虑在模型上拓扑出描边的形状,将其UV移动到一块单独的纯色色块当中。 见上图膝盖上的描边,这是一种类本村线的做法。相比起本村线通过调整UV来调整描边形状、粗细的做法,类本村线则是直接将描边制作在模型上,然后UV展开时将这部分描边的UV单独指向一篇纯黑色色块当中。

从某个角度上来说,这种做法对模型的制作没有附加要求。因为这种描边是本来就是做在模型上的,所以美术同学制作模型的时候只要采用跟平常一样的制作方法即可。

3、SDF

SDF,全称Signed Distance Field,中文名为有向距离场。定义如下:每个像素(3D场景对应体素)记录与距离自身最近物体之间的距离,如果在物体内距离为负、正好在物体边界上为0、在物体外距离为正。假定白色为物体,下图是由中央白色物体球所生成的一张SDF图。

SDF最常见的应用是制作矢量字体:由于描述的是相对距离、所以无论放大到多大都不会出现锯齿。除此以外在图形学领域也非常常用:由于其特殊性、可以进行数学运算,尤其适合用于在Ray Marching中进行建模(Painting a Selfie Girl, with Maths - YouTube)。

受到SDF制作矢量字体的启发,我想也可以利用SDF来绘制物体的内描边。步骤如下:

1、和普通贴图一样,绘制模型的描边并导出贴图

2、离线将第一步的贴图制作成SDF图

3、渲染时将SDF图还原成原贴图

理论上SDF能够做到与本村线一样的效果,并且能够保证是矢量的、即便是贴脸描边也不会出现锯齿。而且不会改变原来的模型制作流程、制作要求也相对较低,只需对描边贴图特殊处理即可。相比起其他的基于手绘的方式,生成的SDF图往往也不需要太高的分辨率。缺点是需要多采样一张SDF图。

但是似乎没有找到用这种方式制作内描边的游戏案例。。。网上也就只能找到一篇小教程。

  • 问题:线条颜色怎么整

如果是整体的线条颜色的话还是比较好做的。如果希望不同地方有不同颜色,可以考虑写入顶点色或者UV通道里面。

4、总结

基于贴图的方式对美术来说应该是最友好的。会比较符合美术的工作方式和工作思维。但是其缺点就全部继承了贴图的缺点,最明显的就是依赖贴图的分辨率,一旦分辨率不够就容易出现锯齿。所以基于贴图的方式最急需解决的问题是如何处理锯齿。

最基本的应该是源头上也就是贴图上就不要出现锯齿,尽量让描边保持与水平、垂直轴对齐的笔直线条。如果想要注入灵魂、增加线条的粗细变化,可以考虑上本村线或者类本村线。我觉得SDF会是一种很好的思路,但是似乎不常见,太怪了。


【参考资料】

1、GDC2015 24:17 本村线介绍 GuiltyGearXrd's Art Style : The X Factor Between 2D and 3D - YouTube

2、「日本語」本村线详细的文本介绍 西川善司の「試験に出るゲームグラフィックス」(1)「GUILTY GEAR Xrd -SIGN-」で実現された「アニメにしか見えないリアルタイム3Dグラフィックス」の秘密,前編 (4gamer.net)

3、第二条的翻译 【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(2) - Trace0429 - 博客园 (cnblogs.com)

4、V社2007年论文,用矢量图制作Decal SIGGRAPH2007_AlphaTestedMagnification.pdf (pinyuncloud.com)

5、SDF介绍 Signed Distance Field - 知乎 (zhihu.com)

6、SDF制作内描边教程 用SDF处理卡通内描线的锯齿问题 - 知乎 (zhihu.com)

7、参考制作SDF图 Signed Distance Fields (codersnotes.com)

8、介绍了几种生成SDF图的方法 Various Distance Field Generation Techniques (shaderbits.com)


四、基于图像处理/后效的方式

1、检查数据的不连续性

不确定是不是后效的描边,但是要做到这么大规模的通常都是后效描出来的 マニフォールド ガーデン Nintendo Switch/PS4 リリースアナウンストレーラー - YouTube

通过后效进行的描边更接近于Compute Vision也就是计算机视觉的领域,游戏里的后效描边一般就是根据世界法线或者场景深度贴图的突变程度(或者说不连续性)来决定是否需要描边。常见的会使用Sobel过滤器(也叫卷积核)来得到这个突变程度。其中A是原始图像,Gx得到的是x轴方向上的突变程度、Gy得到的是y轴反向上的突变程度。换算到图形学语言的话就是在Pixel Shader中取附近九个像素的内容乘上因子后相加得到突变程度。

基本上基于后效的描边就是上面的所有内容,其他的东西都是基于A、基于算子、或者基于计算得到的突变程度来做一些trick,从而得到符合自己项目情况的描边结果。个人觉得在这些trick里面比较强的是二之国2里的描边。

二之国2里的描边全部基于后效,最后能够得到非常干净的描边效果。至于二之国2的算法是如何的,可以参考本文的下篇里二之国2的参考资料。

  • 问题:还是那个问题,线条粗细跟线条颜色呢?

如果只是整体的线条粗细的话还是比较容易调整的,只要调整阈值即可。不过需要注意的是调整阈值大小可能也会导致在不想要描边的地方出现描边。如果想要特定地方线条加粗,那就得将粗细度写进对应的像素里面,例如在虚幻里就写入CustomData然后在后效材质里读。

线条颜色同理,可以读取BaseColor,也可以将专门的描边颜色写入对应的像素。但如果专门的描边颜色就占用3个通道了,可能会大大地增大带宽。可以考虑用一张贴图做调色板,只用一个INTERGER通道去索引调色板当作线条的颜色。

但是总的来说还是不太好调整的,相比起其他方式来说,基于后效的方式对描边的控制度最低。

2、Jump Flooding Algorithm

左边是利用Jump Flooding Algorithm实时生成的DOF图,右边是利用DOF图生成外轮廓边后加上原来图像之后的鬼畜结果

另外这种做法按理来说也能归为「基于贴图的方式」,但是想想还是更接近图像处理,所以最终还是放在这一栏里面了

Jump Flooding Algorithm(下简称JFA)是一种能够对二维图像实时生成DOF图的算法。DOF与SDF一样,但是没有正负距离之分。对DOF图做一个简单的step或smoothstep操作就可以得到外轮廓边。至于JFA的算法流程请看参考资料,这里就不过多叙述了。

相比起外线外扩法生成的外描边,JFA的特点有两个:

1、能够生成很宽的外描边

2、能够生成真正贴合模型的外描边

下面是简单的示意步骤。原文没有提到实际的应用场景,也没有考虑场景深度的影响。如果是用在延迟渲染的管线上的话,我认为一种可行的方案如下:

1、输出原图:在BasePass中将那些需要外描边的物体输出到一张RT的某个通道上

2、生成DOF图的Pass:以BassPass输出的RT和场景深度作为输入跑一个JDF算法,输出的R通道是DOF值、G通道是最近像素的深度值

3、合成:在DeferredShadingPass之后进行合成,将第2步的R通道做step/smoothstep操作生成外描边,并根据G通道的深度和场景深度做比较判断是否需要输出

JFA的缺点是对DOF图的分辨率有较高的要求,否则会出现明显的锯齿,使用smoothstep可以增加一些过渡减少锯齿感。还有不知道是不是我实现的有问题,生成的DOF图精度并不是特别高、会有明显的块,而且分辨率高了以后会有闪烁现象。另外在延迟渲染里会需要额外的一个通道来存储,会稍微增加带宽压力,如果需要生成外描边的物体不算太多可以考虑增加多一个Pass出原图,而不是在BassPass中。最后由于是类似图像处理的做法,所以对生成的外描边的可控性也是特别低的。

但是JFA做法最大的优点也是其他做法都做不到的点在于能够生成贴合模型并且可以做到很宽的外描边。不过实际上一般也用不上这么宽的外描边,我认为可以用在一些风格化特写镜头上面。

5、总结

基于后效的描边,最大的好处是能够大规模地给场景进行描边,并且这个消耗是固定的、不会随着场景复杂度增加而增加,非常适合用于给背景进行描边。还有一个好处是对于Cube这种有着尖锐边缘的物体的支持特别好,由于边缘处法线突变、会产生非常完美漂亮的描边。

但是缺点也是显而易见的,那就是无法保证恒定的描边质量。可能会在不受欢迎的地方出现描边、或者想出现描边的地方却又没有。再者就是对很难对线条进行精细地控制。如果说基于贴图的方式对美术最友好,那么基于后效的方式是对美术最不友好的了。


【参考资料】

1、マニフォールド ガーデン | 新たな物理法則による一人称パズルゲーム (manifold.garden)

2、Sobel过滤器 索贝尔算子_百度百科 (baidu.com)

3、《Unity Shader入门精要》Chapter13

4、二之国2完全使用后效处理描边,并且描边结果非常清晰 Ni No Kuni 2: frame analysis – Thomas' blog (thomaspoulet.fr)

5、冯乐乐老师对上一条的翻译 [Graphics Study] Ni No Kuni 2(二之国2) - 知乎 (zhihu.com)

6、Jump Flloding Algorithm算法 Fast Voronoi Diagrams and Distance Field Textures on the GPU With the Jump Flooding Algorithm « The blog at the bottom of the sea (demofox.org)

7、生成宽的外描边的心路历程 The Quest for Very Wide Outlines. An Exploration of GPU Silhouette… | by Ben Golus | Medium


五、其他参考资料

1、介绍了5种外描边的方法 5 ways to draw an outline (alexanderameye.github.io)

评论 0

0/1000
网易游学APP
为热爱赋能
扫描二维码下载APP