永远相信美好的事情即将发生 😊!

卡通渲染之高光(isotropy)的实现(URP)

分享 Mavis 13℃ 0评论

高光这部分的学习,我回忆一下,一开始是学习《unity shader 入门精要》里面卡通渲染部分,后来了解到各向异性这个东西,然后要做头发的时候,找到了米哈游的技术分享,然后又学习了一下Kajiya-Kay Shading Model 然后网上找找大佬写的教程和分析,最终也算是实现了

先来实现isotropy的高光吧,其中在unityshader入门精要中的内容在 http://tuyg.top/archives/567 我已经写了笔记了,可以去瞅瞅,代码基本网上都是有的,因为以前学的时候并没有真正理解每个函数每个值代表的含义,所以下面讲的是我自己的理解和分析,

高光顾名思义,亮度很高,哈哈哈哈

那么我觉得高光本身有两个决定性因素,一个是被照射点和光源的相对位置,还有一个是光源的强度

其中光源的强度很好理解,光源越强,那么这个点接受到的光肯定也就越多,也就越亮

其次相对位置需要想象一下,当一个点被光源垂直照射(从该点的法线方向照射过来)时也会比没有垂直照射时亮的多,这个其实根据生活经验也可以知道,毕竟大太阳的时候大家一般都不会直视太阳,遇到强光,会下意识转动自己,转动自己=改变法线朝向=改变法线和光线相对关系

所以这样就可以知道要做出高光效果,得先得到三个元素:点法线、光线方向、光线强度

其次还有一个因素会影响,那就是我们从哪儿去看,那就会再加入一个元素:视线方向

因为视线方向和光源方向同时会影响高光效果,所以就出现了半角向量,画个图就知道半角向量如何将两个方向的影响合在一起了:

根据这三张图,都可以看出来VHL三个向量是一种固定的关系,也就是V是L以H为法线反射的路径,也就是如果这个时候法线和H重合,说明视线就在看光线的反射向量,那看到的肯定比其他地方亮。

上述内容主要是为了帮我自己理解和能够真正的灵活运用这些向量关系,总之根据这些关系,就可以很好的理解高光模型了,至于具体那些经验系数为什么要加,有什么用,大家可以看这篇文章 https://zhuanlan.zhihu.com/p/144331612 ,写的很好,比如指数p,文中提到了:添加该项的原因很直接,因为离反射光越远就越不应该看见反射光,需要一个指数p加速衰减

specular = light.color.rgb * _SpecularCol.rgb * pow(saturate(NdotH),1/_SpecularScale);//_SpecularScale=0.003

这样写出来的效果是这样的:

然后因为卡通的光影效果一般是色块,所以根据NdotH的值,可以直接规定大于多少的时候为高光,可以使用step来完成该操作

float spec = step(_SpecularScale,NdotH);//_SpecularScale=0.998
specular = light.color.rgb * _SpecularCol.rgb * spec;

这样之后写出来的效果就是这样:

 

但是这样放大后会发现锯齿问题:

所以还需要对边缘进行平滑,我在之前的文章里提到了对边界进行平滑选择smoothstep,但是肯定只希望在一个非常小的边界内进行平滑,超过边界的该是0就是0,该是1就是1,所以使用fwidth函数,该函数定义为:

float3 fwidth(float3 a){ 
    return abs(ddx(a)) + abs(ddy(a)); 
}

https://zhuanlan.zhihu.com/p/95051582 该文章中有常用函数的大致用法和含义,里面也提到了fwidth

简单来说,按我的理解,用这个函数就可以获得像素级别的变量变化值,这个就可以拿来当做边界的大小,也就是smoothstep的ab值(对了说到这突然想到smoothstep和step原来是一家人,step对边界有严格的界定,而smoothstep会在两个值的范围内平滑边界,所以我觉得都是用来处理边界的函数,只不过一个边界是一条没有宽度的绝对的线,而另一个边界是有宽度的)

因此代码如下:

float w = fwidth(NdotH) * 2.0;//2.0用来缩放这个边界
specular = light.color.rgb * _SpecularCol.rgb * lerp(0, 1, smoothstep(-w, w, NdotH - _SpecularScale));//_SpecularScale

至于smoothstep的x值不是NdotH而是NdotH - _SpecularScale的原因是:需要一个值来控制NdotH 达到高光的范围缩放,就像前面用到的一样。

这样达到的效果如下:

看一下同一处边界的对比图:

好了,基本大功告成了,现在还有一个小问题就是,_SpecularScale是范围为[0,1]的值,当取到1的时候,高光还存在

希望能够完全消失,所以还需要加上一个控制的值

specular = light.color.rgb * _SpecularCol.rgb * lerp(0, 1, smoothstep(-w, w, NdotH-_SpecularScale)) * step(_SpecularScale,0.9999);

不过为了让_SpecularScale更能够符合它的名字,希望值为1的时候,高光范围最大,所以再修改一下代码就可以得到和unity shader入门精要里面一样的效果了

specular = light.color.rgb * _SpecularCol.rgb * lerp(0, 1, smoothstep(-w, w, NdotH-(1-_SpecularScale))) * step(0.0001,_SpecularScale);

最终的效果(忽视截图软件的问题,就当做白色的地方是全白的):

好了,这部分的高光就说到这了,下一篇讲一讲各向异性的高光实现

如有错误,欢迎大家指正!


Mavis , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:卡通渲染之高光(isotropy)的实现(URP)
喜欢 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址