本节是Games101 shading部分的学习笔记
光照和基本的着色模型
Shading
在图形学中是指模拟光照和彩照相互作用的过程。Shading
(着色)是将颜色应用于三维模型表面的过程,以便在渲染图像时呈现逼真的光照效果
blinn-phong着色模型
Blinn-Phong
模型是一种简单的着色模型,是一种对真实物理方法的简化。它把真实世界的光线模拟为:
- 高光;
- 漫反射:物体表面的光线均匀的向所有方向反射,对于所有的观察角度,看到的物体表面的颜色都是一样的。
- 环境光;
最终结果将物体表面在高光、漫反射、环境光三者作用下的结果相加,就是最终物体表面的颜色。
漫反射 diffuse reflection
INFO
那么我们如何定义漫反射下光的强度呢?
我们通过分析可以知道漫反射光的强度受几点东西影响:
- 光源到物体表面的距离;
- 光源与物体表面的夹角
- 物体表面的材质
我们分析得到下边这个式子:注意式子中kd
物体表面材质的漫反射系数。
高光 specular term
我们观察现实世界会发现一个现象,什么时候我们可以看到高光呢?当我们看向物体的角度与光源的角度成镜像时候,所以blinn-phong渲染模型做一个简化,当我们视角距离这个镜像角度a接近时,就可以看到高光。
INFO
**高光计算公式如下:**其中半程向量h是指人眼与光线的夹角的中间向量。 巧妙处采用余弦函数的指数,这样使得物体法线与半程向量夹角在一个比较小的范围内,才能看到高光。
环境光 Ambient Term
环境光不依赖与任何东西:
- 我们使用一个常量颜色,从而解决阴影中的照明
- 这是一种对现实的近似/模拟。
那么最终我们把漫反射、高光、环境光一起考虑,就得到最终的着色模型:
着色频率
有三种着色频率:
flat shading
: 以面为单元进行着色gouraud shading
: 对每个顶点进行插值着色phong shading
: 对每个像素进行着色
上面这张图显示了三种着色频率下,在不同的顶点个数下对最终结果的影响,在顶点数量较少的情况下,phong shading效果较好,当我们顶点数量比较多时,三种方式都能获得不错的效果。
INFO
如何去获得逐个顶点的法线呢?
可以通过对每个顶点相邻的所有三角形的法线进行求和后取平均。
图形渲染管线 graphics pipeline
所谓图形渲染管线,就是我们如何从原始的几何形状到最终屏幕上的一副图像的过程。 上面这张图就是图形渲染管线,主要包括:
- 顶点处理,投影、变换等;
- 光栅化,涉及如何采样、深度测试,找出具体需着色的像素点
- 着色
注:在现代的显卡设备中,这个渲染操作流程已经在显卡中写好了。
**可编程渲染管线:**我们经常听到这个概念,这其实就是指我们可以控制渲染管线的某些部分如何渲染。比如我们可以指定顶点如何处理、如何着色。也就是写shader。
着色器程序 shader
- shader 是对每一个顶点或像素进行处理的程序。
- shader是是直接面向GPU的,操作的是每一个顶点和每一个图元。
注意操作每一个顶点和像素,不需要我们去写循环语句,而是我们写的着色器代码本身就会自动的在每一个顶点和像素上去执行。
纹理映射 Texture Mapping
纹理插值 Barycentric coordinates
我们光栅化一个三角形的过程中,如何根据三角形顶点的颜色对三角形内部进行插值呢?一种常用的做法是使用重心坐标系。Barycentric coordinates
重心坐标系统是指对一个三角形(ABC)内的点(x,y),满足:
INFO
如下图,使用重心坐标对三角形颜色进行线性插值的结果
纹理映射中出现走样问题及其解决
当纹理分辨率与映射目标范围相当时,纹理映射可以产生较好的效果。但是当纹理分辨率过小或过大时,都会出现走样问题。
纹理分辨率较小时-interpolation
纹理上的一个像素叫做纹素(texel)
当纹理分辨率小的时候,一个纹素就会映射到模型上的多个像素,就会产生模糊的感觉。 当我们查询纹理上的非整数值时,我们应该返回什么颜色结果哪?这就需要进行纹理插值了,有几种纹理插值方法:nearest
、bilinear
、bicubic
,从下边几张图里我们也能看出来,最终效果bicubic > bilinear > nearest,但从运算量上:bicubic > bilinear > nearest
nearest 最近纹理插值
最近纹理插值很简单,就是照距离纹理采样点最近的一个纹素中心,取这个纹素的值作为采样值。
bilinear 双线性插值
下图中红色点时我们采样的点,采用双线性纹理插值怎么做那? 取采样点最近的四个纹素,首先通过线性插值求出的值
再对进行线性插值,求出采样点的颜色
bicubic 双三次插值
Bicubic
插值算法基于双三次插值(Bicubic Interpolation)
,它利用目标像素周围的16个相邻像素进行插值计算。具体而言,它使用了二维卷积核来计算目标像素的值,该卷积核考虑了目标像素附近的像素权重。 由于Bicubic插值算法考虑了更多的相邻像素,因此相对于线性插值等简单插值方法,它能够提供更加平滑和精确的纹理映射效果。然而,它的计算复杂度相对较高,可能会导致一定的计算开销。
纹理分辨率较大时 mipmap
当纹理较大,而几何对象较小时会发生什么呢?这实际上会带来更严重的问题: 看右上边这幅图,近处出现了锯齿,远处出现了摩尔纹。那么为什么会出现这种现象呢?这是因为透视投影近大远小的特点,查询远处的一个像素,映射在纹理上是一个较大的范围,最终返回的是这个较大范围纹理的平均值,那这样显然是不对的。 采用mipmap(多级纹理)技术可以很好解决这个问题。
mipmap
mipmap类似于GIS中的图像金字塔的概念,以原始高分辨率纹理创建多级不同分辨率的纹理,它们以递减的分辨率依次排列,这样就能解决我们相机看向较远物体或纹理绘制在小几何对象上的问题。 mipmap的层级计算: 如何知道几何上的一个点对应到mipmap后的那个级别?是通过将屏幕空间下采样点及其相邻采样点映射到原始纹理图像上,计算映射后的采样点与相邻采样点的距离,也就相当于计算了屏幕上采样点对应到纹理图像上的范围,然后通过公式计算所属层级。 mipmap trilinear interpolation 三线性插值 我们解决了如何查询mipmap层级的问题,但很多时候,我们求得的层级可能并不是整数级,比如说我们mipmap分级为1、2、4级,那对于1、2、4级的中间层级,我们该如何对应呢? 这时我们可以用**trilinear interpolation**
**,**它是在两个相邻层级上进行了双线性插值后,再在层级间进行一次插值。**trilinear interpolation**
可以很好的解决mipmap层级分割的问题,使纹理映射连续。
anisotropic 各向异性过滤
如上图所示,使用mipmap后远处的图像出现一片模糊的情况,这是由于mipmap只能进行正方形范围查询,而对于远处的一个点对应到纹理上是一个长方形的范围**(如下图所示)**,对于长条形的范围查询仍然会有问题。 这时我们可以使用anisotropic,各向异性过滤。 顾名思义,各向异性过滤就是纹理图像在各个方向上的表现都不同。各向异性过滤相对于mipmap,在横向和纵向上也分别进行了压缩,所以它可以进行长条形的范围查询。
关于纹理的其他用途
纹理可以理解为一块数据,可以做任意的查询。 纹理不仅仅可以用于模型表面的颜色,还可用于:
- 环境贴图
- 凹凸贴图/法线贴图(仅仅影响法线,实际不改变几何)
- 位移贴图(实际真的移动了顶点,改变了几何)
- 三维噪声
- 阴影
- 环境光遮蔽
- 体渲染()
阴影
这部分内容是老师在讲光线追踪前讲的。因为使用光栅化的技术,一些全局光照,包括阴影并不好做。
在光栅化中实现阴影是采用shadow mapping技术,shadow mapping
的一个核心观点是不在阴影中的点那么会同时被光源和相机看到。也就是说在阴影中的点会被相机看到但光源看不到。
shadow mapping技术只能用于点光源或方向光源。
shadow mapping
采用two pass 渲染: **在pass 1: **先从光源出发看向场景,获取光线与物体最近的交点,最终获得一张在光源视角下的深度图(shadow map)。 **在pass 2: **从相机看向场景,将相机与物体表面的交点重新投影回光源,获取此时交点在光源视角下的深度值,然后将此时的深度值与之前该像素(光源视角下的投影平面)的深度值进行比较,如果深度值相等,说明该点可以同时被光源和相机看到,否则只能被相机看到。
shadow mapping
技术存在的问题:
- 深度值数值精度问题,深度值的数值计算会存在一些误差,比如说场景中相距很近的点。
- 在pass1中记录的shadow map的分辨率问题,类似纹理也会导致锯齿。
INFO
总结一下,shadow mapping 需要对场景渲染两趟(从两个视角看向场景),相比于shading(一趟)会产生更大开销。虽然shadow mapping技术有一些不足,但仍然是3d游戏采用的主流技术。
关于硬阴影和软阴影的概念: shadow mapping技术设计是用来处理硬阴影(因为是点光源)。硬阴影是指形成的阴影比较锐利,不符合自然现象。软阴影会有一些过渡,比如说现实中的日食现象中,在完全看不到光源的地方,阴影会比较深(完全被月亮挡住),在能看到部分光源的地方,阴影会比较浅。 也就是说阴影的程度取决于有多少的光(或多大的光)能够看到,所以点光源只能产生硬阴影,因为光源只能从一个点发出光线。