一转眼已经有一个多月没更新了额,时间过得真快。今天终于决定要更新Part 2了。
----那我们就继续吧。
上篇已经讲完了漫反射、高光和光照贴图,本篇将会介绍角色背光时边缘光与基于阈值图的面部阴影的实现方法。
3.4基于Fresnel原理实现背光边缘光
我们知道,将光照方向与物体表面法线方向点乘,可以获得光照方向越接近表面法线方向,计算值越大的结果。我们可以使用这个结果来配合smoothstep等函数来区分物体的亮暗面。
----那么请想象一下,如果我们将视线方向与物体表面法线方向点乘,可以获得什么结果呢?
是不是相当于:我们的视线就是光源,直视哪个片元,哪个片元的点乘结果就越大。
(我们说的视线方向,指的都是指向眼睛的向量方向。)
现在我们将这个计算的结果先使用Saturate函数控制在0-1之间。然后用1减去这个值,将它反过来。
我们就可以得到如下结果:视线越垂直于片元表面,那里的值就越接近0;视线与片元表面的法线方向越偏离,甚至相反,那里的值就越接近1。
将这个结果直接输出,我们来看一下是什么效果。
可以看到,角色的边缘光效果已经有了“雏形”。
以上效果使用的原理便是著名的“Fresnel菲涅尔”。
但是目前来看,这个效果还是有些过于“浑浊”。为了更加接近“卡通绘画”的效果,我们需要让边缘光更加生硬,过度不要过于柔和。
使用的手段十分简单,和之前进行二值化的操作一模一样:使用smoothstep。
我们将以上计算的结果作为smoothstep的第三个参数,然后声明两个暴露在外的float类型参数作为smoothstep的前两个控制参数,将黑白区域进行平滑二值化。
以下是经过smoothstep以及调参之后的输出结果:
OK,这就是我们想要的效果。
下面要对这个效果进行一个简单的处理,让角色只有在背光时,才会出现边缘光效果。
这个处理的手段十分简单,我们直接创建一个向量,让它指向角色身后,然后让光源方向点乘它。这样就可以获得:光源越直射角色身后,点乘结果越大的效果。
要创建这个背身向量,我使用了UnityObjectToWorldDir函数,它可以接受一个模型空间的向量,将它转换到世界空间下,因为我的光源方向是在世界空间下计算的。我们在进行向量运算时,一定要确保它们在同一个空间。
float3 Front = normalize(UnityObjectToWorldDir(float3(0, 1, 0)));
//获得世界空间下的角色面朝方向的向量,(0,1,0)是角色模型空间的面朝方向的方向向量。"-Front"就是背身向量
接下来,我们将这个值使用Saturate函数限制到0-1之间,然后直接乘以之前计算的边缘光的值。
这样一来,当光源方向不从角色身后照射时,就不会有任何边缘光效果了。并且,当光源从角色侧面慢慢