GDC文章粗读——看看《Hi-Fi RUSH》的卡通渲染(上)


3楼猫 发布时间:2024-05-17 13:32:33 作者:Hakumen Language

前言

在我写上一篇介绍ArcSystemWorks的卡通渲染的文章时,还没有微软解散Tango工作室的这个新闻。虽然我个人没有玩过这个游戏,但我也很欣赏他们能做出这款小而美的游戏——这也是微软去年一众拉跨作品中少数意料之外的亮点了。
我在GDC Vault看到这篇今年3月的分享可以下载阅读的时候,几乎就是同一周看到了关于Tango被关闭的新闻。对于微软这个无论经济还是风评都吃亏的决策,网上的讨论已经很多了,我除了遗憾更多的是不意外,毕竟大公司近些年的草台程度已经越来越夸张了,而财报会议之后背锅的往往就是靠拉报表排序来选择。
回到这篇分享,它或许内容编排上略微有些瑕疵,但总的来说是富有热情且干货满满的——大部分不是最先进的技术,但是确实都是很踏实的细节。一方面我能感受到分享人自豪地掏干货的那种热诚,同时结合近况我也能感受非常遗憾;无论如何,希望粗读这篇分享能带大家更了解这个游戏和这个团队,毕竟人活着就还有希望。
分享了主要内容的Kosuke Tanaka

分享了主要内容的Kosuke Tanaka

分享了人脸阴影方案一节的Takashi Komada

分享了人脸阴影方案一节的Takashi Komada

*在通读了整个分享原文后,我计划用两篇文章覆盖原文的大约80%以上的内容,中间顺序可能会略微调整一下(除了略去开头的介绍部分外,主要是把面部阴影这一节提前了)。后面的内容还是以翻译原文为主,打星号的则是我自己的备注和评论;另外就是尽管以分析渲染为主,但提到虚幻引擎中的一些细节或许没那么准确,如有不对欢迎指正。

1 整体的延迟渲染方案

*主机游戏一般都是用延迟渲染作为主要渲染管线的,他们在力所能及的几个方面做了引擎定制化开发。PPT中也大量使用了游戏中角色来模拟发言,可见他们非常喜爱自己做的这款游戏。
*这部分我调整了一下原PPT的顺序并做了抽选和精简,希望能提高易读性。
画面和性能指标

画面和性能指标

在Xbox平台上的预期与低端机策略

在Xbox平台上的预期与低端机策略

——由于我们把性能和图形质量视为最优先的项,开发过程中一个挥之不去的恐惧(原文是nagging fear)就是人们会对我们的技术力感到失望。最终,无论是游戏的视觉还是技术上都收到了好评,这对我们是一个很好的结果。
——平衡性能、分辨率和渲染特性(的丰富程度)是很难的。作为图形工程师我们往往会对新的渲染技术感到兴奋,也想要开发出更好的图形技术,不过我们游戏最强调的还是性能与画面质量,为此在选择采用哪些渲染特性时我们必须谨慎考虑。
*英文原文中有graphics和image quality这两个词,在这个语境下前者我翻译成了图形技术。图形技术有时候等于更好的画面,有时候则不是。(原文是... want great graphics, but our game emphasized performance and image quality)
默认延迟渲染

默认延迟渲染

定制化卡通延迟渲染

定制化卡通延迟渲染

*可以看到自定义风格化渲染主要的区别就是对阴影的处理,其次才是颜色等
GBuffer分配的内容

GBuffer分配的内容

GBuffer分配的图形预览

GBuffer分配的图形预览

*提到延迟渲染就离不开GBuffer,如果还不清楚是啥的可以看看我之前的文章或是网上介绍延迟渲染的资料。可以简单理解成这些帧缓冲都是后续计算光照的数据提供者,针对这部分内容光照不需要额外考虑其它空间中的几何信息了。
*最基础的PBR不透明渲染需要3维的法线(GBufferA中的RGB)、3维的颜色(GBufferC中的RGB)、2维的粗糙度金属度(或高光程度)就足够了,上面的图1中可以看到引擎中和他们做的一些补充与自定义部分(标了Customize的)。
一帧中的帧缓冲内容

