图形学-渲染管线

渲染管线

渲染管线功能

  • 将物体3D坐标转换为屏幕空间2D坐标
  • 为屏幕每个像素点进行着色

渲染管线的流程

顶点数据输入->顶点着色器->曲面细分过程->几何着色器->图元组装->裁剪剔除->光栅化->片段着色器以及混合测试

  • 顶点着色器:主要进行坐标变换
  • 曲面细分过程:主要对三角面进行细分,分成更细或者更大的三角面
  • 几何着色器:主要讲输入的点或者线扩展成多边形
  • 图元组装: 将输入的顶点组装成指定的图元。图元组装阶段会进行裁剪和背面剔除相关的优化,以减少进入光栅化的图元的数量,加速渲染过程。
  • 光栅化:将物体坐标变换到窗口坐标。光栅化是一个离散的过程,将3D连续的物体转换为离散屏幕像素点的过程。
  • 片段着色器:用于决定屏幕上像素的最终颜色。这个阶段会进行光照计算以及阴影处理,是渲染管线高级效果产生的地方。
  • 测试混合阶段:测试包括裁切测试、Alpha测试、模版测试和深度测试。Alpha混合可以根据片段的alpha值进行混合,用于产生半透明的效果。

顶点着色器

坐标变换

局部坐标-- 模型矩阵(Model)-->世界坐标
世界坐标 -- 观察矩阵 --->观察坐标
观察坐标-- 投影矩阵 --->裁剪坐标
裁剪坐标-- 投射除法 --->标准设备空间(NDC)
标准设备空间(NDC)-- 视口变换 --->窗口坐标

  • 局部坐标和世界坐标的一点理解
    局部坐标:就是以该物体的原点作为中心,当物体前后移动、选装和缩放时,局部坐标系是不会发生变化的,仍然以物体原点为中心
    世界坐标:用于描述物体在场景中的实际位置,在这个坐标系中,所有物体的位置和方形都是相对于世界坐标系原点。
  • 模型矩阵
    这个变换矩阵可以对物体进行位移、缩放、旋转来将它置于它本应该在的位置和朝向。比如对于一个你想要变换的房子,你需要现将他缩小(因为它在局部空间中太大了),并将它位移到郊区的一个小镇,然后在y轴上进行一些旋转使其搭配附近的房子。
  • 观察矩阵
    是将世界坐标转化为用户视野前方的坐标而产生的结果。因此观察坐标就是从摄像机的视角所观察到的空间,而这通常需要一系列的位移和旋转的组合来完成,平移/旋转场景从而使得特定的对象被变换到摄像机的前方。
  • 投影矩阵
    通常分为两类:透视投影和正交投影
    正交投影也称为平行投影,物体在投影平面的大小与距离远近没有关系。
    透视投影,物体在投影平面的大小与距离远近有关系。
  • 投射除法
    为了实现投射投影中,近大远小的视觉效果。因为透视变换采用的是齐次坐标(x,y,z,w)。而w保留了观察空间中物体z坐标的信息,所以可以对w和z进行处理,实现近大远小的效果。
  • 标准化设备空间
    用于在渲染过程中统一处理顶点位置。NDC坐标确保了不同设备和屏幕尺寸之间的顶点位置的一致性和可预测性。因为它使用范围是[-1,1],确保不论设备之间的差异,使用相同的渲染流程均可以获得一致的视觉效果。
  • 视口变换
    主要讲标准化设备坐标映射到屏幕空间(像素坐标)。因为实际显示器的窗口尺寸和分辨率是不同的,视口变换讲NDC坐标映射到特定设备的屏幕空间,以产生与设备分辨率一致的像素坐标。
    在OpenGL中使用glViewport()进行视口变换。同时这个函数一般是放在帧缓冲回调函数framebuffer_size_callback()里,保证窗口可以随着你的拖动而变化

曲面细分着色器(可选)

利用镶嵌化技术岁三角面进行细分,以此来增加物体表面的三角面的数量,从而是物体更加真实和高质量效果。

光栅化

该阶段会把图元映射为最终屏幕上相应的像素,生成供片段着色器使用的片元。光栅化包括两个过程:三角形的组装和三角形的遍历。

  • 三角形的组装:负责将输入的顶点数据投影到屏幕空间并计算相关的边缘方程和属性插值
  • 三角形的遍历:在屏幕空间的最表组装三角形之后,我们遍历这些三角图元覆盖了那些片元的采样点,随后得到该图元所对应的片元。

片段着色器

主要目的就是计算一个像素的颜色。(ps:当然也不止颜色,也有可能是纹理贴图)。通常,片段着色器包含3D场景中的数据,比如光照(比如Phong光照模型)、阴影(静态物体选择Lightmap烘焙方法获取物体影子;动态物体选择Shadowmap技术)和光的颜色等。

  • 锯齿和抗锯齿
    3D场景中的物体是连续的,而2D屏幕空间的像素点是离散的,当我们使用离散的像素点来表示连续的物体时,丢失了物体连续的信息,导致锯齿的产生。之前光栅化阶段是抗锯齿技术的基础。
    • 超级采样抗锯齿(super sampling anti aliasing,SSAA)
      比如使用4xSSAA,假设屏幕分辨率是800x600,那么4xSSAA会将屏幕渲染到1600x1200的缓冲区上,然后下采样到800x600。这样可以得到很好的抗锯齿效果,缺点就是计算量庞大,同时渲染缓存也是原来的4倍。
    • 多重采样抗锯齿(Multi-Sampling Anti-aliasing,MSAA)
      就是多设置几个采样点,然后根据这几个采样点有几个在片元里,那么就乘以相应的权重,比如有四个点,其中两个在渲染片元里,南无这个像素就x0.5作为最终的颜色值。达到抗锯齿的效果,不然这个像素就可能没有颜色了。

测试和混合

深度测试

在日常生活中,近处的物体会挡住后面的东西,我们可以通过深度缓冲来实现这样的效果。其原理就是:比较当前片元的深度值(即更接近摄像机)是否比深度缓冲中预设的值小,如果是,则更新深度缓冲和颜色缓冲;否则丢弃片段不更新缓冲区的值。我们可以通过glEnable(GL_DEPTH_TEST)来开启深度测试。我们在渲染半透明物体时,需要开启深度测试而关闭深度写入功能。在渲染场景开始时,深度缓冲区每个像素都初始化为一个最大值(如,1.0)。

Alpha测试

Alpha测试可以根据片段颜色的alpha值来裁剪片段。GLSL采用discard()实现。

void main(){
  vec4 texColor = texture(texture1, TexCoords);
  if(texColor.a < 0.1){
    discard;
  }
  FragColor = texColor;
}

Alpha混合

就是场景中有一些透明物体和半透明物体,我们首先需要渲染不透明物体,然后在渲染半透明物体。之所以要分开渲染,是因为我们可以透过半透明物体看到半透明物体背后的东西,所以对半透明物体进行渲染时需要后面的图层信息,这样才能够实现正确的混合。从上面描述中,我们其实可以发现,渲染的顺序其实比较重要的,所以一般可以采用顺序无关半透明算法,该技术也被成为深度剥离技术,首先会记录距离相机最近的一层,然后记录距离第二近的层,以此类推,层层递进。但是如果剥离太多层就会影响到运行效率的问题,后面有人提出双向深度剥离算法来解决这个效率问题。

参考文章

posted @ 2023-07-24 21:24  九叶草  阅读(72)  评论(0编辑  收藏  举报