unity 2020 怎么写shader使其接受光照?_UnityShader入门精要笔记6:基础光照模型B

4f47038642e91853a2ce12e481285349.png
路漫漫其修远兮,吾将上下而求索。——《离骚》

笔记当前使用的Unity版本:“2019.3.3”

笔记当前Unity最新的版本:“2020.1.0.Alpha 25”

1.概述

在基础光照模型A中,我们编写兰伯特光照模型,以及更深切的体会了一下逐顶点和逐像素的区别,在本篇当中,我们首先要把之前的兰伯特漫反射光照模型的一些问题解决掉,然后再提出新的光照模型。

对于笔记有任何问题或者疑问,以及笔记中所出现的任何纰漏与错误,欢迎大家在评论区指出,谢谢~

2.自己提出疑问

我们先来看一张图哦

be68c0cb13c487c6f17d175be6c706a0.png

很像纯黑色的着色器计算,其实这个就是我们上一节中编写的两个着色器所渲染的两个模型的背光面,它是纯黑的~~~。非常的糟糕,事实上,在太阳底下,即使是背光面也会有一定的亮度,并不是完全纯黑的,但是半兰伯特光照模型却无法解决这点。

2.1 为什么是完全的黑色?

事实上,如果是纯黑色,也就是颜色值为0(一定要注意,在着色器当中,颜色值的分量范围是0-1,0是黑色,1是白色),我们可以回想一下计算兰伯特漫反射模型的公式

fixed3 

我们之前提到了,如果光源的方向夹角与模型表面发现的夹角超过90°,就会变成负值,为了不让它成为负值,我们必须要使用max函数把所有的值限定在0以上。那么很显然,模型的背光面的法线与光源的夹角是超过90°的,并且它们被max(0,x),既然是负值,最终结果肯定就是0了,于是我们在背光面就得到了纯黑色。

2.2 如何解决

说实话,我并不知道如何解决,可能是我的水平还不够,单纯的从兰伯特光照模型上去解决这个问题可能难度太大了。但是有更多的trick技巧。(也就是通过一些视觉欺骗手段,让它看起来像是那么回事)

2.3 最最原生的Trick

我们可以为整个模型表面都增加一点亮度,亮光的地方可以更亮,暗的地方也不至于纯黑,不久解决了吗,说做就做,我们接着上一节的

“UShaderMagicBook/PerFragmentLambertLight_0”

着色器来增加一些新的代码,读者可以在下面的链接中找到这个着色器的源代码。

529324416/UnityShaderMagicBook​github.com
56170801e803a1f0724cc4c3d5b59558.png

和上一节的代码几乎相同,我只增加了三行代码。第一行是在diffuse计算的下面

a3f47998974bd4e3274707913eb0124f.png

定义了一个trick,由于我并不是清楚这个“一点亮度”是多亮才合适,我们不妨自己来控制,所以我们写一个_Scale变量,让它来控制trick的亮度。由于这个是一个用户输入,所以不要忘记在材质面板和在你需要使用的CGPROGRAM块中声明和定义。如下图所示

acd63ad85887650506c23e00735fc2ea.png

9a6887a46f11cf61be4cd2e5c2cd778c.png

当然啦,非常明显的是,颜色强度的调整是一个0-1之间的范围值,所以我们使用Range对象来作为它的类型。

16ed3b20786c0e35a35bbf9ac94147b4.png

最后,我们把这点亮度增加到模型的表面,再继续观察一下。

1eb0a812c45e31280972083e3bcad771.png

可以看到,当_Scale为0.2的时候,模型就有了一个更为合适的亮度值。

2.4 官方的trick

当然啦,其实官方提供了一个独特的变量来帮我们解决这个问题。那就是环境光(Ambient)。它也是内置在Unity渲染底层中的一个变量。即

UNITY_LIGHTMODEL_AMBIENT

表示环境光,一般来说,我们会增加环境光到漫反射或者高光反射的计算中,让它的亮面和暗面不会对比度那么强烈,操作起来也很简单,只需要把它直接加到diffuse上去即可。

还是刚才的代码,我们删掉刚才我们为trick增加的三行code。然后增加一行环境光的计算。

b3941dc1c70af0659392c625058be746.png

然后再回到Unity中观察结果

aa0b2d2dc0dc28177309220679181d59.png

现在的效果就好了很多。

3.乘法的多重含义

乘法是我们很早就会学习到的一种运算手段。如果单纯的两个标量相乘,还是非常好理解的,但是一旦乱七八糟的东西乘在一起,它们可能就具有了不同的意义,比如当一个矩阵乘以一个坐标的时候,就是一种变换。

而在Shader当中,我们经常会发现两种颜色乘在一起,那么两种颜色乘在一起代表什么呢?

其实稍微百度一下,我们就可以知道答案了,两种颜色相乘,其实就是用两种颜色作正片叠底。这个效果我们可以在ps中尝试一下。

3.1 正片叠底

我们先准备一张原始的图像,最好是色彩稍微丰富一些。

7c5ac92a6418b8d0b3254377a3864521.png

然后我们用蓝色去和它叠加在一起(用数学手段也就是乘在一起)

6bbf81acc0e04ef976df3462164872dd.png

得到的效果还是非常明显的。整个画面都变成了蓝色调。

3.2 在着色器中实现正片叠底

首先我们要知道,一般模型都有自己的颜色。虽然我们现在只能绘制出纯色,但是这种原理和手法我们一定要知道。为了改变模型的颜色,还是我们前面使用的那段代码,我们希望能够在一定程度上改变模型表面的颜色,于是我们在Properties语块增加一个颜色输入

