OpenGL Pipeline
OpenGL 实现了我们通常说的渲染管线(rendering pipeline),它是一系列数据处理过程,并且将应用程序的数据转换到最终渲染的图像。OpenGL首先接收用户提供的几何数据(顶点和和几何图元),并且将它输入到一系列着色器阶段中处理,包括:定点着色、细分着色、几何着色,然后将几何数据送入光栅化单元(rasterizer)。光栅化单元负责对所有剪切区域(clipping region)内的图元生成片元数据,然后对每个生成的片元着色器。
OpenGL Host
向OpenGL传输数据
- 在Modern OpenGL中,host(CPU端,也称服务端)会设置一个有序的顶点列表,并把这些数据送到流水线,这些顶点定义了原图形的属性:形状、点、法线、纹理坐标等等,同时也告诉了OpenGL如何去解读这些数据(Vertex Array Object)。
- OpenGL需要将所有的数据都保存到缓存对象(Vertex Buffer object等等), 它相当于由OpenGL服务端维护的一块内存区域(GPU)。创建这样的数据缓存,最常用的方法就是使用
glBufferData()
命令。
将数据传输到OpenGL
缓存初始化之后,我们就可以在host上通过调用OpenGL的一个绘制指令来请求渲染几何图元,例如glDrawArrays()
。OpenGL的绘制通常是将顶点数据发送到OpenGL服务端,通常来说就是显卡。我们可以将一个顶点视为一个需要统一处理的数据包,其可以包含任何数据(位置、纹理、颜色、法向量等等)。
Vertex Rendering
顶点着色
- 顶点着色器对每个独立的顶点进行基础处理,顶点Shader接受输入属性,并且基于用户定义的程序,将每个输入的顶点转换为一一对应输出顶点。
- 顶点Shader可以使用用户自定义的输出,但是也有一个特殊的输出gl_Position,代表顶点的最终位置,一般填充为裁剪空间的顶点位置。
- 一个限制是,在顶点处理过程中,因为各个顶点shader之间的不可共享状态,任何一个输入属性必须被映射到唯一一个顶点输出中。如果你填充了完全相同的属性到相同的顶点Shader中的相同原图形中,那么你将会获得相同的输出顶点数据。
- 一个复杂的程序可能会有多个顶点着色器,但是同一时刻只有一个起作用
细分着色
- 细分着色器会使用patch来描述一个物体的形状,并且使用相对简单的Patch几何体连接来完成细分的工作,其结果是几何图元数量增加,使得模型外观会变的更为平顺
- 细分着色器分为控制和计算两个步骤
几何着色
- 几何Shader是用户自定义的程序,处理每个输入原图形,并产生0-n个输出原图形。一个很典型的应用就是计算每个顶点的法向量。
Vertex Post-Processing
- 经过基于Shader的(可编程的)顶点处理之后,顶点会执行一系列的固定管线函数处理,由Host(context)来控制。
图元装配
- 图元装配阶段将之前顶点和相关的几何图元之间组织起来,准备下一步的剪裁和光栅化工作。
剪切
- 原图形随后被裁切,裁切意味着,在边界上的原图形,将会被分离成几部分原图形,这样所有的全图型,都将保证在可视空间之内。
- 同时,最后的顶点处理Shader也可以执行用户自定义操作。顶点位置在透视分割和视图转换时,被转换到裁切空间。
面剔除
- 三角形元数据可以被剔除,这基于三角形面在窗口空间的朝向。他允许你不渲染背对观察者的三角形。这些三角形会被离观察者近的面挡住,所以你永远没有必要去渲染他们。
光栅化
- 剪切之后马上要执行的工作,就是将更新后的图元传递到光栅化单元中,生成对应的片元。我们可以将一个片元视为一个候选像素,也就是可以放置在帧缓存中的像素。
- 以屏幕而发出均匀扫描线,而此时由于图元是3D的,所以会进行非均匀采样,这样我们就能将图元映射到屏幕上。
- 这个过程可以选择性的进行多重采样。(anti-aliasing)
片元着色
- 光栅化过程中产生的片段数据,将由片段Shader进行处理。片段Shader的输出是为每个颜色Buffer写入的颜色列表,一个深度值和一个模板值。片段程序无法为片段设置模板值,但是可以控制颜色和深度值。
- 片段Shader也是可选的。如果你不使用片段Shader,深度(和模板)值,将是他们的通常值。但是所有片段的颜色值是未定义的。不使用片段Shader进行渲染,在我们只想将一个原图形的深度信息写入到深度缓冲的时候,会显得非常有用,比如在做 遮挡查询(Occlusion Query )测试的时候。
逐片元操作
剔除测试
剔除测试是由Host context实现,如果测试被激活且片段没有通过测试,对应的像素或者采样将不会被更新(discard):
- 像素所有权测试
- 剪刀测试(Scissor Test):当片段的像素位于指定的屏幕的矩形区域之外的时候,测试将会失败
- 模板测试(Stencil Test):如果被激活,如果测试提供的模板值,与用户指定的模板缓冲中的值无法匹配,那么测试将会失败
- 深度测试(Depth Test,z-buffering):如果被激活,当片段的深度值,与用户指定的深度缓冲中的深度值,不匹配,那么测试将会失败
- Alpha Test
混合
- 对于每一个片段颜色值,在已经在FrameBuffer中的颜色值,和当前颜色值之间,有一个特殊的混合操作