一帧中的帧缓冲内容

*这张图列举了一些其它的帧缓冲内容如深度、AO等。其中大部分内容后面的章节会分析到。
为全局光照的每一个模块做定制化的调整

为全局光照的每一个模块做定制化的调整

*这些特性也可以结合前一张帧缓冲来看(大部分会被渲染到一张帧缓冲上)。
*很多特性之前我的文章里也介绍过,例如ProbeGI对应光探针采样和球谐光照技术,SSR是屏幕空间反射的缩写,StaticShadowMap是烘焙的阴影贴图,AOVolume是一种能动态算出AO的策略,SSAO是屏幕空间的AO等。后面和下一篇文章会有章节逐一分析。
*可以感受到引擎中的全局光照就像拼零件一样,把各种高性能高近似度的技术一个一个拼接起来。
逐体积区域后处理

逐体积区域后处理

——另一件我们想提升的事项是,UE4的后处理是逐摄像机添加并计算的。
——我们感觉这限制得太死了,因为我们想为同一个摄像机渲染的不同区域设置不同的颜色和风格(通过后处理区域)。
依据GBuffer和Box体积渲染后处理的方案

依据GBuffer和Box体积渲染后处理的方案

*这里解释了逐体积做后处理的思路——基于延迟渲染的GBuffer和DepthBuffer,可以判断着色点是否在体积包围盒的范围内,并添加相应的后处理渲染。
管线及修改总览

管线及修改总览

*图中从左到右大概是:深度缓冲、阴影深度、GBuffer、AO和胶囊阴影等、全局光照、动态屏幕空间阴影、静态阴影贴图、屏幕空间反射、Decal灯、后处理勾线。
*标黄的是经过自定义修改的部分。可以看到比人物难度更大的是环境的卡通化渲染,几乎所有光照和阴影都需要做深度的定制化修改。

2 人物面部阴影

*上一篇文章中提到了面部阴影轮廓的问题,只依靠手调法线肯定是不靠谱的。这里很快会就介绍一种目前比较主流的方案。
角色阴影

角色阴影

——Hi-Fi RUSH中角色的自阴影是cel风格着色。阴影和非阴影区域有着清晰的分界线,并且阴影区域会被指定一个特定的颜色。由于有这种阴影风格,视觉质量就取决于阴影的轮廓和颜色表现。
——Hi-Fi RUSH的艺术理念是sharp, clean和colorful,所以阴影也需要始终保持清晰的轮廓。
*这部分理念如果看了上一篇关于罪恶装备卡通渲染的文章,应该能看出其中的一致性。
角色阴影

角色阴影

*除了脸部,角色其它部位的着色都是通过阈值判断法线与光线的夹角(的点乘)来计算的。上一篇也介绍过一样的内容,不过写成step函数会比if有更好的性能。
*对于脸部,最初的方案是调整面部法线。下面就列举了这一方案的一些问题:
顶点法线问题1:难以生成顺滑的曲线

顶点法线问题1:难以生成顺滑的曲线

顶点法线问题2:面部动作会破坏阴影轮廓

顶点法线问题2:面部动作会破坏阴影轮廓

顶点法线问题3:艺术家无法通过仅仅调整模型设置解决以上问题

顶点法线问题3:艺术家无法通过仅仅调整模型设置解决以上问题

*可以看出对于模型视角需要自由转动、或者模型可变化的情况,只调法线都是不适用的了。
通过阈值数据纹理来着色

通过阈值数据纹理来着色

