UnityShader崩坏渲染解析系列(2)--明暗计算

身体渲染

  • 什么是明暗
    • 处理明暗
    • 崩坏处理
  • 资源解析
  • 总结

什么是明暗

与实时渲染不一样的地方在于,日式卡通渲染阴影过度有明显的分界线。
UnityShader崩坏渲染解析系列(2)--明暗计算_第1张图片

没错,左边的明暗变化有一条很明显的交界线,而右边的是有过渡效果的。日式卡通基本采用左边的方式。
可能会有人会说好丑啊,确实因为日式的漫画的明暗变化都是由艺术家完成的,可以说是漫画不可或缺的灵魂,而作为程序员只能按照一定的规律生成阴影,我们按照入射角、视角、法线的关系生成对应的明暗面,肯定 无法满足艺术家的需求,他们肯定会说哦,nonono…你们这里这里不能这样这样,那里那里要那样那样。。。然后我们说,哦,nonono,他们这样是因为那样的。。。。哦,nonono。。哦。。。八嘎。

处理明暗

双方都有道理,都是不能减少的,那么有的游戏会采用的策略相对简单粗暴,美术直接把阴影画到贴图上,那样效果就一致了。但是这样的效果在某一个角度下看上去是完美的,一旦视角发生变化,或者角色旋转就会发现这个明暗关系跟场景不搭,动态效果极差,但是如果我们是3D游戏,显然必须要高大尚,然后这个方向继续延伸,搬砖员登场,我们再此基础上加上程序运算的阴影。由此第一个方案诞生了,美术颜色贴图+程序阴影。显然这种方案制作相对简单,容易理解,但是效果却差强人意,因为美术做的阴影在任何时候都是一样的,可能在灯光直射的情况的这些阴影是不需要存在的。而程序的阴影虽然看起来可以有动态效果,但是美观角度上却可能让美术扣分了。

崩坏处理

为了解决这种尴尬的处境,大佬们使用了遮罩图来处理,明暗关系单独使用贴图处理,程序员使用公式计算阴影,只不过计算的这个地方是不是阴影会通过遮罩图决定。那么这样一来,美术可以按照自己的想去做阴影,又可以实现动态的效果。

资源解析

按照我们上面的理论,崩坏有一个阴影的遮罩图,遮罩图的G通道就是处理的明暗计算,如下:
UnityShader崩坏渲染解析系列(2)--明暗计算_第2张图片

通过这张图和Lambert的光照系数,共同计算出阴影阀值。代码如下:

 //2.采样遮罩图--g通道确订明暗关系
fixed3 lightTexColor = tex2D(_LightMapTex, i.uv.xy).rgb;
//return lightTexColor.g;
fixed3 secondShadowColor = baseTexColor.rgb * _SecondShadowMultColor.rgb;
fixed3 firstShadowColor = baseTexColor.rgb * _FirstShadowMultColor.rgb;
//2.1 通过lambert、阴影衰减和外部设定的明暗面阀值确定明暗面
//-----通过顶点颜色和遮罩图的g通道的乘积来表示,此片元成为暗面的倾向
fixed realMask = lightTexColor.g * i.color.r;
float t1 = (realMask + i.lambert * atten) * 0.5f;
//进行二分色,可以优化使用平滑处理
t1 = 1.0f - step(_SecondShadow, t1); //smoothstep(_SecondShadow, _SecondShadow+0.03, t1)				
		
//计算第一次二分色的颜色
secondShadowColor = t1 * secondShadowColor + (1.0f - t1) * firstShadowColor;
float4 outCo;			
//对应的做了映射x=1.2x-0.1  y=1.25y-0.125
fixed2 expandMask = realMask * fixed2(1.2f, 1.25f) + fixed2(-0.1f, -0.125f);
//对暗面权重值做映射之后的阀值处理
t1 = 1.0f - step(0.5f, realMask);
t1 = t1 * expandMask.x + (1.0f - t1) * expandMask.y;
//--上面是做了分段的解析t1=1.2x-0.1 {x=[0,0.5]}    t1=1.25x-0.125 {x=[0.5,1]}				

//将设置的阴影和光照信息结合,本次进行的
t1 = (t1 + i.lambert * atten) * 0.5f;
//再次进行二分色
t1 = 1.0f - step(_LightArea, t1);
//这里是在阴影处添加了另一个阴影颜色
firstShadowColor = t1 * firstShadowColor + (1.0f - t1) *baseTexColor;
//处理第一次二分色和第二次二分色的叠加。暗面倾向小于0.09的定义为二次暗面。
fixed t2 = 1.0f - step(0.09f, realMask);				
//t2阴影部分为1,亮面为0,而这里亮面使用了
fixed3 diffuseColor = t2 * secondShadowColor + (1.0f - t2) * firstShadowColor;

outCo.xyz = diffuseColor;
return outCo;	

有代码我们看出,G通道的数据只是一种明暗倾向图,实际操作时会和Lambert计算出的光照系数以及阴影衰减(这个源码中是没有的)混合来计算明暗的差值。第二次做二分色的时候做了一次映射,[0,0.5]对应[-0.1,0.5],[0.5,1]对应[0.5,1.125],单从结果上看就是再次加深了倾向,以0.5为分界,是小于0.5的地方暗面敏感度更高,大于0.5的地方敏感度更低。这种操作更深层次的含义就参详不出来了。然后就是对两次二分色做叠加,以0.09为分界。小于的部分为阴影2,[0.09,_LightArea]的部分为阴影1,其他部分为默认色。致此,明暗计算完毕,我们看下效果(为了更直观,去掉贴图颜色):
UnityShader崩坏渲染解析系列(2)--明暗计算_第3张图片

遮罩数值(mask.g*vertexcolor.r) 颜色获取
0-0.09 第一次二分色计算的颜色,图片中绿色
0.09-1 第二次二分色计算出来的颜色,图片中绿色之外的颜色

两次二分色的计算对比:

二分色系数 对比数值 Mask计算
_SecondShadow (Mask+lambert * atten)*0.5 Mask= _LightMapTex.g*vertexcolor.r==>[0,1]
_LightArea(个人理解_FirstShaow更形象) (Mask+lambert * atten)*0.5 令 x=_LightMapTex.g*vertexcolor.r ; Mask=1.2x-0.1 {x=[0,0.5]} Mask=1.25x-0.125 {x=[0.5,1]}

OK~~明暗计算,其实到这里就结束了,这篇很低产,确实没啥动力写东西了,觉的还是有点浪费时间。无聊的时候写写吧,当个笔记还是不错的,接下来分享的就是高光,Bloom,以及Dither效果、SpecialSstate、RimGlow、Distortion。其实源码中还有一部分是LUT纹理查找的以及双线性插值计算的一些方法,主要是角色颜色贴图的,有时间的话可以分享一下。

总结

分析可能有错的地方,如果有错还请多多指出。

你可能感兴趣的:(UnityShader崩坏渲染解析系列(2)--明暗计算)