当这些物体都变成三角形之后,变成屏幕上的一个个像素点之后,这些像素的值和颜色应该是什么呢?这个就是着色的功能。下一步操作为着色。
为什么我们能看到不同颜色,不同光照下,挪动一下光源后,物体并没有发生变化,但是物体的颜色却发生了变化。这个问题应该如何解决,就是着色的作用。
这门课中着色的定义:对不同的物体应用不同的材质这样一个过程。
因为不同的材质肯定和光线的相互作用有不同的方法,可通过这种不同的表现形式来展示材质的特性。
A Simple Shading Model (Blinn-Phong Reflectance Model)
最基础的模型:Blinn-Phong 反射模型
Blinn-Phong 模型是一个经验模型,并不是一个完全符合物理规律的模型。(下图的所有向量都是指的单位向量。)
- 镜面高光 Specular:一束光打到比较光滑的表面上,这束光就会在这镜面反射的附近被反射出去。
- 漫反射光 Diffuse:一束光打到比较粗糙的平面上,光会向四面八方反射出去。
- 环境光 Ambient:并不是一种直接光照,是间接光照。通过四面八方的物体反射得到的光。(路径追踪会详细介绍)
Shading is Local
我们此处考虑的这个 shading point 的着色结果,就只考虑这个点和其他的几个不同的方向,不考虑这个点是否在阴影内。考虑这个点的着色情况就只看它自己,不考虑其他物体的存在。
着色有局部性。因此着色可以看到物体的明暗变化,但是看不到阴影。阴影如何生成稍后再讲~
Shading 和 Shadow 是两件事。
Diffuse 漫反射
漫反射,光会向四面八方均匀地反射出去。看起来容易,但怎么做?
同样的光照到同样的表面上,不同角度为什么明暗会不一样?
假设下图每一根光线带着一定的能量。旋转之后只能接收到三根光线了,那么我们可以通过法线和光线来源决定有多少光可以到表面上。
首先我们要考虑光是一种能量,看到物体是指物体接收到多少能量。如果考虑一个 shading point 接收多少能量,那么就要考虑一个单位面积接收的能量。
为什么有春夏秋冬?因为夏天在北半球会被光线直射,因此夏天和冬天的区别是光线和地球所处表面的夹角不同导致的。单位面积接收到能量要少。
考虑一个角度接收多少能量,我们有 Lambert 余弦定律(Lambert's cosine law)。
- 知道了有多少能量会被传播到当前的 shading point
光线在传播的过程中,在单位面积的情况下,在任何一个位置上所能接收到的能量和光线传播的距离是成平方反比的。(也就是,光线传播的距离越长,单位面积上所接收到的能量是逐步衰减的。)
- 也知道了有多少能量在这个 shading point 上会被吸收
Lambert 余弦定律“接收到的能量”和 “光照方向与法线方向之间夹角的余弦” 是成正比的。
将以上两者结合到一起,就可以知道 diffuse 的表示方法了。
首先我们要定义一些属性:
- Viewer direction, v
- Surface normal, n
- Light direction, l (for each of many lights)
- Surface parameters (color, shininess 例如陶瓷涂了釉, …)
我们提到了接收,就提一下 Falloff。
如果光来自点光源,无时无刻都在以一种方式向四面八方辐射能量,我们有一个方法,认为任何时刻,点光源辐射能量一定会在某个时间集中在一个球壳上。
能量守恒定律,离中心近的能量和离中心远的能量应该是一样的,那么某一个点上对应的能量应该如下图一样,和半径平方成反比。
shading point 到点光源的距离为 ;点光源到单位距离上的能量强度是 ,那么我们就能够知道这个点光源通过 这个距离传播到 shading point 的能量有多少。能量到达了物体之后会被物体表面吸收,而吸收多少取决于 Lambert 余弦定理。最终计算的结果 就表示我们可以看到有多少能量,对应的就是物体的明暗。
有一个地方需要注意的是, 这里为什么要做一个点乘?表示的是当点乘结果是负值的时候,表示光从下面打过来才可能出现,此时并没有什么物理意义,就把值取为 0。
shading point 为什么会有颜色?
因为它自己本身会吸收一部分能量,反射出去的是它不吸收的能量。
由于不同的点对能量有不同的吸收率,定义一个系数 来表示这个点本身吸收多少。(当 意味着不吸收任何能量, 意味着吸收了所有的能量,我们看到的是个黑的。)不同的漫反射系数 决定了它的亮度和颜色。
如果我们将 表示成一个三通道颜色的 rgb 值, 分别 0~1 之间,那么 shading point 上我们就可以定义颜色了。
漫反射光打到 shading point 上去,会被均匀地反射到各个方向上去,就意味着我们不管从哪个方向上观察这个点,所看到的结果应该是一样的。(因为能量是被均匀反射出去的。)最终看到的结果和 (观察方向) 完全没有关系。
白色是因为法线方向和光线方向很接近。
Specular 高光
什么情况下看得到高光?
比较光滑的物体的反射都具有一定的特性,反射方向非常接近镜面反射的方向。当观察的方向和镜面反射方向接近的时候,就能看到高光了,其他时候看不到高光。
和 接近,就说明了 和 接近。它们两个有一定关系。可通过衡量 和 是否接近来判断我们是否可以看到高光。
可以参考《Unity Shader 入门精要》的高光反射小节。
如何衡量两个向量是否接近呢?
点乘。两个向量很接近的话,那么点乘结果接近 1,离得很远的话,点乘结果接近 0。
多少能量到达了 shading point 还是和上面一样考虑。但是没有考虑有多少能量被吸收了?
因为它毕竟还是一个经验模型,这里简化掉了,只判断是否能看到高光,它在这里没有考虑这部分。
为什么要用半程向量和法线来近似 和 是否接近呢(这个是 blinn-Phong 模型)?为什么不直接计算 和 的夹角是否接近(这个就是 phong 模型。 )?
这是一个改进,因为反射向量 不好计算。近似可以简化很多的计算量。
向量方向和夹角方向的确能算出两个向量是否足够接近,但是容忍度太高了,如果我们只用夹角余弦来算,就会发现下图左边第一个图中,45° 就已经足够大了,这就是为什么我们需要指数 。
Blinn-Phong:
Ambient 环境光
环境光来自四面八方,强度都是相同的。这里的环境光其实是一个常数。它的作用就是保证物体并非完全是黑的。(环境光是间接光照,计算起来十分复杂,这里用一个合理的常数来近似代替实际的环境光。)
:环境光系数
:环境光强度
相乘即可得到环境光,和观察方向、法线等无关,就是一个常数。
这只是一个假设,如果真的要精确地算环境光,需要全局光照的知识。
最终的 Blinn-Phong 模型
着色模型考虑的是任何一个点长什么样,下一步就是对所有的像素点做一个着色操作,那么整个场景就能看得见了。
Shading Frequencies 着色频率
球的边界可以看到三个球用的模型是一模一样,但是为什么结果不同?
这就是着色频率导致的。
着色频率指着色要应用在哪些点上。
三种着色模型:
- Flat shading
- 每一个三角形是一个平面,对每个三角形的面求出一条法线。(对三角形的两个边做个叉乘即可求得)
- 不适用于光滑表面。
- Gouraud shading
- 对每个三角形的每个顶点求各自法线,求得之后做一次着色,那么每个顶点就有颜色了,三角形内部的颜色就可通过插值计算出来。
- 如何对顶点求法线?答案
- Phong shading
- 对每个三角形的每个顶点求各自法线后,可以对三角形内部的每个像素都插值出一个自己的法线方向。对每个像素进行一次着色,就可以得到一个相对比较好的结果。
- 区分一个概念,之前的是 Blinn-Phong 着色模型,这个 Phong shading 是指的着色频率,是不一样的。
由下图可以看出,当模型足够复杂时,其实可使用相对简单的着色模型,因为效果差不多。这时用更小的开销,得到近乎一样的效果。
因此着色频率取决于面、点出现的频率,当面出现的频率已经很高的时候,就可以用相对简单的着色模型。
如何知道逐顶点的法线是什么?
理想情况:知道它近似的是什么形状,因此直接求得法线。比如近似的是球,那么直接连接这个点和球心就好了,延长线就是该点的法线。
很多三角形往往会共用一个顶点,那么这个顶点的法线可以认为是这相邻所有面的法线的平均。做加权的平均,相邻三角形面积越大认为它对顶点法线的贡献的越大。
又如何真正地去定义一个逐像素的法线呢?
已知两顶点的法线,如何插值出中间的法线呢?这个就需要用到重心坐标。(稍后会讲到。)
法线记得归一化。