Shader入门精要(二)

因为第一章内容太少,就再更一章吧~

第二篇 渲染流水线

Q:渲染流水线在干什么?

A:渲染流水线的最终目的在于生成或者说是渲染一张二维纹理,即我们在电脑屏幕上看到的所有效果。它的输入是一个虚拟摄像机、一些光源、一些Shader以及纹理等。

使用流水线的好处在于可以提高单位时间的生产量。

image-20210911215352715

流水线系统中决定最后生产速度的是最慢的工序所需的时间(短板效应)

理想情况下,如果把一个非流水线系统分成n个流水线阶段,且每个阶段耗费时间相同的话,会使整个系统得到n倍的速度提升。(多线渲染?)

渲染流程(概念上的流水线)

渲染流程分成3个阶段:应用阶段(Application Stage)、几何阶段(Geometry Stage)、光栅化阶段(Rasterizer Stage)。

image-20210911215704318

应用阶段

这个阶段是由我们的应用主导的,因此通常由CPU负责实现。主要包含三个任务:

1
2
graph LR
准备场景数据 --> 粗粒度剔除culling--> 渲染图元and输出渲染所需几何信息

几何阶段

几何阶段用于处理所有和我们要绘制的几何相关的事情。例如,决定需要绘制的图元是什么,怎样绘制它们,在哪里绘制它们。这一阶段通常在GPU上进行。几何阶段负责和每个渲染图元打交道,进行逐顶点、逐多边形的操作。这个阶段可以进一步分成更小的流水线阶段,这在下一章中会讲到。几何阶段的一个重要任务就是把顶点坐标变换到屏幕空间中,再交给光栅器进行处理。通过对输入的渲染图元进行多步处理后,这一阶段将会输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等相关信息,并传递给下一个阶段。

光栅化阶段

这一阶段将会使用上个阶段传递的数据来产生屏幕上的像素,并渲染出最终的图像这一阶段也是在GPU上运行。光栅化的任务主要是决定每个渲染图元中的哪些像素应该被绘制在屏幕上。它需要对上一个阶段得到的逐顶点数据(例如纹理坐标、顶点颜色等)进行插值,然后再进行逐像素处理。和上一个阶段类似,光栅化阶段也可以分成更小的流水线阶段

CPU与GPU通信

真实的流水线🔨

渲染流水线的起点是CPU,即应用阶段。应用阶段大致可分为下面3个阶段:

1
2
graph LR
把数据加载到显存中 --> 设置渲染状态--> 调用DrawCall

把数据加载到显存中

数据需要从硬盘到内存再到显存,显存读取速度快!加载之后就会丢弃,为了运算,某些数据不应该被丢弃,

image-20210911221304571

因为从硬盘加载到RAM的过程是十分耗时的,故我们要编写程序指导CPU ,不要丢弃。

9.14 继续更新啦😃

设置渲染状态

这些状态定义了场景中的网格是怎样被渲染的

image-20210914131148894

在同一状态下渲染3个网格。由于没有更改渲染状态,因此3个网格的外观看起来像是同一种材质的物体

调用Draw Call

在设置好渲染状态后,就开始绘制了。由CPU发起,GPU接收

11.30 继续更新 😺

image-20211130192900305

GPU流水线

当GPU收到渲染命令后,进行一系列流水线操作,将图元渲染到屏幕上

GPU主要实现 几何阶段和光栅化阶段。

image-20211130193451213

其中说明如下:

  • 完全可编程控制: 顶点着色器、曲面细分着色器、几何着色器、片元着色器
  • 可配置不可编程:逐片元、裁剪、
  • 不可控制:屏幕映射、三角形设置、三角形遍历
  • 实现为必须由开发者实现,虚线为可选

顶点着色器

完全可编程

用于实现顶点的空间变换、顶点着色

  1. 输入来自于CPU
  2. 处理单位为顶点
  3. 不可以创建或销毁顶点、无法得到顶点之间的关系
  4. 处理速度很快
  5. 进行坐标变换和顶点光照
  6. 还可以输出后续所需要的一些数据

image-20211130214131052

核心任务:**顶点坐标从模型空间转换到齐次裁剪空间

image-20211130214642514

转换到齐次裁减空间后,硬件通过做透视触发得到NDC(设备坐标)下的坐标。

NDC OpenGL(Unity) 范围在【-1,1】

曲面细分着色器

是一个可选的着色器,它用于细分图元

几何着色器

是一个可选的着色器,它可以被用于执行逐图元(Per-Primitive)的着色操作,或者被用于产生更多的图元

裁剪

可配置但不可编程。

这一阶段的目的是将那些不在摄像机视野内的顶点裁剪掉,并剔除某些三角图元的面片

用于完成裁剪掉只有部分在摄像机范围内出现的物体。

image-20211130215204031

经过裁剪,会在相交处生成新的点,同时原先的旧的点(在范围外)将被删除掉。

屏幕映射

输入三维坐标,二维屏幕坐标

任务:把每个图元的X和y坐标转换到屏幕坐标系

image-20211130215609144

在OpenGL中,原点位于左下角,在DirectX中,原点在左上角

image-20211130215806421

三角形遍历

检查每个像素是否被一个三角网格所覆盖。如果被覆盖就生成一个`片元,输出片元序列。

image-20211130220052140

片元着色器

计算出颜色

经过前面的三角形步骤,获得了一系列的数据信息,用来表述一个三角形网格式怎样覆盖到每个像素的。

输入为:顶点插值数据

输出为:一个或多个颜色

image-20211130220450027

逐片元

渲染流水线的最后一步❤️

逐片元操作(Per-FragmentOperations)阶段负责执行很多重要的操作,例如修改颜色深度缓冲进行混合等

用于合并数据。

这一个阶段有如下任务:

  1. 决定每个片元的可见性。这涉及了很多测试工作,例如深度测试、模板测试等。
  2. 如果一个片元通过了所有的测试,就需要把这个片元的颜色值和已经存储在颜色缓冲区中的颜色进行合并,或者说是混合。

image-20211130220813558

需要通过所有的测试,才能和颜色缓冲区混合,最后写入颜色缓冲区

这些测试用于筛选掉一些无用片元

image-20211130221004246

当片元通过了所有测试,就会进入混合阶段

image-20211130221310360

对于透明效果需要开启混合,对于不透明物体可以选择关闭混合效果

出于性能考虑,很多GPU会先进行测试在进行片元着色器—— Early-Z技术

为了避免我们看到正在光栅化的图元,GPU使用双重缓冲,我们看到的是前置渲染结构,同时后置渲染也在工作,但后置渲染完成就会交换前后渲染的内容。

什么是Shader?

  • GPU流水线上一些可高度编程的阶段,而由着色器编译出来的最终代码是会在GPU上运行的(对于固定管线的渲染来说,着色器有时等同于一些特定的渲染设置)
  • 有一些特定类型的着色器,如顶点着色器、片元着色器等;

第二章完!