目前为止我们学会了什么?

相机和物体 -> 变换坐标到原点 -> 拉伸后映射成2D图像 -> 对2D图像进行光栅化:滤波、采样、后处理,最终变成屏幕上的像素点

现在我们引入着色

着色 Shading

什么是着色?

着色在普世意义上,是对明暗、颜色进行绘制。

而在图形学中,着色是一个对物体应用材质的过程,正是材质的不同才导致颜色不同。

理解一个简单的着色模型

Blinn-Phong Reflectance Model 是一个常用参考的光照反射模型,它分为以下:

高光 Specular highlights:一根光线打到光滑平面(比如镜面)上,会往镜面反射附近去反射。

漫反射 Diffuse reflections:一根光线打到粗糙平面(比如墙面)上,被反射到各个地方的情况。图中茶杯从浅黄到深黄的变化。

环境光照 Ambient lighting:是由间接光源组成的光源统称。图中光线并没有直接打到箭头处,而是打到桌面上被反射,反射光再打到了箭头处。

开始前先定义一个“着色点” shading point

虽然反射到的面有曲面、有直面,但我们只观察一个最小的反射点,那么曲面的极小也可以当作直面。这个被观察的点我们叫做 shading point ,同时再定义几个其他的属性。

单位向量 v:和相机的连线。

单位向量 n:反射面的法线。

单位向量 l:光照方向。

表面参数:颜色 color、反射强度 shininess、…

着色的局部性 shading is local:我们着色只关注光线照过来这个点该怎么着色。比如因为遮挡产生的阴影,着色器是不考虑的。

接下来我们根据定义好的观测点 shading point,来分析上面提到的3种反射情况。

漫反射 Diffuse reflections

一根光线打到一个点上时,会被均匀反射到四周。

通过观察就可以知道,着色点的法线n和光照打来的方向l的夹角θ,会和这个着色点接收到的光照能量成反比。而这个反比有一个推导,叫 朗伯余弦定理 Lambert’s cosine law

朗伯余弦定理的推导中,将点光源发出的光的轨迹视作一个个圆球体,而根据能量守恒,只要在真空中传播没有发生损耗,这些圆球体上某点的能量和圆球体表面积(也就是半径平方r)成正比。

最终公式如下:

从上面也能得知一件事,就是漫反射与观测方向v无关 => 通俗点说就是某点被漫反射了,即使你在不同的位置看它,它的着色情况也是不变的。

公式里还有个系数kd,这是颜色系数,乘以漫反射光的强度(右边的值),得到最终显示的颜色。

高光 Specular Term

高光与v有关。当你的观测角度v,和反射光的方向R一致或者接近的时候,你才能看到高光。

而观测角度v和反射光R的夹角有多大,又可以转化为更简单的计算:求l和v的中间向量,然后和法线n求夹角。 这一个计算转化算是优化了,计算R和v夹角的方式叫做Phong模型,而我们这种优化的方式叫做Blinn-Phong模型,推导过程和最终公式如下:

公式里还有一个问题,就是为什么要有一个P次方去处理cosα?原因是cos函数的容忍度太大了,导致非常大的夹角仍然能保持高光,这是不合理的,所以需要人为控制一下,一般P会采用256甚至以上,让夹角控制在3°以内。

环境光照 Ambient Term

环境光来自四面八方,与光照方向l和观测方向v都没有关系,是一个常数。

最终3种光照

着色频率 Shading Frequencies

下面介绍3种着色方式,对应着不同的着色频率。

逐三角面着色 flat shading

每个三角面着一次色。

逐顶点着色 Gouraud shading

每个三角面的3个顶点进行着色,着色完后三角面进行内部插值着色。顶点的法线方向,是通过这个顶点周围所有的面的法线平均或者加权平均算出来的。

插值可以用重心坐标插值,但是需要用投影前的重心坐标,而不是投影拉伸到屏幕后的,因为深度不同。取到重心坐标之后可以转换为uv坐标,这样就能取到对应的着色系数了。之后再把系数带入漫反射公式,算出光照下该像素的最终颜色。

逐像素着色 Phong shading

在顶点插值完之后,对每个像素都进行单独计算着色。

并不是频率越高越好

当面数比较多的时候,哪怕是逐三角面着色也会有很好的效果。