2065f262ae986ec108968f2707e856d9.png

不要忘记在CGPROGRAM块中声明

82bd9d7534d438ea418260b6c3d6f8f5.png

最后和diffuse乘在一起。

b09779c79d99ac256edde84cb08d10b1.png

然后我们回到Unity中调整一下颜色并且观察其变化。

7b845d4305ad7436adaae3018f93cd46.png

观察后发现环境光这部分并没有任何变化,因为环境光并没有和我们的颜色叠底,所以我们也让环境光和颜色叠一下。

1c2d1ab91848e23226958f0319e30eda.png

然后再观察变化。

222a5bb0f7e3fdf6371e0fb2275f2649.png

芜湖~,看起来还可以。

至于颜色相加是什么意义,大家完全可以自行百度,没有那么复杂。

4.方法总是有的

前面说到了,我并不知道该怎么从兰伯特定律出发,去解决模型背面纯黑(纯黑会丢失背面的模型细节)的问题。但其实早在Valve开发《半条命》的时候,就提出了一种解决方法,被称为“半兰伯特光照模型”,也可以称为广义兰伯特模型。它的公式如下

和我前面所说的原生的trick很相似,它引入了两个新的算子,Alpha和Beta,其实也就是在避免计算出来的结果出现0值。自己创造函数其实非常有意思,有一些函数可以自己解出来,而有一些函数可以自己构造。这要求我们学好微积分,尤其是泰勒公式。由于这部分的代码比较简单。大家可以自己动手尝试一下,增加两个输入,然后按照这个计算方法,去改造一下之前的着色器代码。

5.高光反射模型

漫反射是一种基础的材质,如果模型的表面更为光滑,它是有一定的反光能力的。我们可以尝试利用一种比较基础的高光反射模型来实现它。在此之前,我们需要先了解一下它的原理。

5.1 Phong高光反射模型

高光的反射并不是像漫反射一样,四面八方的,当我们从不同的角度观察某个光滑的对象的时候,高光的位置也在发生改变,简单点来说,我们观察模型的方向,也就是视角方向和物体反射光方向共同决定了高光的位置和强度。

c9c6242239f634097392e57596ecb1e6.png

我们需要先根据入射光方向和法线方向计算出反射光方向,然后通过反射光方向的和视角方向夹角的余弦值来决定高光的强度。

5.2 反射方向

有了入射光和法线,我们可以很轻松的计算出反射方向,通过下面的公式

不过甚至不用自己计算,Unity提供了非常便利的函数 reflect,通过这个函数,可以直接计算出反射方向

(ps:由于这里的I指的是入射光方向,所以必须加一个负号,否则我们计算出来的反射角度就是反向的)

5.3 视角方向

视角方向是针对模型的,所以我们必须先有模型的位置,然后用摄像机的位置坐标减去模型的位置坐标,以得到视角方向。

摄像机的位置也是内置于UnityCG.cginc中的一个常用的变量,每次开始渲染流程之前,Unity会把摄像机的世界坐标位置填充到变量_WorldSpaceCameraPos中,由于摄像机位置是世界空间中的,所以我们也需要把模型的坐标位置传入到世界坐标中。

5.4 高光计算的公式

这里采用的高光计算方式也是一种经验模型,即Phong光照模型。它通过以下的公式来计算高光

其中I是光源信息,r是反射方向,v是视角方向,gloss是光滑度

有了以上的公式,和获取参数的手段,我们就可以开始编写着色器代码了。

5.5 Phong高光光照模型代码

Shader 

我们可以用刚才的这个着色器来初始化一个材质然后赋给一个模型观察一下效果。

af9af1e540c73c7e319981ac962fcfaf.gif

1.又是因为余弦值!

仔细思考Phong光照模型的话,如果视角方向和反射方向的夹角超过90°,那么高光就会完全消失,但是事实并不是这样的。

274a4a74af19d373d24cf7a882b289af.png

上图的左图中,是Phong可以处理的情况,而到了右图的话,Phong光照模型就露出了“经验模型”的马脚,因为它和反射方向的夹角超过了90°,然而事实却是,即使超过了,我们应该还是可以看到高光的。

迫于这个问题,就有人提出了Blinn模型,Blinn模型抛弃了依赖夹角余弦值的反射方向,而使用半程向量来解决这个问题。

即光线与视线夹角一半方向上的一个单位向量。当半程向量与法线向量越接近时,镜面光分量就越大。

0fbada990654e106cda12cf8ecab13cf.png

当观察向量与反射向量越接近,那么半角向量与法向量N越接近,观察者看到的镜面光成分越强。

这个半角向量就是光源方向和视角方向的相加后再标准化。

而Blinn光照模型也被称为Blinn-Phong光照模型。

Shader 

我们用Blinn-Phong光照模型和Phong光照模型做一个对比

5164dc6472f7af2c094be32a06b00871.png

可以看见区别还是非常大的。

ok,那么到这里,我们简单的接触了一下几种比较经典的“经验模型”。我自己在编写这个笔记的时候也会有很多新的认识和发现。而不单纯的是把自己以前记得笔记再叙述一遍。希望这个笔记也会对大家有一定的帮助。另外,本节使用的所有代码都放到了我的github上,大家可以在下面的链接中找到,如果你喜欢这个系列的文章,不妨点一手赞或者贡献一颗⭐,灰常感谢~~

529324416/UnityShaderMagicBook​github.com
56170801e803a1f0724cc4c3d5b59558.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值