图形学(渲染管线)学习日记
作用:1.物体3D坐标转成屏幕2D坐标 2.对屏幕每个像素点进行着色
流程:顶点数据的输入、顶点着色器、曲面细分过程、几何着色器、图元组装、裁剪剔除、光栅化、片段着色器以及混合测试(每个前一阶段输出均为该阶段输入)(顶点着色器、曲面细分相关着色器、几何着色器和片段着色器可以编程)
顶点数据:顶点坐标、纹理坐标、顶点法线和顶点颜色。
顶点着色器:将输入局部坐标变换为世界坐标,观察坐标和裁剪坐标。输入:顶点及其属性。输出:变换后的顶点
曲面细分(可选):利用镶嵌化处理技术对三角面进行细分从而增加物体表面的三角面的数量,实现离摄像机近物品细节多,远细节少。
几何着色器(可选):输入:完整图元 输出:0个或1个或多个其他图元。 作用是将输入的点/线拓展为多边形。
图元组装:顶点组装为指定图元。 会进行裁剪,背面剔除优化,减少输入光栅化图元数量。在光栅化之前需要屏幕映射(透视除法和视口变换,由硬件实现)
光栅化:将连续3D物体离散为屏幕像素点。过程:确定图元覆盖的片段,利用顶点属性插值得到片段的属性信息,然后送到片段着色器进行颜色计算(片段是像素的候选,通过测试才能成为像素点)
片段着色器:决定屏幕中像素最终颜色。(进行光照计算和阴影处理,产生高级效果)
测试混合阶段:裁切测试、Alpha测试,模板测试和深度测试。(未通过测试片段被丢弃,通过测试片段进行alpha混合,alpha=0完全透明,=1完全不透明)通过OpenGL或directX定制测试和混合方式

《Real Time Rendering》将渲染管线划分为4阶段
应用程序:碰撞检测、动画物理模拟以及视椎体剔除,将顶点数据送入渲染管线
几何处理:顶点着色器,图元组装(可以理解为先变换顶点,再将变换后顶点进行裁剪,拼接为图元,映射至屏幕)
光栅化:连续图元离散化为片段。
像素处理:像素着色和混合。
对于几万面的复杂模型,通过加载建模工具(Blender,3dmax,maya)产生的数据,提高效率。有大量三角面共用顶点,通过使用顶点缓存对象来避免顶点间数据冗余。
顶点着色器
从建模工具中得到的是局部坐标,通过模型矩阵变换成世界坐标,再通过观察矩阵变换成观察坐标,通过投影矩阵变换成裁剪坐标,通过透视除法变换成NDC(标准设备坐标),通过视口变换变换至窗口坐标进行显示。
视锥体
由近平面,远平面,垂直视场角,屏幕纵横比四个参数定义。

在OpenGL中分别用glm:ortho()和glm:perspective()创建正交投影和透视投影矩阵。
根据光照计算不同,有平面着色、高洛德着色和冯氏着色。
平面着色是使用一个顶点颜色来代表整个三角面的颜色,默认使用索引第一个顶点颜色,无法展示高光效果。性能开销最小。
高洛德着色在顶点着色器中计算三个顶点的光照信息,然后在光栅化阶段插值得到三角形内部各个片段的光照信息。由于高光部分非线性,故插值表现差。
冯氏着色,效果最好但性能开销最高。根据输入的顶点法线信息在光栅化阶段插值得到各个片段的法线信息,然后在片段着色器中利用法线、纹理坐标、位置等信息计算每个片段的光照信息
曲面细分着色器(可选)
利用镶嵌化处理技术对三角面进行细分,以此来增加物体表面的三角面的数量。
不用创建高模来丰富网格信息。基于GPU可以实现动态的LOD技术,可以根据物体距离摄像机的远近来调整多边形网格的细节。远的时候用高模渲染就浪费。拉近的时候使用连续镶嵌就可以。先保存低维信息,有需求再GPU动态加细节,可以节省内存。
几何着色器(可选)
可以创建或销毁几何图元。通常用来实现一种叫做公告栏的视觉效果。
图元组装
裁剪:完全位于视椎体外部的图元会被裁剪掉,不会对它们进行渲染。
背面剔除:剔除那些背对摄像机的图元。默认不开启,用glEnable(GL_CULL_FACE)开启优化。仅可对完全不透明对象使用。

屏幕映射(透视除法+视口变换)
透视除法:实现透射投影中近大远小的视觉效果,由硬件自动执行。
视口变换:获取窗口坐标,带有变换后的z轴信息。glViewport() 设定视口的坐标和宽高。如果视口小于屏幕空间,会造成多余的像素被渲染

光栅化(三角形组装+三角形遍历)
三角形组装:对顶点输入数据(法线、纹理坐标)插值,获取片段对应数值(法线、纹理坐标)。很少插颜色值而通过片段着色器着色。

三角形遍历:通过屏幕空间坐标组建三角形,遍历这些三角形所覆盖的片段采样点,获取对应片元。
片段着色器
计算光照使用模型-phong
将物体光照分为漫反射分量、镜面高光和环境光来计算
环境光:叠加一个较小光照,来表示来自其他物体反射的间接光照
漫反射光:无论观察者从哪个方向进行观察,漫反射效果相同,与观察位置无关。在原来的代码加上float hLambert = difLight * 0.5 + 0.5。即可升级为半朗伯模型。将之前的漫反射系数从[0,1]变到[0.5,1]

镜面反射需要知道观察者位置,进而表现金属反射出的高光。
锯齿和抗锯齿
3d连续物体离散化成2d像素点会产生锯齿。
超级采样抗锯齿(ssaa)最直接最好,800x600的屏幕渲染到1600x1200的缓冲区再下采样回800x600。计算量很大。这个用的4*ssaa,使得光栅化和片段着色器都是原来的4倍,渲染缓存的大小也是原来的4倍。
测试和混合
裁切测试:避免当视口比屏幕窗口小时造成的渲染浪费。当视口的大小和屏幕空间一样大就不用。默认不开启裁切测试,glEnable(GL_SCISSOR_TEST)开启,glScissor()指定裁切区域
alpha测试:颜色一般采用RGBA,a代表透明度,根据片段颜色的 Alpha值来裁剪片段。消耗大性能低,必要才用。

Early-Z Culling:深度/模板测试是在片段着色器之后进行的,所以导致着色器计算资源的浪费(着色无用被遮挡片段)不能修改深度缓冲否则gpu无法开启early-z。alpha测试会让他失效所以效率低。
模板测试:通过mask的值来控制那些片段的可见性,无法通过mask的片段将被丢弃。默认不开启,通过glEnable(GL_STENCIL_TEST)开启,用glStencilMask设置mask,用glStencilFunc和glStencilOp设置模板函数,控制测试成功/失败时的行为。可实现平面镜效果、平面阴影和物体轮廓。

深度测试:片段深度值是否比深度缓冲预设值小,若是,则更新深度缓冲和颜色缓冲,若否,则丢弃片段不更新缓冲区值。默认不开启。通过glEnable(GL_DEPTH_TEST)开启。glDepthFunc() 来设置深度比较运算符,默认为GL_LESS(前面的物体会盖住后面的物体)
Alpha混合
用于实现半透明效果,通过glEnable(GL_BLEND)开启。通过 glBlendFuncSeparate() 、glBlendFunc() 和glBlendEquation() 来设置各种混合效果。通过下面混合方程调整效果。

先渲染不透明再渲染半透明,因为透过半透明能看不透明,渲染半透明需要不透明图层信息,才能精确混合。
学习自https://positiveczp.github.io/