首页 > 代码库 > 解读Unity中的CG编写Shader系列6——漫反射

解读Unity中的CG编写Shader系列6——漫反射

如果前面几个系列文章的内容过于冗长缺乏趣味着实见谅,由于时间原因前面的混合部分还没有写完,等以后再补充,现在开始关于反射的内容了。

折射与反射

在物理世界中,光的反射与折射往往是同时存在的,光源由真空或者空气中射入一种材料,光在进入这种材料的同时就发生了折射,折射的程度与各个介质的折射率有关,使光的传播路线偏离原来的路线;

继而如果光在通过不同传播介质的表面时,会像乒乓球一样弹回来,我们人眼能够看到东西,都是因为东西会反射光源,如果一种物质无法反射光,或者没有光源,我们就看不到东西。同样对于不同的材料,光的通过性越高,反射越不明显,比如玻璃(但是玻璃同样会有一定的反射,不然我们是看不到玻璃的,实际上我们看得到);光的通过性越低,反射越明显,比如水面,我们可以看到水中的鱼(光从水射到鱼的皮肤,鱼的皮肤发生了漫反射),也可以看到自己的倒影(水面发生了镜面反射)。


镜面反射与漫反射

那么镜面反射与漫反射的区别就是光碰到的物体表面被弹回来的过程中,表面的平整程度,越平越接近镜面反射,越粗糙就是漫反射。我们现实生活中看到的绝大部分东西的表面都是粗糙的,所以都是漫反射。

在讨论漫反射之前一定要回过头来复习一下我们的物理科学中的镜面反射与折射,因为漫反射可以拆分为无数个微小的镜面反射(就像圆形可以看做无数条边的正多边形),毕竟游戏世界也是为了模拟物理世界,游戏引擎必须要符合物理规律。画个图回忆一下:



上面这张图我画的不是很好,我需要说明的入射光线与法平面的夹角是入射角,反射光线与法平面的夹角是反射角,入射角永远等于反射角,图没画好。

同理对于凹凸不平的介质交汇面,我们可以无限细分为无数个法平面,对于每一个表面的点的反射情况,我们都可以沿着当前点做一个切面与物体表面在该点相切,得到一个法平面。所以镜面反射的规律可以直接适用到漫反射。示意一下:

我们对于介质B的表面的某一点的反射情况进行分析的时候就在这个点做一个切面与表面相切于这个点,同样得到了这个位置的法平面、法线、入射角、反射角

好了复习到这里了,在前面的系列中我们已经解除到了法线与法平面,我们将我们的三维物体看到上图的介质B,那么黑色曲线部分为它的部分表面,则这个表面上的每一个点的法线的朝向都不相同,因为每一个点做切面切与这个表面得到的平面不一定平行,所以在CG中mesh信息中传递过来的每一个顶点都有一个normal值,即法向量。

法向量就是我们的法线中切点往介质A的方向上的单位向量。

反射过程

好了 接下来我们将前文提到的入射光线、法线 替换为入射向量L(表面到光源的方向为正方向)、法向量N。
那么我们的物体表面发生漫反射时,漫反射的量即是由入射向量I与每一个顶点位置的法向量N确定的:
diffuse=L·N
这个过程是向量I与N进行点乘,其过程为:
diffuse=|L|*|N|*cos∠(L,N)

由于我们直接讨论入射向量与法向量的单位向量,所以两个绝对值为1,那么漫反射duffuse=cos∠(L,N)

由此我们可以得到单位向量的入射光的漫反射强度diffuse的值域为:[-1,1]
由余弦曲线可以得知,diffuse在夹角为0~90°为正,90°~270°为负,270~360°为正

对于cos∠(L,N)超过90°的情况,我们认为入射光已经在介质表面的另外一面了,这种情况我们不考虑,一般来说漫反射都是在外表面进行的。

所以我们的diffuse当夹角超过90°时便认为反射量为0,所以我们对该式子修改为:

diffuse=max(0,cos∠(L,N))

即对入射向量与法向量的夹角的余弦 与0 取最大值,保证了反射量不会出现负数。

接下来让我们从物理和数学世界回到CG世界中

用CG编写漫反射Shader

在编写我们的第一个单光源漫反射shader之前,先说明一下我们需要的几个参数的来源:
1.光源的位置由unity的内置uniform参数 _WorldSpaceLightPos0给出,由此我们可以计算出每个顶点的入射向量
2.光源的颜色由uniform参数_LightColor0给出
3.法向量直接通过顶点着色器的输入参数中的带语义NORMAL来获得
有了入射向量、法向量、光源颜色,我们就能在顶点着色器中计算出每个顶点位置的反射量,并与光源颜色和材料颜色相乘得到最终表面实际着色的颜色。
用户自定义的参数: 
1.材料颜色,通过shader的property定义






解读Unity中的CG编写Shader系列6——漫反射