Games101-lecture 07.08 着色
零、提纲
(1) 可见性/遮挡
Z-Buffer算法(深度缓存)
(2) 着色
着色和光照
着色频率
图形渲染管线
shader program+GPU
Texture Mapping
一、深度缓存 Z-Buffer算法
前面只考虑了一个三角形的绘制,但是当多个三角形重叠在一起时,如果绘制出正确的图像。这就涉及到深度缓存。
1. 画家算法:从后往前画
步骤:先绘制最远处的物体,然后逐步绘制近处的物体,由于近处会自动覆盖远处物体,因此能得到正确的效果。
缺点:
1.在绘制前需要对所有待绘制物体进行排序。排序时间复杂度O(nlogn)
2. 无法处理循环遮挡

2. 深度缓冲解决画家算法的缺点
分析角度由三角形变为像素。从画家算法中考虑三角形的前后关系,到Z-buffer中只考虑像素点绘制的前后关系(只绘制最前面的像素)
需要两个额外空间的支持:
1. frame buffer stores color values(存储颜色)(buffer大小与屏幕大小一致)
2. depth buffer stores depth(存储深度)(buffer大小与屏幕大小一致)
深度缓存中的值默认为无穷远。每当确认像素被某个三角形覆盖时,在更新像素颜色前,先比较当前三角形这个点的距离与当前像素对应位置的depth buffer中的值。如果三角形上点的深度比当前值更远,则直接抛弃。否则,更新frame buffer中该像素的颜色,同时更新depth buffer中该像素的深度。(即通过深度缓存的作用,depth buffer总是记录最近的深度, frame buffer总是记录最近的颜色值)
注意:我们通常假定 z 总是正的,z代表深度(z越小越近,z越大越远)

另:Z-buffer算法与绘制顺序没有关系

如图:如果深度相同则不处理,也就是说如果像素深度相同,保留原先的像素。
二、着色 Shading
着色的目的是引入明暗与不同的颜色。在这门课中指对不同的物体引入不同的材质 (material) 。
着色只考虑局部点自身的光照效果,与场景中其他点的光照效果无关。因此着色本质局部光照(区分全局光照),也不会产生阴影。
注:全局光照(Global illumination)是间接光照,指是被反射过来的光照,考虑到环境中所有表面和光源相互作用的照射效果
局部光照(local illumation) 简单说就是只考虑光源到模型表面的照射效果,而不考虑其他点反射的效果
1. 基本着色模型(局部光照模型):blinn-phong reflectance model(blinn-phong反射模型)
specular highlights + Diffuse reflection + Ambient lighting (高光+漫反射+环境光照)

2.泛光模型(Ambient lighting环境光)
第一个, 泛光模型即只考虑环境光,这是最简单的经验模型,只会去考虑环境光的影响,环境光与观察、与法向都没关系,实际上就是一个常数。并且不会去精确的描述,而只是用一个简单的式子表示

其中



tips:其中反射率还是光的亮度都是一个3维的RGB向量,为什么一个物体能够有颜色,其实就是它吸收了一定颜色的光,将剩下的光反射出来,也就有了颜色
效果如下:

3.Lambert漫反射模型
Lambert漫反射模型是在泛光模型的基础之上增加了漫反射项。漫反射便是光从一定角度入射之后从入射点向四面八方反射,且每个不同方向反射的光的强度相等,而产生漫反射的原因是物体表面的粗糙,导致了这种物理现象的发生。

首先应该考虑入射的角度所造成的接收到光强的损失

只有当入射光线与平面垂直的时候才能完整的接受所有光的能量,而入射角度越倾斜损失的能量越大。我们应该将光强乘上一个入射角度。

再考虑光源与照射点的距离

图中中心为一个点光源,光线均匀的向周围发射,可以想象光源发射出来的能量其实是一定的,那么在任意两个圈上接受到的能量之和一定相等。而离圆心越远,圆的面积越大,单位面积所接受能量也就越弱,因此光强 I 要除以 r的平方。
漫反射的光强与两个参数有关。一是光线的强度、二是入射光与交点处法向的角度大小

其中 Kd 为漫反射系数,I 入射光强,n,l 分别如图中所示为法线向量和入射方向(由观察点指向光源),max是为了剔除夹角大于90°的光,v 是观察方向(由观察点指向相机)。 注意漫反射光线强度是与出射方向无关的,因此无论人眼在哪观察接收到的强度都是一样的!
tips:通过改变漫反射模型的3维反射系数 Kd ,我们就能够得到物体表面不同的亮度或颜色


将环境光与漫反射一起考虑之后(Lambert模型):

4.Phong反射模型
看见高光的条件:当观察方向 V 与反射光线 R 越接近时,高光越明显


其中ks为镜面反射系数,I为入射光强,r为光源到入射点距离,注意这里在max剔除大于90°的光之后,夹角 α 是观察方向和反射光线的夹角,我们还乘了一个指数p,添加该项的原因很直接,因为离反射光越远就越不应该看见反射光,需要一个指数p加速衰减


最后我们把环境光,漫反射光,镜面反射光全部累加得到Phong模型效果:

5.Blinn-Phong反射模型
Blinn-Phong反射模型是对phong模型计算反射方向与人眼观察方向角度的一个优化!
如果观察方向与反射方向角度大于90度,则根据公式会看不到高光。与实际不符合。使用半程向量可以避免这种情况,并且大大加速了角度计算的速度。

