崩坏三人物渲染分析

申明:本文的所有的算法以及资料版权归原公司所有,仅供学习,严禁商业使用!转载请注明链接!

背景

崩坏三的图形渲染是近年来手游卡通渲染的标杆,目前市面上的卡通类手游很少能望其项背。通常一款受欢迎的游戏出来很快就有大批同类型的游戏跟进,但是近一年的时间过去了,还没有出现效果能媲美崩坏的游戏,追根溯源还是因为其渲染技术不能被模仿。作为一个爱好者,为了学习其中的奥秘,特带着敬意去分析其渲染方法。采用的方法是采用工具抓取了其中一帧数据,然后人肉翻译了其shader算法,进而探求其中的奥秘。选取了芽衣这个角色来分析,因为做了很多subshader,在不同性能的设备上表现效果是不一样的,本篇使用的是小米5作为设备。

贴图模型分析

在人物渲染中,采用了高光以及两层阴影来模拟光照信息,同时,阴影之间不是采用重合来表现的,采用两层交错示(显示了阴影一就没有显示阴影二)。

整个渲染使用了两张贴图,一张是主纹理图,另一张是lightmap;这张lightmap的三个通道分别存储了高光mask、阴影mask以及高光信息;



lightmap


rgb通道存储值

阴影mask作用是根据其灰度值来控制阴影的交错显示,在这张阴影mask图中,灰度值最大的为138,这有点迷茫,为了配合算法?


阴影mask

模型是自带了顶点色的,这个顶点色不是为了调色之类的,而是用来控制阴影的显示,观察游戏主界面中人物的模型,可以发现人物脸上的光照只在某些地方显示,有些地方是不会出现,这些地方都是使用了顶点色来调节阴影强度,使其更平滑更真实。详情可以参考米哈游技术总监在unite 2017大会上的 [ 演讲报告 ].

第一层阴影

第一层阴影是根据half lambert光源,顶点色以及阴影mask共同作用的结果,算法如下:

fixed mask = lightMapColor.y * i.color.x;
mask  += saturate(i.halfLambert);
mask  = mask *0.5 +(-_LightArea) + 1;
intlightStep = step(1,mask);
mainCol.xyz = tex2D(_MainTex,i.uv).rgb;
fixed3 firstShadow = mainCol.xyz * _FirstShadowMultColor.rgb;
if(lightStep!=0)
    firstShadow = mainCol.xyz;
else
    firstShadow = firstShadow ; 

lightMapColor.y直接显示控制了阴影显示的区域,当其灰度值较小时,即显示第一层阴影。通过参数LightArea来调节显示阴影的区域大小。同时,为了根据视角的方向来变化阴影,特意加入了half Lambert光源强度值,就是为了根据视角来控制阴影的变化。定点色的i.color.x数值用来调节是否显示阴影。如上分析的那样, 有些区域比如脸部,某些部位是没有阴影的,那么这里就可以使用顶点色来抑制阴影。

第二层阴影

同理,根据第一层阴影的方法,可以获得第二层阴影:

fixed3 secondShadow = mainCol.xyz * _SecondShadowMultColor.rgb;
fixed secMask = i.color.x * lightMapColor.y + saturate(i.halfLambert);
secMask = secMask *0.5 + (-_SecondShadow)+1;
lightStep = step(1, secMask);
if(lightStep !=0)
    secondShadow= mainCol.xyz;
else
    secondShadow= secondShadow;

为了使两层阴影之间实现过渡,则需要将进行交错显示,而不是叠加显示,代码如下:

fixed sep = i.color.x * lightMapColor.y + 0.9;
int sepMask = step(1,sep);
fixed3finalColor
if(sepMask != 0)
    finalColor= firstShadow;
else
     finalColor= secondShadow;

高光

高光的计算方式如下:

float3 viewDir = -i.worldPos + _WorldSpaceCameraPos.xyz;
viewDir = normalize(viewDir);
float3 halfView = viewDir + normalize(_WorldSpaceLightPos0.xyz);
halfView = normalize(halfView);
float shinPow = pow(max(dot(normalize(i.worldNormal.xyz), halfView), 0), _Shininess);
float oneMinusSpec = 1 - lightMapColor.z;
oneMinusSpec = oneMinusSpec - shinPow;
int specMaslk = step(0,oneMinusSpec);
fixed3 specColor = _SpecMulti * _LightSpecColor.xyz;
specColor = lightMapColor.x * specColor;
if(specMaslk!=0)
     specColor = 0;
else
     specColor = specColor;

效果分析

直接上结果:

对与我这个对美术十窍通了九窍的人来说,实在调不出满意的颜色(囧o(╯□╰)o);但其阴影效果以及高光效果已经能随着角色的转动而变化。再看一下面部:

这张图是没对顶点色处理的图,可以看到,脸部的阴影变化很不自然。为了改变这种别扭的效果,可以通过修改顶点色值,建立mask来抑制不必要的光影;修改顶点色的方法,一是可以通过模型软件来修改,但这样很难立即看到修改后的效果,还有一种方式是直接unity中编写一个修改顶点色的工具,这样能更直观的修改顶点色;下面的效果是修改顶点色后的结果:

可以看到,脸部的阴影效果变得更加自然;

总结

在崩坏三的角色渲染中,并没有采用很多高深的渲染技术,而是通过各种非常技巧性的方法来进行渲染的;这让人联想到图形学上的至理:如果它看上去是对的,那么它就是对的。其没有使用很多复杂的算法来表现出满意效果,而是通过各种trick来实现想要的结果,尤其是在卡通渲染中,更是各种采用奇技淫巧来实现效果。后续文章中,将继续这款游戏中的各种渲染技术。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值