【读书笔记】计算机图形学基础(虎书)第10章 - 表面着色(Surface shading)
表面着色指的是用光线元素给物体表面增加额外一层材质。本章节记录了Diffuse Shading漫反射着色,Phong着色和艺术着色Artistic Shading。
1. 漫反射着色
首先一个物理知识小科普,漫反射和镜面反射的区别。漫反射在现实生活中是因为物体表面较为粗糙(哑光平面),导致光线入射在不同的凹面或者凸面上,导致反射光线四散的现象。与其对应的是镜面反射,表面较为平滑,反射方向也相当的一致。
1.1 Lambertian着色模型
为了模拟漫反射物体/哑光平面开发了Lambertian模型,即表面在所有视角下亮度都是一致的且将光线完全反射出去。因此该模型显然与视角向量无关,与光线与表面法向量之间的夹角相关。需要注意的是我们通常把光线理解为方向光,也就是光源距离物体较远,而不是从入射点和光源之间划一条线来判断光线的方向。为了反应不同材质的漫反射效果,我们增加了一个漫反射率\(c_r\)来记录RGB三色的反射情况。同时我们需要一个输入光的强度\(c_l\)。为了避免cos为负数的情况,即光线并没有入射在平面上(从法向量的另一边进入,属于折射而不是反射)时,我们采用\(max(0,n\cdot l)\)而不是单纯的cos值\(n\cdot l\)(这里等于cos是假设了n和l为单位向量)。还有一种方法双向光是在对称的地方也放一个光源,也就是两个光源之间的光线穿过了物体,这样则采取\(|n\cdot l|\)也就是cos的绝对值。
1.2 环境光照(Ambient Shading)
环境光照模拟物理世界中总是存在的一些光强,例如月光等。他也避免了物体在光线较少时纯黑的不美观现象。实际实现的时候可以选择在视线处放一个光源,例如游戏里的手电筒。也可以使用之前说的双向光的方法。最常用的是直接在光强处增加一个\(c_a\),即Ambient lighting,构成了\(c=c_r(c_a+c_lmax(0,n\cdot l))\)。这一项环境光往往会导致有些高光处超过上界如[0,1]或者[0,255],我们则需要对上界进行裁剪操作来保证输出光在边界内。
1.3 基于顶点的漫反射
如果一个三角形面上的法向量都一致(显然,在物理上确实如此),那么不同角度的三角形拼接显现出来的则会有一面一面的明显差别,就如观察一个高尔夫球一样。
为了将光照变化显得更加平滑,我们可能会让不同的顶点拥有不同的法向量,而中间点则有插值来决定法向量。而在建模时我们有不同的方法来决定这些顶点向量,一个较为简单的方法是对于一个被多个三角形共享的顶点而言,该顶点的法向量为周围三角形法向量的平均值。下图是一个插值获得法向量的例子。
2. Phong着色
有些物体的表面属于含有高光的哑光平面,例如白板。高光处会根据视角的变化而变化,从光学上这些高光主要和光源的颜色相关,与物体表面的关系较低。为了模拟这个效果我们在Lambertian光照的基础上引入了Phong光照模型。
2.1 Phong光照模型
在Lambertian光照的基础上,我们加入了反射光直入眼带来的高光效应。两种解法分别为直接计算反射光\(r\)并利用\(r\cdot e\)来计算夹角,第二种是在之前章节提到过的利用\(l,e\)的中间值与\(n\)之间计算夹角。为了避免负数的问题我们采用\(c=c_l*max(0,e\cdot r)\),但因为这样很容易增加0.5左右的光强会导致大部分地方都拥有高光,所以我们额外增加了一个快速降低高光效果的幂p,得到\(c=c_l*max(0,e\cdot r)^p\)。采取不同的p显然会让Phong光照的效果不同,可见下图。
就计算而言,\(r=-l+2(l\cdot n)n\),见如下示意图,\(l\cdot n\)代表了\(l\)在平面上的高度,因此\(-l\)加上两倍的高度相当于高度不变但是在平面的横向位置沿着入射处翻折了。在之前提到过的计算\(e,l\)中线的方法中,中线为\(h=\frac{e+l}{||e+l||}\),这个方法主要的好处是只要光线和视线都在同一平面上则有正值,而前一个方法多在视线和光线分别居于法向量两侧的时候使用(若r和e之间的夹角大于九十度则会出现问题)。缺点是会出现开根和除法运算,计算时间会稍长一些。另外通过角度计算我们可以得出nh之间的夹角是re之间的夹角的0.5倍,这会给phong光照带来一些别的变化。
我们可以额外引入一个\(c_p\)的控制因子来进一步控制高光的颜色,即利用\(c=c_r(c_a+c_l*max(0,n\cdot l))+c_lc_p(h\cdot n)^p\)。这里有一个有意思的事情要注意,在之前的某些章节和之后的颜色里提到过,油画等颜色是减法律的(颜料吸收了一部分),光是加法律的(光线混合),计算机图形和物理建模将它理解为乘法律,具体原因可以参考之后的章节,我的初步猜想是因为光谱频率本身就不适合用加法。因此这里\(c_l*c_p\)形容的就是入射光线和另一种光之间的混合效果。对于金属表面我们使用\(c_p=c_r\),即让入射光和金属颜色直接混合。文中还提到让\(c_p=1-M\),其中\(M\)是\(c_r\)三色中的最大值(我个人认为应该是\(c_r(c_a+c_l)\)的最大值,不过也有可能是这里的公式隐藏了clamp超出部分这一步,如果有这一步的话那么\(M\)等于\(c_r\)三色最大值是合理的),这样就确保了最后的颜色不超出界限\(1\)。
最后重复一次,如果采取Gouraud Shading,即在顶点着色器中混合法向量,因为法向量改变在Phong光照模型中对颜色变化较大,很容易出现三个顶点和最亮处的法向量类似但是三个顶点的着色颜色较低,结果颜色插值后的最亮点变暗了的情况。这一现象在下图中较为明显。Phong着色即在片元着色器部分才开始对每一个片元计算着色(之前则会基于三个顶点在片元上插值得出法向量),最后的结果则会更加贴近真实情况。这一部分在本知乎链接中有较好的解释。
3. 艺术渲染(非真实渲染)
之前的两个模型是真实渲染的基础,现在我们开始聊一聊基础的艺术渲染。这个知乎文章有一个比较好的导引。
3.1 描边
首先是剪影(silhouettes)或者说轮廓。在三维世界的三角形中,如果我们要绘画轮廓,我们需要绘画的那一条边需要满足其一边的三角形面朝摄像机,而另一个三角形背朝摄像机的特征。这也可以理解为可见和不可见的边界(轮廓)。如果三角形的面有法向量\((n_1,n_2)\),则我们可以简单检查\((e\cdot n_1)(e\cdot n_2)\leq 0\)即可(这个方法似乎会画多个垂直于视线的面的轮廓,但是个人认为这样总比有些轮廓画不出来要好,同样的地方z-buffer一下就可以了)。同样如果我们运用隐式函数的方法,那么就是\(f_0(e)f_1(e)\leq 0\)。
其次是可视多面体的绘制,我们很多时候会选择描绘角度改变较大地方的轮廓,例如一个人的鼻子,而不是整个脸上的三角形边都一个一个的画。如下图所示,主要绘画轮廓线即可。在图形学计算的时候我们仅在\(n_0\cdot n_1\)低于一定值的时候进行绘画(注意点乘cos在180度内随着角度增加而减小)。
3.2 Cool to Warm Shading (Gooch Shading),暂且称为冷暖色着色
这个做法是对于面向A面的表面用冷色调(蓝色等)绘画,面向B面的表面用暖色调(橙色)等绘画来增加对比度。通常这些颜色的饱和度都不会太高或者太低,这样黑色的轮廓线就较为明显。他的实现方法是将光线视为暖色调,然后得到warmth constant温度常数\(k_w=\frac{1+n\cdot l}{2} \in[0,1]\)。我们利用这个常数来混合冷暖色调\(c=k_wc_w+(1-k_w)c_c\),其中\(c_c,c_w\)分别是冷色调和暖色调的颜色。下图分别是Phong着色,单独的冷暖色着色,以及冷暖色着色+轮廓。理论上这个图的冷暖色着色还加了一些底色,否则应该是看起来更为单调的样子,例如第二个图。