——为解决这一问题,我们采用了一张类似高度图(heightmap)的纹理。在内部我们把这个纹理称为阈值纹理(threshold map)。后续我会解释如何制作它,不过让我们先解释一下如何用它来计算阴影。
——首先,计算面部朝向与光线方向的水平分量的夹角。这里水平分量指面部骨骼坐标系中相应的水平分量。最终计算结果为夹角除以Pi之后归一化的值。(注意这里不是用的点乘结果了)
——然后,通过采样阈值纹理,比对上一步计算得到的值就能判断着色点是否在阴影中。
*这也是目前卡通渲染游戏中主要的面部阴影方案了。另外需要注意这个纹理是有左右方向的。
通过阈值数据纹理来着色

通过阈值数据纹理来着色

*这样得到的阴影是水平阶段变化的(符合预期)。光线从左侧入射时(对于图中的纹理方向),需要水平翻转阈值纹理。
*这一方案引用自2018年UnitySeoul大会上米哈游贺甲的分享。(说句题外话,贺甲在原神后不久就离开米哈游了,现在也已经不在腾讯了)
如何制作阈值纹理——通过DCC工具做180度的烘焙

如何制作阈值纹理——通过DCC工具做180度的烘焙

如何制作阈值纹理——艺术家手动调整纹理(修改其中的边缘部分)

如何制作阈值纹理——艺术家手动调整纹理(修改其中的边缘部分)

如何制作阈值纹理——把一组调整后的烘焙通过类似距离场的方式插值合并

如何制作阈值纹理——把一组调整后的烘焙通过类似距离场的方式插值合并

*到这里基本就揭秘了目前卡通渲染主流的面部阴影渲染方案。后续只要是想让颜色看起来很“平”的,基本还是这个方案。
最后是顶点法线方案和阈值纹理方案的对比

最后是顶点法线方案和阈值纹理方案的对比

3 环境的卡通风格着色

*这部分主要的干货就是有向距离函数(SDF——Signed Distance Function)的应用。
卡通风格着色

卡通风格着色

*光照的视觉是通过网点来表现,阴影是通过填充线来表现。熟悉漫画的会发现这是日漫里常用的明暗填充方式。(这里Halftone的翻译直接采用了漫画绘制中的网点,原词本身也是形容一种网格板的结构)
半色调的点

半色调的点

*网点主要运用在:(基于解析算法的)雾,Bloom光晕效果,探针全局光照,屏幕空间反射。
填充线

填充线

*填充线主要运用在:体积AO阴影,静态阴影纹理,玩家动态阴影纹理,屏幕空间AO。
*下面进入干货部分,主要是SDF用于纹理渲染和把生成的纹理“贴”到场景:
1)SDF用于纹理渲染:
有向距离函数——网点和填充线都可以用SDF函数计算

有向距离函数——网点和填充线都可以用SDF函数计算

实现网点——第一步

实现网点——第一步

*SDF可以简单理解为对空间中的任何一个点都可以描述它的一种或多种位置关系(例如距离边缘的距离、和其它点的最小距离等)。在这个案例中比较简单,就是一组平铺的0到1纹理坐标映射。
*对于其中一格,首先通过其纹理坐标(左图)、点的颜色、背景颜色、点的半径就可以绘制出一个点。(贴出来的代码页比较简单了,延中心点做了个距离比较)
实现网点——保持长宽比

实现网点——保持长宽比

实现网点——重复格子

实现网点——重复格子

*在之前的一步基础上增加了长宽比修正平铺,标蓝的是增加的内容。
实现网点——旋转45度

实现网点——旋转45度

*整个纹理旋转45度即可。到这一步已经实现了预期效果。
*后面还补充了在公式中如何做抗锯齿计算的内容:
实现网点——抗锯齿

实现网点——抗锯齿

实现网点——通过smoothstep做柔和边缘

实现网点——通过smoothstep做柔和边缘

