渲染的开始
图形渲染管线
图形渲染管线(Graphics Pipeline)指的是完成将3D场景转化为最终在屏幕上显示的2D图像的过程。图形渲染管线分为两个主要阶段:几何渲染阶段和光栅化阶段。
几何渲染阶段
几何数据输入:3D场景中的模型、相机、灯光等数据被输入到渲染管线中。
顶点着色器(Vertex Shader):对输入的3D模型的顶点进行变换、投影和裁剪等操作,将其转换到屏幕空间。
图元装配(Primitive Assembly):将顶点组装成基本的图元,如点、线段或三角形。
几何着色器(Geometry Shader,可选):在一些现代的图形渲染管线中,可以使用几何着色器进一步处理图元,如生成新的几何图元或对现有图元进行变换。
面剔除(Face Culling,可选):根据需要,可以剔除不可见的面,以提高渲染性能。
光栅化阶段
光栅化(Rasterization):将图元转换为屏幕上对应的像素点,并生成片元(Fragment)。
片元处理(Pixel Processing):对每个片元进行插值、纹理采样、光照计算等处理。
片段着色器(Fragment Shader):对每个像素执行着色操作,确定最终像素的颜色和特性。
深度测试(Depth Testing):根据深度值来确定像素是否可见,遮挡面会被丢弃。
帧缓冲输出:最终渲染结果会输出到帧缓冲(Frame Buffer),并在屏幕上显示。
着色器
着色器(Shader)是一种特殊程序,用于描述在图形渲染管线的不同阶段对图形数据的处理和着色过程。着色器主要用于在GPU(图形处理单元)上执行高度并行的图形计算,从而实现对3D场景中的几何对象和图像的渲染、着色和效果处理。着色器主要分为顶点着色器、几何着色器、片段着色器(也叫像素着色器)三个类型。在OpenGL中,着色器是用OpenGL着色器语言(OpenGL Shading Language, GLSL)写成的。
在OpenGL中,我们必须定义至少一个顶点着色器和一个片段着色器(因为GPU中没有默认的顶点/片段着色器)。
顶点
OpenGL中,一个顶点(Vertex)是一个3D坐标的数据的集合。顶点属性(Vertex Attribute)是指一组用于描述顶点的各个特征属性的数据,每个顶点可以具有多个属性,例如位置、颜色、法线方向、纹理坐标等。一系列顶点的集合称为顶点数据(Vertex Data),它是由顶点属性表示的。通常,顶点属性/数据会以数组的形式传递给OpenGL,并且每个数组元素对应一个顶点的所有属性值。在渲染过程中,OpenGL会按顶点的顺序来使用这些属性,从而构建出图形模型。
由于OpenGL是一个3D图形库,所以在OpenGL中我们指定的所有坐标都是3D坐标(x、y和z),并且仅当x、y、z都在-1.0到1.0的范围内时才处理它。所有在这个范围内的坐标叫做标准化设备坐标(Normalized Device Coordinates),此范围内的坐标最终显示在屏幕上(在这个范围以外的坐标则不会显示)。
假设我们需要渲染一个三角形,我们一共要指定三个顶点,并把它们放在一个数组中。

定义这样的顶点数据以后,我们会把它发送给顶点着色器。它会在GPU上创建内存用于储存这些顶点数据。我们通过顶点缓冲对象(Vertex Buffer Objects, VBO)管理这块内存。而VBO需要调用函数glGenBuffers()来生成。glGenBuffers的作用是在显存中分配一块缓冲区,并返回一个唯一的标识符。这个标识符在OpenGL上下文中是唯一的,当不再使用它时,应调用函数glDeleteBuffers()释放相应的资源。


调用glGenBuffers:

OpenGL中有很多用于绑定缓冲数据的缓冲对象类型,其中用于绑定顶点数据的是GL_ARRAY_BUFFER。OpenGL允许同时绑定多个缓冲,只要它们是不同的缓冲类型。绑定缓冲使用函数glBindBuffer()。

我们可以把刚刚创建的VBO绑定到对应的类型上:

调用glBindBuffer后,在对应目标(GL_ARRAY_BUFFER)上的所有操作都将影响到当前绑定的缓冲对象(VBO)。
注意:OpenGL是一个状态机,因此一旦将缓冲对象绑定到目标上,除非显式绑定其他缓冲对象或者解绑,该缓冲对象会一直保持绑定状态,即使在不同的绘制调用中也有效。
接下来我们调用函数glBufferData(),用来把用户定义的数据复制到当前绑定缓冲。

它的第一个参数是缓冲对象类型,第二个指定传输数据的大小(以字节为单位),第三个是实际发送的数据,第四个指定缓冲区的使用方式,例如GL_STATIC_DRAW表示缓冲区将被写入一次,但要被绘制多次。
我们把之前创建的顶点数据传入到缓冲中:

至此我们已经把顶点数据储存在了显存中。