OpenGL疑惑

本篇文章基于 完整例子和调用关系qt OpenGL-CSDN博客进行的疑惑补充,建议先观看例子,在看此篇。

1. 为什么 glBindVertexArray 解绑和绑定是一样的?

        glBindVertexArray 是用来 绑定和解绑 顶点数组对象(VAO) 的。绑定 VAO 的目的是告诉 OpenGL 在当前上下文中使用哪个 VAO,它会保存和管理与该 VAO 相关的顶点缓冲区对象(VBO)和其他状态。
  • 绑定 VAO(glBindVertexArray(VAO)):
    • 绑定 VAO 后,OpenGL 会知道接下来所有关于顶点属性的数据(例如,顶点位置、颜色等)是使用哪个 VAO 的。也就是说,VAO 保存了与这些属性相关的 VBO 和属性指针。
    • 绑定时,OpenGL 会把当前的 VAO 和顶点缓冲区绑定起来,意味着接下来的绘制操作将使用当前绑定的 VAO。
  • 解绑 VAO(glBindVertexArray(0)):
    • 解绑 VAO 是为了防止后续操作错误地使用了当前绑定的 VAO。通过 glBindVertexArray(0),你告诉 OpenGL 不再使用任何 VAO。此时,OpenGL 会恢复到“无 VAO”状态,直到你再次绑定另一个 VAO。
为什么绑定和解绑看似重复?
        其实,它们的作用不同。 glBindVertexArray 是为绘制操作选择并设置使用的 VAO,而 glBindVertexArray(0) 是为了切换回一个没有绑定任何 VAO 的状态,避免后续操作影响当前的绑定状态。虽然从代码上看,它们都用在 initializeBuffers() 或 paintGL() 中,但它们的目的是不同的。
为什么解绑时参数为0?
  • 在 OpenGL 中,glBindVertexArray(0) 的意思是 解绑当前的 VAO。这里的 0 并不代表你解绑了一个编号为 0 的 VAO,而是表示“无 VAO”状态
  • 当你绑定 VAO 时,OpenGL 会记住你当前绑定的是哪个 VAO,并在后续的绘制操作中使用该 VAO。当你调用 glBindVertexArray(0) 时,OpenGL 会解除当前绑定的 VAO,使得后续操作不会受到已绑定 VAO 的影响,直到你再次绑定一个新的 VAO。
如果有多个VAO,可以解绑它们吗?
  • 解绑多个 VAO:你可以绑定并解绑不同的 VAO。例如,如果你有多个 VAO,每次绑定不同的 VAO 时,它们会覆盖当前的 VAO 绑定状态。
  • 解绑 VAO:每次你要切换到其他 VAO 时,调用 glBindVertexArray() 并传入不同的 VAO ID(例如 glBindVertexArray(1) 或 glBindVertexArray(2)),这会切换到新的 VAO。
    • glBindVertexArray(1) 会将当前 VAO 绑定为 ID 为 1 的 VAO。
    • 如果你希望解绑当前绑定的 VAO,则使用 glBindVertexArray(0),这会解除所有 VAO 绑定。
        假设你有 3 个 VAO,它们的 ID 分别是 1、2 和 3,你可以这样操作:
// 绑定第一个 VAO
glBindVertexArray(1);  
// 执行一些与 VAO 1 相关的操作...

// 绑定第二个 VAO
glBindVertexArray(2);  
// 执行一些与 VAO 2 相关的操作...

// 解绑 VAO,恢复到没有绑定 VAO 的状态
glBindVertexArray(0);  
// 执行与无 VAO 相关的操作...

// 绑定第三个 VAO
glBindVertexArray(3);  
// 执行一些与 VAO 3 相关的操作...
为什么常用 glBindVertexArray(0) 作为解绑?
        通常我们在 OpenGL 中,不需要手动管理多个 VAO 的解绑。因为 VAO 是一个 全局状态,也就是说, 在整个程序执行过程中,只有一个 VAO 可以处于绑定状态。因此:
  • 解绑 VAO 时常使用 glBindVertexArray(0) 来恢复“无绑定”状态,表示不会影响其他后续操作。
  • 绑定时通常不需要指定解绑某个特定的 VAO,只需要调用 glBindVertexArray(0) 就能解绑所有 VAO,避免后续操作无意中使用当前绑定的 VAO。
使用 glBindVertexArray(1)、glBindVertexArray(2) 等的场景
        如果你确实需要在同一时间操作多个 VAO,并且希望每次绘制时都切换不同的 VAO,那么你需要手动管理不同 VAO 的绑定。glBindVertexArray(X)(其中  X 是任何非  0 的 VAO ID)表示切换到绑定  ID 为 X 的 VAO。这样,OpenGL 会开始使用该 VAO 中保存的顶点缓冲数据和其他相关状态。绑定不同的 VAO 会影响后续的操作,特别是与顶点属性(VBO)相关的操作,因为每个 VAO 都关联了不同的 VBO 和状态。
