1 Blinn-Phong reflectance model(Blinn-Phong 反射模型)
1.1 Specular Term(高光项)
高光能被看到是由于观察方向和镜面反射方向非常接近:
而观察方向和反射方向的接近也可以转化成半程向量和法线方向的接近,所以Blinn-Phong反射模型高光项的式子为:
上图式子中使用点乘来表示两向量之间的夹角大小,点乘结果越大代表夹角越小,两单位向量点乘结果为1则代表重合,也完美表达了高光的特性。
那这里产生一个疑问:为何点乘项的结果上要加上一个P次幂?
答案见下图:
由上图可见,一个标准的余弦是能够表示两向量的夹角是否接近。但是一般来说高光的区域都是很小的,换句话说就是高光必须在两向量之间(观察方向和镜面反射方向)夹角非常小的时候才能看到。那么这时候直接用余弦就显得太宽容了,当在90度的时候高光项才完全没有值,所以这个时候用多个余弦相乘来拟合就刚好合适高光的特性。(所以这个幂数一般来控制高光到底有多大,幂数越大高光越小)具体可见下图:
K越大高光越亮,P越大高光越小。
1.2 Ambient Term(环境光项)
环境光是一个非常复杂的东西。可以想象,如果单纯考虑直接光照,那么没有被光照射到的物体的背面应该是完全黑的,但其实不是,就是因为它的背面可以接收到来自四面八方环境中弹射而来的光照,这种是间接光。所以环境光是非常复杂的,它与光照的入射方向、反射方向等等都没有关系,在这里就假设成一个常数项。后面学习全局光照以后再考虑这些。
现在把所有项都加起来:
2 Shading frequencies(着色频率)
什么是着色频率?见下图
所谓着色,之前讲了是针对一个着色点进行的,那着色频率也就是说把着色应用在哪些点上。比如第一个图,则是在每个多边形上计算一次着色,然后就把着色结果应用在整个多边形上。而第二个图则是针对每个三角形的顶点做一次着色,然后对三角形的内部在着色结果上进行插值。第三个图则是对每个像素都进行一次着色计算,然后将着色结果应用在像素上。
所以,针对以上情况都进行一个正规的定义:
1.Flat Shading(逐面)
对每个三角形只进行一次着色,整个三角形就都用一个着色结果,都是一种颜色,这种叫Flat Shading:
2.Gouraud Shading(逐顶点)
在每个顶点上求出法线,对每个顶点做一次着色,这样每个顶点都有自己的颜色,而三角形内部再进行插值,这种叫Gouraud Shading:
3.Phong Shading(逐像素)
如果利用三角形三个顶点的法线插值算出每个像素的法线,然后对每个像素都进行一次着色,这样每个像素都有了自己的颜色,这种叫Phong Shading(这里的phong和Blinn-Phong着色模型没有任何关系,只是碰巧都是同一个人研究的):
三种着色频率都讲完了,但是并不是说逐像素就一定比逐顶点和逐面要好:
三种着色频率都取决于面、顶点和像素。在模型本身不够复杂,面较少时(第一行)可以看出逐像素的效果是最好的;但是当模型本身已经足够复杂了,有很多的顶点和面(第三行),就会发现此时flat Shading得到的结果就并不一定比phong shading要差,因为此模型面很多,flat Shading就已经足够了,反而phong shading的开销会很大。
那怎么知道顶点的法线呢?我们只知道三角形的法线:
顶点的法线 = 它所涉及到的面的法线的加权(三角形的面积)平均。
那根据顶点的法线得到像素的法线:
利用重心坐标进行插值。
3 Graphics pipeline(渲染管线)
第一步,顶点经过MVP矩阵变换到齐次裁剪空间,之后再经过视口变换到屏幕空间:
第二步:屏幕中的顶点连成三角形,其实这一步没有什么实质意义。因为所有的顶点在三维空间中就已经设置好了哪三个顶点属于一个三角形,而映射到屏幕上以后是不会改变的。
第三步:光栅化,在屏幕中采样,看哪些像素在三角形内:
第四步:光栅化得到了一系列像素后,要对像素进行可见性判定,
第五步:着色,这里的着色会依据选择的是逐顶点的处理还是逐像素的处理而发生在不同的阶段,比如逐顶点就在顶点处理中做,逐像素就得等fragment产生了才能做。
第六步:纹理映射(接下来说)
第七步:最终形成framebuffer渲染到屏幕上。
在渲染管线中,允许自定义编程顶点、像素的处理,而这些编程代码就叫做Shader:
4 Texture Mapping(纹理映射)
将一张2D的平面图,经过拉伸啊压缩啊蒙在物体表面的这个过程就叫做纹理映射。例子:
将模型的顶点与纹理上的点一一对应起来(而这一步通常是由美工完成的或者是自动化生成的),然后就能将纹理贴在模型上了。
纹理上的坐标系,UV: