unity 球体反射 unity 地面反射_右键


作为离开学校十几年的学渣,最近决定要改一改过分功利地追求工程进度,得过且过的烂习惯。从最基础的光照理论复习起来。当年的课本镇住题头先。

一般计算机图形学在讲解光照理论[1]时都是如下的顺序。虽说都是过时了的技术,但是你们fashion的PBR还是以这些老祖宗为基础的。魔改的时候,请注意这些是骨头动不了。

1.1 Shading 着色方式

1.1.1 Flat shading平面着色

这是渲染一个材质表面最简单高效的方式。每个面使用单个颜色来表现. 在Unity中并没有实现这种着色方式,你需要自己动手写一个简单的CG shader。如果你要的是lowpoly那种效果的话,记得别用光滑组,按三角面给出顶点法线。


unity 球体反射 unity 地面反射_插值_02


1.1.2 Gouraud shading 哥罗德着色

哥罗德着色方式被引入图形学用于提升颜色在光滑物体上的自然过渡。该方式计算每个模型顶点的光色,然后将光色插值到顶点所在的三角面.


unity 球体反射 unity 地面反射_unity 球体反射_03


这样的方式显然会比逐像素计算光色来的高效,但是由于大部分的材质并不只有漫反射,还有镜面反射,而镜面反射大都发生在多边形内部并非顶点上,所以该方式在表现具有细节高光的物体时显得非常糟糕.

Unity 自带VertexLit shader使用的该着色方式,请右键你的shader文件,在inspector中查看生成后的shader代码中的frag函数。

1.1.3 Phong shading 旁氏着色

如上所说大部分的材质表面并不是只有漫反射,还有高光反射。为了表现材质高光,旁氏着色被引入渲染方法中。在渲染多边形中的某一个点时,该点的法线为相邻顶点法线的插值。光色逐点计算.


unity 球体反射 unity 地面反射_插值_04


这能非常完美地表现一个球体,因为法线的插值正是符合球体的法线分布规律。

UNITY surface shader默认使用Phong shading,请右键你的mobile diffuse shader,在inspector中查看生成后的surf shader代码中的frag函数。

1.2 Lambert Lighting Model


unity 球体反射 unity 地面反射_光照模型_05


漫反射材质的特性是进入表面的入射光线会从一个随机方向反射出去。你可以把这个过程想象成为一个原子会吸收一部分光子后向另外一个方向发射另外一个光子,由于光反射方向的随机性,我们的观察角度变得不那么重要,唯一需要关注的就是光从什么方向进入这个材质表面。


unity 球体反射 unity 地面反射_unity 镜面反射_06


unity 球体反射 unity 地面反射_unity 球体反射_07


如果光从一个更接近垂直的角度进入表面,意味着更密集的光能量到达了材质表面。如果光从一个斜视的角度进入材质,同样多的光能量会覆盖更大的区域而单位表面接受到的光能量更少一些.如图所示,材质表面接收到的光能量以及它能反射出去的量 是与材质表面以及光方向之间的夹角成比例的。

这就是为什么我们需要材质表面的法线向量。法线与光方向夹角的余弦值决定了我们需要的照明比例。

在Unity中我们可以找到Lambert Lighting的实现如下:


//Shader代码 from Lighting.cgin
fixed diff = max (0, dot (input.Normal, light.dir));
fixed4 output;
output.rgb = s.Albedo * light.color * diff;


1.3 Phong Lighting Model


unity 球体反射 unity 地面反射_右键_08


正如我们之前提到的,材质表面并不仅仅只有漫反射,现实中的物体表面都会有一部分镜面反射,即部分入射光线会从反射方向射出。如果材质的观察者正好从反射角度来观察材质,会发现材质表面的一个区域会比其他区域更亮一些。注意观察者的位置是不变的。观察者可以选择观察材质表面的不同区域,但是高光反射只会出现在反射光线直射到观察者的区域。这样,一部分光被材质表面吸收并反射到了任意方向,而另一部分直接反射出表面,如果这个直接反射的部分为100%,那么这个表面就是一个理想镜面了.

我们仍旧可以使用观察方向和光线的反射方向的余弦值来定义这个光照模型,但是,由于余弦函数衰减得太慢,它在材质表面呈现的是一个大而松散的高光。Phong提议使用余弦值的n次幂来快速衰减余弦值,且不会影响函数0~1的值域,这个次幂在程序圈里被叫做shininess。由于计算量相比Blinn-Phong更大,Unity的默认shader中并没有使用该光照模型,当然你自己魔改一下也可以,高光点的位置会稍微有区别,不过,别杠行不?

1.4 Blinn-Phong Lighting Model

更接近真实世界的光照模型其实是Blinn-Phong模型,它计算高光的方式有些许的不同。在Blinn-Phong模型中,我们使用的是光的入射向量与观察方向之和与材质表面法线的余弦值来衡量照明效果,从图中可以看到,在光的反射方向与观察方向重合时,该向量与法线方向重合,余弦值为1,感受到最强的光,理论自洽没毛病。


unity 球体反射 unity 地面反射_unity 镜面反射_09


其他的一切不变,在Unity中的实现如下


//Shader代码 from Lighting.cginc
half3 h = normalize (light.dir + viewDir);
fixed diff = max (0, dot (s.Normal, light.dir));
float nh = max (0, dot (s.Normal, h));
float spec = pow (nh, s.Specular*128.0) * s.Gloss;
fixed4 c;
c.rgb = s.Albedo * light.color * diff + light.color * _SpecColor.rgb * spec;


本来还想写写PBS,最后发现我知道的那点皮毛都是从别的知乎大神那里学来的,还是等实践体会再深一点,再来总结吧。

Reference

[1]Computer Graphics: Shading and Lighting