总结:
  • glBindVertexArray(0) 是解绑当前 VAO 的标准方式,表示不再绑定任何 VAO。
  • 对于多个 VAO,你可以通过不同的 ID 来绑定不同的 VAO,例如 glBindVertexArray(1)、glBindVertexArray(2),然后在使用完一个 VAO 后,调用 glBindVertexArray(0) 来解绑。
  • 0 作为解绑 VAO 的标准表示“无 VAO 绑定”,它不会指向任何特定的 VAO。

2. glLoadIdentity 重置的作用?

        glLoadIdentity 是 OpenGL 中的一个常见函数,它的作用是 重置当前矩阵 为单位矩阵。通常用于在修改视图矩阵或投影矩阵之前重置矩阵,以确保它们不会受到之前矩阵变换的影响。
  • OpenGL 中的矩阵有三种类型:- 
    a.模型视图矩阵(Modelview Matrix):用于物体和视点之间的转换。
    b.投影矩阵(Projection Matrix):用于定义透视或正交投影。
    c.纹理矩阵(Texture Matrix):用于纹理的变换(例如缩放、平移、旋转)。
  • glLoadIdentity会把当前矩阵设置为单位矩阵,这意味着不做任何变换。其常见用途包括:
    • 在每次绘制前重置模型视图矩阵:确保没有之前的变换影响当前的绘制。
    • 在设置投影矩阵时使用:为了避免之前的变换影响新的透视设置,常常会在设置透视矩阵之前调用 glLoadIdentity。

3. 为什么初始化时绑定一次,绘制时还要再绑定一次?

        glBindVertexArray(VAO) 绑定 VAO 的目的是让 OpenGL 知道我们正在使用哪个 VAO。实际上,绑定 VAO 的过程是告诉 OpenGL 你将使用该 VAO 所保存的顶点数据进行渲染。在整个程序中,我们通常有多个不同的 VAO,每个 VAO 可能对应不同的几何数据。
为什么绘制时还需要绑定 VAO?
  • 初始化绑定一次:在 initializeBuffers() 中,我们创建并初始化了 VAO,并将其与顶点数据(VBO)关联起来。这时,绑定 VAO 使得 OpenGL 在设置顶点属性时知道使用哪个 VAO。此时,VAO 内部已经保存了数据和属性信息。
  • 绘制时绑定 VAO:每次绘制时,OpenGL 需要知道当前应该使用哪个 VAO 来绘制。因此,在 paintGL() 或绘制的函数中,我们必须 重新绑定 VAO,这样 OpenGL 才会知道要使用哪个 VAO 中的顶点数据进行绘制。
为什么不能只绑定一次?
  • OpenGL 是一个状态机,它在执行渲染任务时会保持当前的状态(例如,当前绑定的 VAO)。当你调用绘制函数时,OpenGL 会基于当前的绑定状态进行渲染。
  • 绑定 VAO 后进行绘制:每个绘制调用(如 glDrawArrays)都是针对特定的 VAO 的,所以每次调用绘制函数时都需要重新绑定 VAO,确保每个绘制操作使用的是正确的顶点数据。
绑定和解绑 VAO 的顺序:
  1. 在初始化阶段绑定 VAO(initializeBuffers()),用于设置顶点数据。
  2. 每次绘制时绑定 VAO(paintGL()),以确保绘制时使用正确的顶点数据。
  3. 绘制后可以解绑 VAO,以避免影响后续的 OpenGL 操作。

4. VAO和VBO如何绑定的?

创建 VAO:
        glGenVertexArrays(1, &vao):这行代码是用来 创建 VAO 的。它会生成一个未绑定的 VAO,并将其 ID 存储在 vao 变量中。
        glGenVertexArrays(1, &vao) 创建一个 VAO,并将其 ID 存储在 vao 指定的变量中。此时,VAO 已经存在,但 尚未绑定。
绑定 VAO:
        glBindVertexArray(vao): 这行代码是  绑定  VAO 的,告诉 OpenGL 在接下来的操作中使用哪个 VAO。绑定 VAO 后,所有后续的操作(例如,设置顶点数据、定义顶点属性指针等)都将与当前绑定的 VAO 相关联。
        glBindVertexArray(vao) 绑定  vao(你刚才通过  glGenVertexArrays 创建的 VAO),之后 OpenGL 就会使用这个 VAO 中保存的状态,直到你切换到另一个 VAO 或解绑它。
代码示例:
GLuint vao, vbo;
glGenVertexArrays(1, &vao);  // 创建 VAO
glGenBuffers(1, &vbo);       // 创建 VBO

glBindVertexArray(vao);      // 绑定 VAO,后续操作会与此 VAO 相关联

glBindBuffer(GL_ARRAY_BUFFER, vbo);  // 绑定 VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);  // 设置顶点数据

// 设置顶点属性指针(例如,位置、颜色等)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
glEnableVertexAttribArray(0);  // 启用顶点属性数组

// 在绑定 VAO 后进行其他操作,比如绘制等