如上文所提,我们将反射方向与人眼观察方向夹角替换成一个半程向量和法线向量的夹角

tips:反射向量可由入射方向在法线方向投影的两倍减去入射方向得出
整体计算公式:

二、着色频率
在上文中我们讲解完了局部光照模型,其中主要利用了观察方向,入射光线与法线向量的位置关系,但并没有具体说究竟是三角形面的法线向量还是三角形顶点的法线向量,这也就牵扯出了本章内容——着色频率(面着色,顶点着色,像素着色),这3种不同的着色频率其实也就对应了三种不同方法。
分别为 Flat Shading + Gouraud Shading + Phong Shading (Phong Shading不是Phong反射 模型)


1.Flat Shading
面着色以每一个面作为一个着色单位。模型数据大多以很多个三角面进行存储,因此也就记录了每个面的法线向量,利用每个面的法线向量进行一次Blinn-Phong反射光照模型的计算,将该颜色赋予整个面,效果如下:

2.Gouraud Shading
Gouraud Shading会对每个三角形的顶点进行一次着色,原先我们只有每个面的法线向量,将所有共享这个点的面的法线向量加起来求均值,再标准化就得到了该顶点的法线向量,有了每个三角形的顶点向量之后,自然就可以计算出每个顶点的颜色。
对于三角形内部的每一个点用重心坐标(后面会讲到)来插值

其中 c0,c1,c2 为三角形三个顶点的颜色,α,β,γ 为三角形面内一点的重心坐标,c 为该点的插值之后得到的颜色。
这样就能成功的得到每一个点的颜色了,效果如下:

(tips:1.这里有两个tips可以注意一下,首先重心坐标一定要是原世界坐标空间中的重心坐标,但实际计算中一般会使用投影之后的二维平面来计算重心坐标,存在着一个误差需要校正——透视矫正插值,这会在下一节笔记中展开来谈(zhihu孙小磊的笔记6)。 2. 第二点,其实按理来说Gouraud用的是双线性插值(会在之后的贝塞尔曲线中具体讲解),但是道理都是相同的,本文这里为了方便就直接用了重心坐标插值)
3.Phong Shading
以上只对每个三角形顶点进行了着色,其它的颜色都是通过插值得到,
而Phong Shading可以真正的对每个点用Blinn-Phong模型计算得出颜色
以上步骤只得到每个顶点的法线向量,而Phong Shading需要的三角形内部的每一个点的法线向量也可以像插值颜色一般得到:

其中 n0,n1,n2 分别是三角形三个顶点的法线向量,α,β,γ为三角形面内一点的重心坐标,n 为该点插值之后得到的法线向量。如此便得到了任意一点的法线向量了,也当然可以对任意一点进行Blinn-Phong模型的计算了。最终对比渲染效果如下:

三、渲染管线(流程)

1.先将点绘制在三维空间中,
2.3.然后将三维空间的点投影在平面上,这些点会形成三角形
4.由于屏幕是离散的,要将三角形画在屏幕上就要使用光栅化离散成像素(片元)
5.像素进行着色
6.Framebuffer的处理,就是将所有的像素颜色信息整合在一起,输送给显示设备加以显示。

顶点处理的作用是指对所有的顶点数据进行Model,View,和Projection的变换,最终得到投影到二维平面的坐标信息(同时为了Zbuffer保留深度z值),当然如果超出观察空间的会被剪裁掉。
三角形处理也十分容易理解,就是将所有的顶点按照原几何信息,变成三角面,每个面由3个顶点组成。

光栅化过程中进行采样(判断像素是否在三角形内部),深度测试(Z-Buffer),知道了哪些在三角形内的点可以被显示。

tips:其实在片元处理阶段有一点还未叙述,我们也可以去做texture mapping,利用texture的信息来代替blinn-phong模型漫反射系数来当作颜色。
四、shader program与GPU
shader program分两种,分别是对顶点以及对片元的处理
(分别命名为vertex shader,fragment shader),程序员可以自行编程来代替原来固定的顶点处理和片元处理从而达到各种各样令人惊叹的效果!这也就是现在的可编程渲染管线。
shader程序是对每一个顶点或像素编写通用的模板,也就是说写一个shader程序,使每一个顶点或片元都按照shader程序来执行。

GPU是计算机图形软件的硬件实现,在硬件上就实现了 mvp 变换和光栅化,着色器是可编程的,并且随着GPU的发展,有越来越多不同种类的着色器,不只顶点/片元着色器。
五、纹理映射 texture mapping
纹理可以理解为给每个点赋值不同的光照系数(也就是blinn-phong中的ka\kd\ks),这样物体就有了花纹。
1. 纹理坐标
模型(3D)上的任意三角形上的点,都对应着纹理(2D image)上的一个位置,而这个位置就是由纹理坐标标识的(可以只标识三角形的三个顶点,其内部纹理坐标可以通过插值计算出)


其中texture的坐标(u,v),不管texture是长方形还是什么形,u,v范围都是【0,1】 * 【0,1】
知道了每个三角形顶点对应texture的颜色,那怎样判断三角形内部的插值呢?用重心坐标
纹理和着色之间的关系:纹理是用来定义着色的时候各个不同的属性,如果不希望着色的时候每个顶点都以相同的方式来着色,就用纹理来改变