一、OpenGL 加载流程
OpenGL的加载过程涉及多个步骤,主要包括初始化OpenGL环境、创建窗口、设置着色器和渲染循环。以下是一般情况下OpenGL加载的基本步骤:
初始化库和上下文: 在开始使用OpenGL之前,首先要初始化 OpenGL 库和创建 OpenGL 上下文。这可以通过调用特定的库函数(如GLFW、SDL等)来实现。这些库负责创建窗口、管理输入和OpenGL上下文等。
//初始化GLFW和配置
glfwInit();
//使用glfwWindowHint函数来配置GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//将主版本号(Major)设为3
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//次版本号(Minor)设为3
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//我们同样明确告诉GLFW我们使用的是核心模式(Core-profile)。明确告诉GLFW我们需要使用核心模式意味着我们只能使用OpenGL功能的一个子集
创建窗口和上下文: 使用选定的库,你可以创建一个窗口,将OpenGL上下文绑定到该窗口中。上下文是OpenGL用来管理和执行图形渲染的关键环境。
//创建一个窗口对象,这个窗口对象存放了所有和窗口相关的数据,而且会被GLFW的其他函数频繁地用到
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
//将我们窗口的上下文设置为当前线程的主上下文
glfwMakeContextCurrent(window);
加载和编译着色器: 创建和编译顶点着色器和片段着色器,这些着色器将定义你的渲染效果。通常,你需要使用GLSL(OpenGL Shading Language)编写着色器代码,然后将其加载到OpenGL中。
传入着色器的源文件,读取文件的缓冲内容到数据流中,编译着色器
// 2. 编译着色器
unsigned int vertex, fragment;
// 顶点着色器
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// 片段着色器
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
创建着色器程序: 使用已编译的顶点和片段着色器,创建一个着色器程序对象。这个程序对象将管理着色器的链接和使用。
// 着色器程序
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
if (geometryPath != nullptr)
{
glAttachShader(ID, geometry);
}
glLinkProgram(ID);
设置顶点数据和缓冲区: 为你的渲染创建顶点数据,例如顶点坐标、法线、纹理坐标等。将这些数据存储在缓冲区对象中,这些缓冲区可以是顶点缓冲区、索引缓冲区等。
设置顶点属性指针: 将顶点数据与着色器的输入变量(如顶点坐标、法线等)关联起来。通过使用glVertexAttribPointer
函数,将顶点数据绑定到顶点着色器的属性变量。
//初始化缓冲,最终使用Draw函数绘制网格
glGenVertexArrays(1, &VAO);//任何随后的顶点属性调用都会储存在这个VAO中
glGenBuffers(1, &VBO);//针对缓冲ID:VBO生成一个VBO对象
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);//要想使用VAO,要做的只是使用glBindVertexArray绑定VAO
glBindBuffer(GL_ARRAY_BUFFER, VBO);//把创建的VBO缓冲对象绑定到GL_ARRAY_BUFFER目标上
//我们使用的任何(GL_ARRAY_BUFFER目标上的)缓冲调用都会用来配置当前绑定的缓冲(VBO)
//glBufferData参数:(1)目标缓冲的类型(2)传输数据的大小(3)我们希望发送的实际数据(4)希望显卡如何管理给定的数据(* GL_STATIC_DRAW :数据不会或几乎不会改变。* GL_DYNAMIC_DRAW:数据会被改变很多。* GL_STREAM_DRAW :数据每次绘制时都会改变)
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);//把之前定义的顶点数据复制到缓冲的内存中
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW);
// 顶点位置
glEnableVertexAttribArray(0);
//glVertexAttribPointer参数:(1)指定我们要配置的顶点属性,具体依据着色器中的定义layout(location = 0)我们希望把数据传递到这一个顶点属性中
//(2)顶点属性的大小VEC3,大小就是3.(3)数据的类型(4)是否希望数据被标准化(Normalize),是的话就会映射到0-1之间
//(5)Stride步长,顶点属性组之间的间隔(6)它表示位置数据在缓冲中起始位置的偏移量
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
// 顶点法线
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));//Normal在Vertex结构体中的偏移量
// 顶点纹理坐标
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));
// vertex tangent
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Tangent));
// vertex bitangent
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Bitangent));
glBindVertexArray(0);
启用着色器和缓冲区: 启用着色器程序,让OpenGL知道你要使用哪些着色器进行渲染。还要绑定相应的缓冲区对象,以供渲染使用。
// 程序ID
unsigned int ID;
glUseProgram(ID);
设置绘制参数: 设置绘制参数,如清除颜色、深度缓冲区,设置视口、投影矩阵等。
渲染循环: 进入渲染循环,即不断地绘制场景并处理输入。在每一帧中,你需要设置相应的状态、更新数据,然后调用绘制函数来执行渲染操作。
while (!glfwWindowShouldClose(window))
{
...
}
交换缓冲区: 在渲染完成后,使用交换缓冲区操作,将渲染结果显示在屏幕上。
```
glfwSwapBuffers(window);//交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。
```
二、OpenGL ES 加载流程
OpenGL ES(OpenGL for Embedded Systems)是用于嵌入式系统的一种轻量级的OpenGL API。它在很大程度上与标准的OpenGL相似,但考虑了资源受限的嵌入式环境。OpenGL ES的加载过程与标准的OpenGL加载过程类似,但有一些特定的步骤和差异。以下是OpenGL ES加载过程的基本步骤:
初始化库和上下文: 与标准OpenGL一样,你需要使用特定的库函数(如EGL、GLFW、SDL等)来初始化OpenGL ES库和创建OpenGL ES上下文。这些库负责创建窗口、管理输入和OpenGL ES上下文等。
创建窗口和上下文: 使用选定的库,创建一个窗口,并将OpenGL ES上下文绑定到该窗口中。上下文是OpenGL ES用来管理和执行图形渲染的关键环境。
加载和编译着色器: 创建和编译顶点着色器和片段着色器,同样使用GLSL编写着色器代码,然后将其加载到OpenGL ES中。OpenGL ES 2.0和3.0支持GLSL ES(OpenGL ES Shading Language)。
创建着色器程序: 使用已编译的顶点和片段着色器,创建一个着色器程序对象。这个程序对象将管理着色器的链接和使用,与标准OpenGL类似。
设置顶点数据和缓冲区: 为你的渲染创建顶点数据,将这些数据存储在缓冲区对象中。OpenGL ES支持顶点缓冲区、索引缓冲区等。
设置顶点属性指针: 将顶点数据与着色器的输入变量关联起来,通过使用glVertexAttribPointer
函数。同样,将顶点数据绑定到顶点着色器的属性变量。
启用着色器和缓冲区: 启用着色器程序,绑定相应的缓冲区对象,使OpenGL ES知道你要使用哪些着色器进行渲染。
设置绘制参数: 设置绘制参数,如清除颜色、深度缓冲区,设置视口、投影矩阵等,与标准OpenGL类似。
渲染循环: 进入渲染循环,即不断地绘制场景并处理输入。在每一帧中,设置状态、更新数据,然后调用绘制函数进行渲染。
交换缓冲区: 在渲染完成后,使用交换缓冲区操作,将渲染结果显示在屏幕上。