glBindVertexArray(0);  // 解绑 VAO(可选)
步骤解析:
  1. glGenVertexArrays(1, &vao) 创建一个 VAO,但此时它还没有被绑定。
  2. glBindVertexArray(vao) 将 VAO 绑定到当前上下文。接下来所有操作都会与这个 VAO 相关联。
  3. 创建并绑定一个 VBO(顶点缓冲对象),然后通过 glBufferData 将顶点数据加载到该 VBO 中。
  4. 设置顶点属性指针 glVertexAttribPointer,并启用这些属性 glEnableVertexAttribArray,这些操作是与当前绑定的 VAO 相关联的。
  5. 最后,如果不再需要继续使用该 VAO,可以调用 glBindVertexArray(0) 来解绑它。
为什么 VAO 和 VBO 都需要绑定?
        VAO 存储了顶点数据的描述信息(即顶点属性),而 VBO 存储了实际的顶点数据。通过绑定 VAO 和 VBO,OpenGL 可以知道如何解释这些数据以及如何在 GPU 上进行渲染。
关于 VAO 和 VBO 绑定顺序:
1.VAO 在绑定之前的 VBO:
  • 在调用 glBindVertexArray(VAO) 之前,所有的 VBO(顶点缓冲区对象)都会在当前 OpenGL 上下文中有效。
  • 但是,在 VAO 绑定之前的 VBO 不会被 VAO "自动包含" 或 "记录",它们只是在当前的 OpenGL 上下文中有效。
  • 换句话说,VAO 不会记录 VBO 在它绑定之前的状态。当开始设置顶点属性(glVertexAttribPointer 或 setAttributeBuffer)时,这个绑定的 VBO 会影响当前的 VAO。
2.VAO 绑定后 VBO 的行为:
  • 一旦绑定了 VAO,所有的后续 VBO 和顶点属性设置都会记录在这个 VAO 中。
  • 所以,在 glBindVertexArray(VAO) 之后创建和绑定的 VBO,VAO 会自动 "记录" 这些 VBO,并与顶点属性一起存储,确保在渲染时能够正确地使用它们。
具体行为:
1. 在 VAO 绑定之前的 VBO:
  • 这些 VBO 对 VAO 是不可见的。
  • VAO 只会记录你在绑定它之后的顶点数据和顶点属性设置。
  • 换句话说,glBindVertexArray(VAO) 之前设置的 VBO 和顶点属性不会被 VAO 保存。
2. 在 VAO 绑定之后的 VBO:
  • 所有 VBO 和顶点属性设置都会被 VAO 记录下来。
  • 如果你创建了多个 VBO,并在绑定了 VAO 后设置了多个顶点属性,这些都会在 VAO 中保存,之后你只需要通过 glBindVertexArray(VAO) 进行切换即可使用它们。
举个例子来说明:
// 1. 先创建 VAO 和 VBO
GLuint VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);

// 2. 在绑定 VAO 之前设置顶点数据和 VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
GLfloat vertices[] = {
    -1.0f,  1.0f, 0.0f,
    -1.0f, -1.0f, 0.0f,
     1.0f, -1.0f, 0.0f
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// 3. 在绑定 VAO 之前设置顶点属性(不推荐)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);

// 4. 现在绑定 VAO
glBindVertexArray(VAO);  // VAO 绑定后,它才会记录 VBO 和顶点属性的状态

// 5. 设置另一个 VBO 和属性(VAO 记录下来)
GLuint VBO2;
glGenBuffers(1, &VBO2);
glBindBuffer(GL_ARRAY_BUFFER, VBO2);
GLfloat vertices2[] = {
    0.0f, 0.0f, 0.0f
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW);

// 设置顶点属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);

// 6. 解绑 VBO 和 VAO
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
在这个例子中:
  • 第 2 步和第 3 步 在绑定 VAO 之前设置了一个 VBO 和它的顶点属性,这些设置 不会 被 VAO 记录下来。
  • 第 5 步创建了另一个 VBO VBO2,并在绑定了 VAO 后设置了另一个顶点属性。这个 VBO 和顶点属性设置会 被 VAO 记录下来,并且以后绑定 VAO 时就能使用这些数据。
  • VAO 绑定之前的 VBO 和顶点属性设置,不会被 VAO 自动记录。
  • VAO 绑定之后的所有 VBO 和顶点属性设置,都会被 VAO 记录。这就是为什么你需要在绑定 VAO 后再设置顶点数据和顶点属性,这样你就能通过 VAO 来高效管理渲染状态。

5. 总结:

  • glBindVertexArray 的作用:绑定 VAO 是为了让 OpenGL 知道你正在使用哪个 VAO,确保之后的操作(如设置顶点数据、绘制图形等)是基于正确的 VAO 进行的。解绑是为了避免操作影响其他部分。
  • glLoadIdentity 的作用:它重置当前矩阵为单位矩阵,通常在进行投影或视图矩阵设置之前调用,以确保之前的矩阵变换不会影响当前的设置。
  • 为什么初始化和绘制时都要绑定 VAO:在初始化时绑定 VAO 是为了设置顶点数据的格式,而在绘制时绑定 VAO 是为了告诉 OpenGL 使用哪些顶点数据进行绘制。每次绘制前都需要绑定相应的 VAO,以确保渲染使用的是正确的数据。

你可能感兴趣的:(OpenGL,算法,c++,qt,OpenGL)