*这里就是上次提到过的smoothstep的作用,阈值在某两个值之间时会有一个从0到1的过渡。通过这个函数可以把边缘渲染成半透渐变的。
*这里还提到如何计算这个宽度的问题,就不展开了,有兴趣可以去看PPT中引用的2012年的文章。
实现填充线效果——把Y方向的UV赋值为0即可

实现填充线效果——把Y方向的UV赋值为0即可


2)把生成的纹理“贴”到场景
SDF UV格子生成(屏幕空间)

SDF UV格子生成(屏幕空间)

——对于类似Bloom和体积雾等效果,我们使用屏幕空间的纹理坐标。(默认会是物体空间转到世界空间的纹理坐标。体积雾可以参考上图的射灯范围)
——我们没有做任何复杂的事,演示的shader已经很接近我们实际在游戏中渲染的屏幕空间的网点了。
*使用屏幕空间纹理坐标主要是为了Bloom和体积雾的网点始终是斜45的。
SDF UV格子生成——世界空间

SDF UV格子生成——世界空间

——屏幕空间的纹理坐标对于不需要表现深度的特性,例如Bloom和体积雾有很好的效果,但在特定的多边形表面上,我们希望网点能看起来像贴在表面上而不是朝向摄像机。因此我们也需要世界空间的纹理坐标。
SDF UV格子生成——世界空间

SDF UV格子生成——世界空间

——为计算世界空间的纹理坐标,我们使用GBuffer中的世界空间法线去计算多表形表面的主要轴(dominant axis),以便(将网点等)投影到对应轴的平面上。
——例如,如果Z值是世界空间法线中的最大值,我们就会选择投影到xy平面上,这意味着我们会选择缩放后的世界空间xy坐标作为投影的纹理坐标。
SDF UV格子生成——世界空间

SDF UV格子生成——世界空间

——对于有穿插的平面,我们没有采用三平面映射(triplanar mapping,是一种在空间中的纹理映射方案)。我们尝试过使用这个渲染技术,但混合的结果过于明显,所以我们选择了简单投射到一个平面上;由于我也没有因为网点和线在平面交叉处被切断的效果被抱怨,所以最终我们保留了这种简化的方案。
游戏内的卡通风格着色——渐变

游戏内的卡通风格着色——渐变

——由于我们程序化地生成了这些网点和线,因此我们可以轻松地基于场景光照条件来调整它们的视觉表现。我们使用场景的光照强度(作为参数)来调整网点和填充线的尺寸。
——在这一页的图中,全局光照的网点在靠近光源处更大,在远离光源处更小。
——通过这种简单的技术我们可以用(网点尺寸的)渐变表现出光线延距离衰减的视觉效果,这也使整体的场景视觉更倾向于计算机生成的样式(look more computer generated)。
*网点和填充线后续会作为光照和阴影在各处的基本样式,但其计算方式这里都介绍完了。

最后对比一下卡通风格的光影未开启和开启时的视觉效果:
卡通着色关闭的视觉

卡通着色关闭的视觉

卡通角色开启的视觉

卡通角色开启的视觉

结语

GDC和SIGGRAPH大家略有了解可能会知道,后者的分享内容都是有行业创新性的论文级别的内容,所以干货很多;而GDC的分享很多其实没那么先锋,有些甚至没有展示其有技术含量的部分,很多分享都做成了开发总览或游戏优点展示。
相比起来Tango的这篇分享篇幅很长细节很多。里面提到的很多特性虽然不是最先进的技术方案,但确实深入细节知无不言,能感受到他们在做分享时的自信与自豪。(在知道结局后就更对其中的一些内容表示遗憾,有一种转喜为悲的感觉)
下周预计会更Tango这篇分享的下半部分,讲讲卡通风格的光源、各种阴影的处理与全局光照。

最后是资料链接:
GDC Vault的《3D-Toon-Rendering-in-Hi》
Triplanar Mapping 的一篇介绍

© 2022 3楼猫 下载APP 站点地图 广告合作:asmrly666@gmail.com