4 渲染太慢_[译] 崩坏3的卡通渲染实现方式拆解

a5db706d474f58cdb5800047dc29b9a3.png
本文仅限于学习参考交流,请勿做商业用途和随意转载。

好久没发文了。

之前一直都在忙着准备NDC的演讲,然后又出去旅游了一下,期间也自学了一些落下的功课,所以隔这么久才发新文。但这次给大家分享的主题非常有趣。

本文我会分析和实现Jack He先生在Unite2018上关于《崩坏3》:在Unity中实现高品质的卡通渲染的演讲内容。在实现过程中,也顺便研究了一下其他卡通渲染方式。

Unite讲座视频 - https://www.youtube.com/watch?v=egHSE0dpWRw

本文的目录如下。

1. 利用Floor实现卡通渲染

2. 利用Ramp实现卡通渲染

3. 利用IF实现卡通渲染 + Shadow Threshold

4. 崩坏3中的卡通渲染

那么,进入正文内容了!

其实,在实现1,2,3步之前,要先用灯光矢量(light vector)和法线矢量的点积(dot)来实现Lambert和HalfLambert。但,

120090492c01f420949dec9da931f777.png

这部分我之前已做过分享,本文不再赘述,详细请见下文链接。

https://blog.naver.com/mnpshino/221441167376​blog.naver.com

其次,关于如何实现轮廓线,此前也做过分享,不再重复说明,详细请见下文链接。

c451319f1704b64dce4c092bc83eff66.png

