计算机图形渲染管线:顶点处理,图元装配,光栅化,片段着色,输出合并
渲染
- 预渲染(pre-rendering)
- 实时渲染(real-time rendering)
Unity的内置渲染管线分为:CPU应用程序端渲染逻辑和GPU渲染管线
CPU渲染逻辑
- culling
- 打包数据(顶点,UV,法线,MVP矩阵,shader,灯光)
- 调用drawcall
GPU渲染管线
顶点着色器
主要任务是通过MVP矩阵将物体额自身坐标转换为裁剪空间中:
- M:物体顶点在模型空间下的坐标转换为世界空间下的坐标
- V:世界空间转化为视图空间(相机空间)
- P:视图空间转化为裁剪空间(相机的视野范围是一个视椎)
曲面细分着色器
对三角形面进行细分来增加物体表面三角形的数量。
用来实现LOD(细节层次),根据相机的远近来调控三角形的数量。
几何着色器
以完整的图元(比如,点)作为输入数据,输出可以是一个或多个其他的图元(比如,三角面),或者不输出任何的图元。几何着色器的拿手好戏就是将输入的点或线扩展成多边形。
图元装配
- clipping将视椎外的部分进行提出,例如,线段的两个顶点一个位于视椎体内而另一个位于视椎体外,那么位于外部的顶点将被裁剪掉,而且在视椎体与线段的交界处产生新的顶点来代替视野外部的顶点
- 标准化设备坐标(Normalized Device Coordinates,NDC),将裁剪空间的坐标(-w,-w,w)转换为(-1,-1,1)
- 背面剔除:三个点组成一个三角片,如果这三个点是顺时针排列的,认为是背面,否则认为是正面。
- 屏幕映射:每个图元的x和y坐标转换到屏幕坐标系
背面剔除:
屏幕映射:
光栅化
将屏幕空间的图元离散化为片元(带各种状态信息的像素)
三角形遍历阶段会根据上一个阶段的计算结果来判断一个三角网格覆盖了哪些像素,并使用三角网格3个顶点的顶点信息对整个覆盖区域的像素进行插值。
这些状态包括了(但不限于)它的屏幕坐标、深度值Z、顶点颜色,以及其他从几何阶段输出的顶点信息,例如法线、纹理坐标等。
光栅化阶段实际上并不会影响屏幕上每个像素的颜色值,而是会产生一系列的数据信息
片段着色器
纹理贴图技术
建立模型3D/2D位置与贴图2D位置的对应关系
包括:
- 凹凸贴图
- 法线贴图
- 高度贴图
- 阴影贴图(shadowmap)
纹理环绕方式
纹理坐标的范围通常是从(0, 0)到(1, 1),那如果我们把纹理坐标设置在范围之外会发生什么?OpenGL默认的行为是重复这个纹理图像(我们基本上忽略浮点纹理坐标的整数部分)
mipmap(多级渐远纹理)
由于远处的物体可能只产生很少的片段,OpenGL从高分辨率纹理中为这些片段获取正确的颜色值就很困难,因为它需要对一个跨过纹理很大部分的片段只拾取一个纹理颜色。
它简单来说就是一系列的纹理图像,后一个纹理图像是前一个的二分之一。多级渐远纹理背后的理念很简单:距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个。
光照计算
- 漫反射:当一束平行的入射光线射到粗糙的表面时,表面会把光线向着四面八方反射
- 镜面发射
- 环境光:环境光分量是用来模拟全局光照效果的,其实就是在物体光照信息基础上叠加上一个较小的光照常量,用来表示场景中其他物体反射的间接光照
Phong模型和Blinn-Phong模型的区别
Phong与Blinn-Phong两个光照模型都是用来实现光照射在物体上,物体表现产生一个高光部分的效果的,两个模型也十分相似,毕竟后者是对前者进行了一个计算上的优化。
输出合并
Alpha测试
通过片元数据,可以获取该片元的alpha值,如果alpha值小于某个数的话,则直接将该片元丢弃,不进行渲染
模板测试(Stencil Test)
这是一个开发者可以高度配置的阶段。如果开启了模板测试,GPU会首先读取模板缓冲区中该片元位置的模板值,然后将该值和读取到的参考值进行比较,这个比较函数可以是由开发者指定的,例如小于时舍弃该片元,或者大于等于时舍弃该片元。如果这个片元没有通过这个测试,该片元就会被舍弃。
深度测试
通过将深度缓存中的值和当前片元的深度进行比较,计算是否需要更新深度缓存和颜色缓存,如果不需要则将该片元丢弃,这于模板测试比较类似。
我们在渲染半透明物体时, 需要开启深度测试而关闭深度写入功能。
混合
对于不透明物体,开发者可以关闭混合(Blend)操作。这样片元着色器计算得到的颜色值就会直接覆盖掉颜色缓冲区中的像素值。但对于半透明物体,我们就需要使用混合操作来让这个物体看起来是透明的。下面是一个简化版的混合操作流程图。
双重缓冲
对场景的渲染是在幕后发生的,即在后置缓冲(Back Buffer)中。一旦场景已经被渲染到了后置缓冲中,GPU就会交换后置缓冲区和前置缓冲(Front Buffer)中的内容,而前置缓冲区是之前显示在屏幕上的图像。由此,保证了我们看到的图像总是连续的。