四、理解OpenGL渲染管线

Posted by 卢小胖 on 2023-09-27
Estimated Reading Time 4 Minutes
Words 1.2k In Total
Viewed Times

理解渲染管线

之前我们简单提到了固定渲染管线和可编程渲染管线,他们有共同特点:渲染管线,那什么是渲染管线?

Contex为我们提供OpenGL的运行环境,而具体的操作则是在OpenGL的渲染管线中进行的。这里的渲染管线是一次完整的图形图像渲染过程,OpenGL1.0对应的渲染管线是固定渲染管线。意味着开发者只能做一些简单操作,后面OpenGL2.0之后渲染管线升级为可编程渲染管线。其中可编程的部分是顶点着色器(Vertex Shader)和片段着色器(Fragament Shader)。

image.png

我们从头一个一个理解:

  1. 创建填充顶点数组(所要绘制图形的坐标信息)然后OpenGL传递到该渲染管线的顶点着色器中,将坐标信息转换为OpenGL的内部坐标信息。
  2. OpenGL有了图形的坐标信息后就通过图元组装描绘出基本的图形。
  3. 通过光栅化将线性的图形映射到屏幕一个个像素(Pixel)上。
  4. 通过片段着色器给每个像素(Pixel)赋予颜色值。
  5. 最后将图像传递到帧缓冲(Framebuffer)中提供给屏幕进行刷新操作。

image.png

OpenGL在渲染一个画面的大体流程,绿色是我们可编程部分。

1. 顶点着色器(Vetex Shader)

负责坐标和图形的描述。在OpenGL中有三种基本的图形点、线、三角形,只能通过这三种基本图形去描述一个图形。其中在OpenGL中我们的显示区域位于x,y均为[-1,1]之内的空间。

image.png

  • :点存在于三维空间,坐标用(x,y,z)表示。
  • 线:由两个三维空间中的点组成。
  • 三角形:由三个三维空间的点组成。

image.png

如图,我们可以通过点、线、三角形创建出一个简单的3D模型。

2. 图元装配

负责将顶点着色器输出的坐标信息进行组装和裁剪,将所有3D的图元转化为屏幕上2D的图元。

image.png

具体的图元装配代码示例参考:图元装配和光栅化

3. 光栅化

图元装配后,将坐标数据裁剪映射为窗口上的像素数据,但是要转换为图像还需要颜色信息。在使用光栅化后基本图元被转换为供片段着色器使用的片段(Fragment),即实现通过插值运算将连续的值用一个个像素片段表示出来。

image.png

4. 片段着色器(Fragament Shader)

到了这一步我们已经有了一个个的像素片段(Fragament),我们在这个阶段给它涂上颜色值就可以变成一个完整的像素点。包括位置,颜色,纹理坐标等信息。同时我们可以编写Fragament Shader的脚本来实现对每个像素颜色的变换来达到一些效果,如纹理贴图,光照,环境光,阴影。

5. 片段测试

这个阶段是对每个像素点进行测试保证这些像素点是正确可用的。最后在输入到帧缓冲(Frambuffer)中。

image.png

6. 帧缓存 (Framebuffer)

经过上述处理流程,我们想要看到的图形图像数据最后都会存储到帧缓存区(Framebuffer) 中。我们可以同时存在很多帧缓存(Framebuffer),并且可以通过OpenGL让GPU把渲染结果存储到任意数量的帧缓存中(这里引申出一个离屏渲染的工作概念)。
但是,只有将内容绘制到视窗体提供的帧缓存(Renderbuffer) 中,才能将内容输出到显示设备。在实现上渲染缓存(Renderbuffer)是直接跟屏幕映射的,可以绕开CPU进行工作。

image.png

7. 渲染帧缓存(Renderbuffer)

基本工作原理是存在两个缓存(前缓存和后缓存),当屏幕的刷新同步信号到达时让系统将后缓存交换到前缓存区上。
这个刷新的时间是由系统决定的,比如屏幕刷新率是60fps即每16.75ms会发生一次前后缓存的交换。我们只需要准备好后缓存的数据提供给系统就能进行屏幕刷新渲染了。

image.png


总结:

理解OpenGL渲染管线,就是理解渲染流程,OpenGL是如何将一堆顶点数据,纹理数据,经过裁剪-透视变换-视觉变换,生成像素数据,最后绘制在屏幕上。

以上只是理论基础,我们在后续中进行实际开发,更加深入理解OpenGL。