(https://blog.naver.com/mnpshino/221495979665)

1. 利用Floor实现卡通渲染

先来了解一下Floor函数的定义。Floor函数是指“向下取整”。(四舍五入函数是Round,天花板函数是Ceil)

Floor指“地板”,字面意思一目了然。大家可以理解为"下楼梯"。

0.1, 0.2, 0.3... 0.9等向下取整的话,就是0。大家可以把“向下取整”理解为直接去掉小数部分便可。

2d2cadd42e760d819dbd7ef2bb69677e.png

那么,从0到0.9999...的下取整就是0吧?我们先举个例子,分为5个阶梯来看。

HalfLambert取0-1之间的值,乘以5,就得到0-5。使用Floor函数,恰好直接就能得到不带小数点的整数0,1,2,3,4,5。刚才乘以了5,现在我们再乘以0.2(相当于除以5),同时把颜色Range重新设置为1,得到0,0.2,0.4,0.6,0.8,1。

4b4678f2d2d3c6a2f816105214232647.png

上面的图表虽然是3个阶梯...咱先不管这些,现在能得到上图的形状。用Shader实现一下。

31e9d005352d4eb806292c874620d1dd.png

在上面,我先任意将阶梯设置为5,但在实现过程中,添加 _StairNum变量的话,就能动态调整阶梯。这里需注意一下,不把光照模式(LightMode)写入为ForwardBase的话,法线可能会翻转。

4c45510e607395d2be7667d312976507.png

在Pixel Shader中,先乘以 _Stair阶梯,再用floor下降,再除以 _Stair阶梯,便能得到你想要的结果。

31bf2841fc1688ba5fe03f038bbb5ffd.png

调节 _StairNum便能得到上图的明暗阶梯变化效果。该方式的优点在于计算量小,明暗分界清晰,缺点在于推广应用难。

我们接着来看下一种方式。

2. 利用Ramp实现卡通渲染

该方法是Diffuse Warp(Warped Diffuse)方式,在这个方法中使用了Ramp贴图。

7b9c4211d04a0aa9444e158680a78416.png

贴图如上。

<军团要塞2>最先使用了该Shading方法,也颇具历史意义。不仅可表现鲜明的明暗,还能表现柔和的明暗表现。我也比较常用这个方法。

相关资料 - https://steamcdn-a.akamaihd.net/apps/valve/2007/NPAR07_IllustrativeRenderingInTeamFortress2.pdf

0d3a01e78290c058cc8a9702595a5c6c.png

大家可以结合上面的Ramp贴图和下方的实现效果一起看下,就能知道Ramp贴图是如何影响结果的。我们可以这样理解,采样RampTexture时,把HalfLambert值应用于UV上,HalfLambert值是暗的话,映射纹理的左边,亮的话映射纹理的右边。

除了横轴对应HalfLambert的方法之外,我们可以通过灵活处理纵轴,来得到不同的效果实现。

c70c5c587d65d2cd135b6f99837d20bb.png

如在<崩坏3 MMD>中,利用顶点颜色绘制(Vertex color painting),把UV的Y轴移到顶点颜色值,直接调整软硬明暗效果。(MMDshader和游戏内shader的实现方式不同。)

在<军团DOTA2>中,额外使用了一张Diffuse Warp Mask纹理,

9ac29987ee39ba917843a63b11c1157c.png

被遮罩的部分,通过采样ramp图来实现明暗渐变。把采样坐标值存入顶点色中更助于优化。

ccd99a3f40ac140beeb3247598d32f5e.png

参考Siggraph的Pre-Integrated Skin Rendering资料后发现,他在横向UV上使用NdotL,纵向UV上使用1/半径(曲率),

相关资料 - http://advances.realtimerendering.com/s2011/Penner%20-%20Pre-Integrated%20Skin%20Rendering%20(Siggraph%202011%20Advances%20in%20Real-Time%20Rendering%20Course).pptx

7f4527eded445d55a0c3988738a51b12.png

用法线偏导数除以位置偏导数的值。这里出现了微分的概念,大家可能会觉得有点难,其实大家简单理解为“变化量”就行。较之位置,法线的变化量可理解为在眼,鼻子,嘴附近增强了SSS的红润气色。(P.S:法线变化量大,说明凹凸明显,不平坦,这些位置通常肉都比较薄,比如耳朵、鼻子、眼皮、嘴唇这些地方,所以会更透光,透出的光带有血色,所以显得红润。)

fwidth的定义如下。

da21e0ae37380474ecfafd9e30d341de.png
*ret fwidth(x)该函数计算 abs(ddx(x)) + abs(ddy(x))。该函数只支持Pixel shader。返回值x是媒介变数的偏微分绝对值。*

此外,为了更方便使用,我们在纵轴上使用NdotV去采样BRDF图。现在我们来实现一下Ramp方法。

37727313d8d642ad15f32a686f1fc341.png

在属性中声明名为 _RampTex的sampler2D变量,便可使用。

float2(halfLambert, 0) <- 这部分是uv,目前纵轴是0,希望大家可以尝试用其他办法来做些变化,应该能得到很多有趣的效果。

d779558f67d56e7da651ce4d058f646f.png

我先用了上面的纹理,适用于球和石像上。

下面是全文的重点。

3. 利用IF实现卡通渲染

大家可能会提问,在shader里面用IF的话会不会太慢了,之前在实现蜘蛛侠风格的shader时,已经给大家分享过了Step()解决方法,详情请参考前文。

97410f1aa7c927401357783e70befdf0.png
https://blog.naver.com/mnpshino/221481588595​blog.naver.com

其实,IF方式的代码很简单。

4556b113537f9a7ac94f5033216e7979.png

如果HalfLambert值小于0.5,则乘以阴影颜色,并将其写入shader,即可。

c54766c382694bc67a45348c3ab3622a.png

Guilty Gear Xrd中也用了此方式处理。但是,如左图所示,AO乘以顶点颜色绘制的值,使用加权值。

23b1de92dfbbfb92957b6020c0c5a388.png

4. 崩坏的卡通渲染

<崩坏>是如何实现明暗呢?先来看下Unite上分享的内容。

d5f9f7145910088f58b2d9953b0c1f31.png

如演讲所述,崩坏中使用了Lightmap,这是崩坏in game shader的核心。把Specular贴图和Shadow Threshold贴图逐通道放入名为Lightmap的Mask纹理中打包,为了解每个通道的作用,我们将纹理分为R,G,B来看。

b47381cae1e2e765986041e387bae8ce.png

通过观察贴图和上面的模型,我认为R,B通道可以看成高光相关的通道,G通道用来调整明暗的权重。因为使用这种卡通渲染方法,高光的使用极度受限。它用于金属材质、弯曲部分和皮革材质的表现。

452bc24cf9ab8a1fd2e28b7c36121e40.png

R通道

仔细观察R通道,会发现一个材质中的所有数值都是一样的。这个材质中的光泽度全部处理成了一样的,所以我认为这是Specular Glossiness(也叫Power)。

d12851e5cf4244545153133fe9dc3ff3.png

B通道

B通道主要强调弯曲部分。可理解为是Specular Masking通道。那么,G通道的作用就非常重要了。

b9cc00fc228d0de31b2bb32b3d7f7a5d.png

G通道

G通道的底色(base color)是中间灰。中间灰就是0.5。

0.5...? 忽然灵感噼噼啪啪一闪而过(233...)。Threshold意思是“阈值”。G通道的作用就是阈值(Threshold),用于调节明暗。用Threshold贴图代替0.5。

59e0805ff14fac3dee064ad42a7de333.png

代码如上,非常简单吧。运行的话,

c423a22cfebbdc5c4580ef4891e3ae68.png
左边 - 运行Threshold / 右边 - 未运行

使用了Threshold的模型,受头发影响,用加权值调整脸部出现的明暗。

(只要超过0.2或0.1,就会变暗)

所以,我们可以通过不断调整,直至得到满意的阴影形态。在一些需要细腻表现明暗的部分,我们可以得到和法线贴图一样的效果。高光在运行R和B通道后得到的结果数值,仅在超出某个变量的部分,表现为高光颜色(SpecularColor)。通过这个方法就能得到崩坏游戏中的shader效果。

最终效果图

大家照这个方法来实现的话,会发现技术其实不难。而且就算不用Shader,它的基础模型的品质非常棒。虽然我们常说Shader有妙手回春之效,但<崩坏>让我重新意识到高品质的模型对于一款优秀的作品来说至关重要。因为如果基础模型的品质不行的话,不管shader如何厉害,也有无力回天的时候。同时我也再次认识到,卡通渲染这块真的需要TA和模型师通力合作才行。

那,我们下回再见,谢谢~~

[文章来源] 붕괴3 방식의 카툰렌더링 구현하기|作者 Madumpa

译 Qinfei

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值