本文初发布于zhihu
https://zhuanlan.zhihu.com/p/553209338
0. 前言
自两年前通关XBDE与XB2以来,笔者就对这个游戏系列非常着迷,不久前发售的XB3更是系列集大成之作,而它相比前两作也有长足进步,不禁让人感叹“这竟然是NS游戏?”;而笔者作为一名游戏开发者,当然也想更加深入了解其使用的技术方案,在研究之时觉得其内容还挺值得分享,因此便有了这篇粗浅之文。
本文大致目录
- 渲染管线总览
- 角色渲染相关
- 结语与其他
渲染管线总览
最终渲染图(1920x1080)
PreZ
平常只包括地形、石头与草,无角色
但特写镜头会有角色
大概是因为平常角色占屏幕比例较小,PreZ收益低,但人物特写的时候开启收益较大。
通常而言草与各种植物需要有PreZ以防止其Alpha Test破坏硬件EarlyZ机制,而石头与人物依情况开启。
GBuffer Pass
GBuffer 0(R8G8B8A8)
Albedo(RGB)960x544
笔触感(A)
Albedo(RGB) 1280x720
笔触感(A)
画脸的时候还特意加了一个类似笔触感的贴图
![[5[@J4PJ{EO6Z%X7NUIEZH.png
脸部延迟Pass结果
效果确实是有……但高桥似乎忘了自己是在开发NS游戏
该值为1
该值为0
总之看上去大概是个Ramp Offset或者扰动法线的什么东西。
在SD里验证了下这笔触图甚至不是四方连续的,高桥,你到底想不想好好做啊!?
GBuffer 1(R8G8B8A8)
960x544
R 猜测为金属度
G 猜测为光滑度
B
对草而言大概是“某值的相加/乘程度”,总之增大之后显得像自发光一样。
原值
原值*100
对角色而言应当是用于决定Ramp图中Y轴取值,可能也趁此区分了不同部位:
B
原值渲染结果
原值*2渲染结果
A 不明确,只能猜测
草的延迟Pass根本没有取A通道,还画个值上去干啥呢,可能又在哪个角落里用到了吧。
A
原值渲染结果
原值x10
原值x100
原值x1000
有些类似“值越大则越倾向于原Albedo值”,但似乎也不完全如此。
对头发而言似乎是高光强度。
原值
Gbuffer1 A通道为0
GBuffer 2(R10G10B10A2)
乍一看好像是个比较普通的屏幕空间法线Gbuffer,其实不然。
它B通道存的是AO
而A通道内容较不明确。
由于法线是一个三维单位向量,有其三维中其中两个值以及另一个值的符号值(正或负)即可还原出法线值;但譬如Spheremap Transform之类的法线编码算法其实是不需要额外存符号位的,xb3这算法可能是将半个球面进行投影,而剩下半球用符号位表示,提升一点精度?笔者对这方面内容实在算不上熟悉。
A作为一个符号位存储
A为原值
A为1 - 原值
A为原值
A为1 - 原值(这一张效果还是比较明显的)
GBuffer 3(R8G8B8A8)
RGB 960x544
乍看可说是极其诡异的一个GBuffer。
R
G
B
这张GBuffer的RGB通道单独看都不知道是什么,笔者一位不愿透露姓名的友人调试后发现这rgb三个通道为编码后的线性深度,也就是-PosVS.z,还原后如下图:
或许是被映射到0-1的非线性深度无法满足雾、后处理等算法的精度要求,才需要出此下策……这就是超远视距的后果吗?
float和rgb24转换的类似算法可参考这篇ShaderToy:
https://www.shadertoy.com/view/WsVGzm
那么接下来是A通道:
A
人身上大多数区域的值为0.5
A为0(还有部分亮部应当是边缘光)延迟Pass输出结果
A为1时延迟Pass输出结果
基本可敲定对角色而言便是影响Ramp的取值,可以理解为类似AO,为0就必为暗部,而为1就更容易为亮部(但不一定为亮部,大概只是把halfLamber的值给乘2了而已)。
于草而言,似乎也是相加了某值,
原值
值为0
值为1
感觉变化实在不明显,或许草也就是顺便蹭一下这个通道而已,不用白不用;比较重要的还是角色那个类似AO的用法。
GBuffer 4 (R11G11B10)
Emissive 960x544
Velocity Buffer(R10G10B10A2)
之后用于TAA的Velocity Buffer。
RGB 960x544
RG调整映射范围后
B
又在标记头发和脸,或许是让脸和头发受TAA影响小些?但为何不同人的值不一样呢,姑且懒得计较了。
A
应当与法线类似同样是记录正负符号位,用两个通道分别给R与G通道记录了符号。
虽说算上这个符号位,每个通道也就11位……作为Velocity Buffer勉强能用。
Specular Mask(R8G8B8A8)
在头发的GBuffer Pass时直接输出到一张RT上
960x544
1280x720
似乎对于卡通渲染中角色的风格化高光必不可少,在蓝色协议的技术分享中也有见到类似的GBuffer:
大伙真是拼了老命也要让角色走延迟。
540p也确实是有理由的。
Depth/Stencil(D24/S8)
经过重新映射后的深度值
同时也用模板值区分各个不同的部位,如普通PBR场景物件、角色身体、头发等。
GI Pass
根据之前的GBuffer以及反射探针计算GI
其中tex3即为反射探针集,共192面,而根据一个反射探针有6面计算可得一共用了32个反射探针……不过每个的分辨率都极小,单面64x64而已。
会输出两张RT,看上去一个是Diffuse GI,一个是Specular GI
Diffuse GI
Specular GI
Shadow Caster Pass
执行近处动态物体(角色和怪物)的Shadow Caster Pass
1024x1024
没错,只有近处动态物体(人物)执行了Shadow Caster。
Screen Space Shadow Pass
经 @GuardHei 点拨,XB3的阴影方案应当与XBX所使用的相差无几,Monolith将其称之为“Shadowmap Cache化”。
用简单而不太准确的话概括,就是将Shadowmap每级联都划分为3x3的9份,当触发shadow更新时才会更新其中一小块。
当然其中实现起来有许多的细节要考虑,看上去也是很吃显存。
在此只展示XBX在cedec2015的其中部分分享:
Shadowmap Cache化
以当前位置为中心渲染Cache Buffer
划分为9块
生成另外两个级联
每个CacheBuffer使用的相机高度(Y)相同,仅移动XZ
从cache中取用部分作为Shadowmap
那么xbx的阴影内容先告一段落,我们回到xb3
输入了3张场景级联ShadowMap,然后再结合上一步角色的ShadowMap进行阴影判断。
三张预烘焙的Shadowmap
三张Shadowmap均为1024x1024,格式为D16
阴影的RT格式是R8G8B8A8,但A通道没有内容.
R通道放的是角色以及场景的阴影结果,G通道放的是只有角色而无场景的阴影结果,B放的是只有场景无角色的阴影结果,大概之后也会对不同的阴影做什么操作吧,不过感觉也无关紧要就是了。
这也解释了为什么场景阴影不会随着时间变化而变化,因为NS的机能无法支持高桥给场景搞实时ShadowCaster,说到底这种超大场景还实时级联阴影的话开销极其爆炸。
而各个大时间段之间不切阴影方向大概也是为了不卡顿而考虑。
AO Pass
下一个Pass应当是AO Pass,在此之前似乎根据ddx以及ddy算了个未平滑的的屏幕空间法线,但这RT也并没有出现在后续的AO Pass Input中,不知是不是截帧有问题。
于是它Input只有深度图,而输出如下图
240x136
看Shader中只采样了5次深度图,第一反应是这采样数也太少了,还有这四分之一分辨率输出也太糊了。
具体是HBAO抑或GTAO,因为真不想看汇编shader,不得而知 。
之后会做模糊后再与前一帧的AO进行混合去噪,最后输出480x272。
Deferred Lighting Pass
于是终于来到了延迟Pass,把之前计算所得的各个GBuffer、阴影RT、AO RT、GI RT等全部输入。
带宽芜湖起飞。
PBR Pass
人物Pass(不包括头发)
植被Pass
头发Pass
于是头发刚画完,又抠出来加个后处理:
应该是用了类似SNN的算法以模拟“水彩”效果,xb3中还有兰兹大衣的绒毛也经过了该处理,在破晓传说中类似这种后处理更是被推广至整个游戏场景,被宣传为“Atmos Shader”,或许也值得其他游戏借鉴学习。
SNN算法可参考:
www.shadertoy.com/view/MlyfWd
Skybox/Cloud Pass
于是之后开始绘制天空盒、体积云等
普通的面片云
1/4分辨率体积云RT(240x136),似乎是作为最远景
并入Frame Buffer后
另一张体积云RT(240x136)
并入Frame Buffer后
Transparent/Forward Pass
这个队列不仅渲染半透明物体,甚至远景的不透明物体也在这时候绘制,如远景的天涯巨剑,大概跟Additional Light无关的玩意都可以扔这里。
Instance的半透明发光面片
天涯大剑,输入Albedo、法线后直接前向绘制到FrameBuffer上
顺便看了下这个大剑的PosCS输出,其中w便是深度.
我们仍未知道那天xb3所使用的相机视距数值。
当然,这种独特的远景物体,大概也可以重新设置相机视距后再提交渲染。
在另一个截帧中可发现半透明粒子在半分辨率的另一张RT上渲染,倒也是个比较常见的优化overdraw的手段。
TAA/Post Process
之后便是TAA以及各种后处理,其中涉及多个RT的创建与Blit等,截帧的内容也开始愈发混乱起来,比如其中某个看似是LUT调色的Input与Output:
总之,笔者难以保证之后的内容的准确度,只能尽量从正常的截帧中选取内容。
TAA
当前帧
历史帧
混合帧
当前帧清晰但是锯齿也清晰,而混合帧整体显模糊但也切实消去了锯齿……
DOF 景深
一个半分辨率且模糊后的RT
CoC Buffer,但是其中黑色区域没有存负值,也就是没有区分“前后景”。不知是截帧问题还是本就没做区分?
DOF结果
Bloom
提取亮部后进行数次Downsample
之后截帧的内容极其混乱,几乎只能看出做了个LUT。
总之最终本帧输出如下:
TAA及后处理前
后处理后,绘制UI前
同时这个草原的TAA会将本帧进行升采样,也就是TAAU,而播片时的mio截帧中TAA似乎并没有做升采样这一步,因此草原帧输出为1080p,mio那一帧为720p。也是笔者感到非常迷惑的一处,也无法排除是截帧问题……
总之,渲染管线至此终于是大致过了一遍。
其实有些内容此次并没有看出来,如笔者在游玩时发现似乎存在自动曝光以及体积光。
角色渲染相关
相信各位玩家都看得出来,此次xb3相比xb2与xbde,进步最为明显的就是它的角色渲染。
示例图
较有趣的部分
就个人而言,截帧前最关心的几个细节是脸部的明暗计算方法、“厚涂”风的头发、脸上的高光点、刘海阴影。
关于头发的独特风格实现在前文已有提及,是做了类似SNN的后处理,当然实际上是不是SNN并不清楚。
一开始以为脸部的明暗是类似sdf或者赛马娘的那种trick方法,但想了想这是延迟渲染,想太trick也有点难,一截帧发现它硬是整了张法线贴图……
脸部使用的法线贴图
法线输出结果
倒也能理解为什么感觉游戏中角色很容易就“阴阳脸”了,毕竟法线图能力有限,做不了很平滑的过渡。至于是烘的还是画的,反正国内应该没人用这种方案,姑且不关心了。
角色渲染使用的ramp图(256x256),可以说是相当复杂
应当是为了兼容各种身体部件、不同角色、各个场景的早、午、暮、夜四个时间段,甚至不同天气,最终导致ramp图如此复杂,也算是延迟渲染卡渲之痛吧。
而国内目前流行的角色走前向渲染就避免了这一点。
xb2时的ramp图就已经趋向复杂,没想到xb3更是夸张(图出自CGWORLD2018年8月号)
角色的衣服、首饰等应当是魔改PBR,让漫反射经过了Ramp图映射,其他大概与标准PBR相差不多,不过这也只是没看代码的纯猜测,可信度不高。
刘海阴影是用面片做的,在GBufferPass之后作为半透明物体前向绘制:
上一次看到这种做法好像还是在MMD的模型里,果然还是工匠克算法。
而在人物延迟光照Pass之后,又加了一些半透明物体以“画龙点睛”
延迟光照结束时的人物效果
增添脸部高光点
直接用半透明面片点了几个高光点,个人感觉它还会根据视角做一点偏移:
比如从左看脸时高光也会偏左,下嘴唇高光位置变化尤其明显。
这一张侧脸特写中,鼻尖高光的偏移与“浮空”十分明显
增添虹膜细节
增加反射
而这个反射真的使用了反射探针,虽然笔者在游玩期间根本没看出这个反射的变化……
最后点上眼睛高光
那么至此也大致将本作角色渲染中比较独特有趣的部分聊过了。
结语与其他
客观而言,这些技术方案实在不像一款NS游戏应该拥有的:超远视距、完整的延迟渲染管线、常增加管线负担的风格化角色渲染,xb3这实在是戴着脚镣跳舞;同样作为一名游戏开发者,我不禁心生敬意。
一开始本是单纯想研究一下角色渲染才截帧,结果因为其他玩家朋友强烈要求让笔者分析一下整个游戏的渲染(顺便问问到底为什么画质这么糊),索性就趁此机会学习一下这个最不像NS游戏的NS游戏所使用的渲染方案,但笔者既非图形程序也非引擎开发,只是一个写写破shader的初出茅庐TA罢了,个人能力所限,能分析的技术只是这个游戏的冰山一角,而文中许多基于猜测的内容很可能错误甚至牛头不对马嘴,还请多包涵。
也希望各位不会被笔者误导,可以亲自看看xb3的渲染流程,因此将截帧分享出来(使用yuzu模拟器OpenGL模式,Xenoblade3 1.00 ver):
链接:https://pan.baidu.com/s/18dbrPCAXGrg_fsZF9FiF8g
提取码:xbnb
参考资料:
- Hallucinations re: the rendering of Cyberpunk 2077:http://c0de517e.blogspot.com/2020/12/hallucinations-re-rendering-of.html
- CGWORLD (シージーワールド) 2018年 08月号
- float to RGB to float :https://www.shadertoy.com/view/WsVGzm
- SNN/Kuwahara Filter :https://www.shadertoy.com/view/MlyfWd
感谢您阅读完本文,若您认为本文对您有所帮助,可以将其分享给其他人;若您发现文章对您的利益产生侵害,请联系作者进行删除。