从0开始制作软渲染器(零)

本文介绍了基本的渲染管线知识。

渲染管线

渲染管线是每个编写渲染器的人都逃不开的话题。这里我直接推荐这个博客,讲的非常详细。

大体来说,在OpenGL中,渲染管线的流程如下图:

渲染管线概览

顶点输入(Vertex Data Input)

这一步就是将顶点传输给渲染器,在OpenGL中就是使用VBO,EBO,VAO然后将顶点数据填充给VBO。

运行顶点着色器(Vertex Shader)

接下来需要运行顶点着色器,顶点着色器会对每个输入的顶点进行操作。

顶点着色器:

  • 输入: 顶点

  • 输出: 顶点

一般在这里进行MVP矩阵运算。对图形学算法的优化的一个方法就是将在片段着色器中运行的代码尽可能地放到顶点着色器中,这样着色器处理的像素点就少。

曲面细分着色器(Tessellation)

然后顶点会传送到曲面细分着色器,在这一阶段进行曲面细分和曲面简化。

曲面细分用于给面增加更多的顶点,以此来增加更多的细节。曲面简化则是相反的操作,会减少面数,减少细节。

这常常用在LOD(细节层次技术)优化中。LOD简单来说就是对远处的物体使用低面数的模型,对近处的看的很清楚的物体使用高面数的模型。这个时候,如果你只有一个模型,但是想使用LOD,你就可以使用曲面细分和曲面简化来达到效果。

这一阶段有两个着色器:曲面控制着色器,曲面求值着色器。

曲面细分着色器:

  • 输入: 顶点

  • 输出: 顶点

几何着色器(Geometry Shader)

这个着色器用于产生新的图元。

注意其和曲面细分着色器的区别:曲面细分着色器是对顶点操作,并且输出顶点。而几何着色器则是对图元操作,输出的是图元。

几何着色器:

  • 输入: 图元

  • 输出: 图元

图元装配(Primitive Setup)

几何着色器输出的图元会进行图元装配。图元组装做如下事情:

  • 将输入的顶点组合成图元

面剔除和裁剪(Face Culling and Clipping)

面剔除即剔除指定的面,通常是顺时针或逆时针的面。裁剪则是当三角形全部位于屏幕外面的时候就直接丢弃以减少计算时间。

光栅化(Rasterization)

光栅化就是对图元内的每个点进行采样,或者说将图元离散化,即屏幕上哪些点在图元内,哪些点不在。不在的点会被丢弃。

光栅化之前会做两件事:

  • 进行透视除法

  • 进行视口变换

光栅化会通过插值来计算出图元内部点的信息(比如z坐标),然后将此点传给片段着色器。

片段着色器(Fragment Shader)

片段着色器是对点进行着色的着色器,它是产生高级渲染效果的着色器,但同时也是最吃时间的着色器。

因为在光栅化之前的所有着色器都是只对顶点进行操作(几何着色器其实也是增加或者丢弃顶点)。一个模型再复杂,顶点也就几千个。但是光栅化会将图元内部的所有点都计算出来传给着色器,这会大大增加点的数目,所以到达片段着色器的点数量会剧增。

测试混合阶段

这是最后一个阶段,主要做两件事:

  • 进行测试,包括:

    • 深度测试

    • 模板测试

    • alpha测试

  • 对于测试通过的点,还要进行alpha混合来达到透明效果

注意,这里可能有提前深度测试优化,即将深度测试提前到光栅化阶段。因为如果片段着色器不改变点的深度值的话,点的深度值其实在光栅化时就已经确定了,这个时候光栅化阶段可以做深度测试将点丢弃,减少片段着色器的负担。

输出到Framebuffer上

最后所有的点会被绘制到framebuffer上,然后系统会将framebuffer绘制到屏幕上,完成一次渲染。

updatedupdated2023-06-082023-06-08