01 着色器对象和着色器程序对象

着色器对象和着色器程序对象是两个不同的实体,它们具有不同的作用和生命周期。

  • 着色器对象是用来存储和编译着色器程序的源代码的。你可以创建多个着色器对象,每个对象都代表一个特定类型的着色器(如顶点着色器、片段着色器等)。然后,你可以将这些着色器对象附加到着色器程序对象,并使用‘glLinkProgram’函数将它们链接在一起形成一个完整的着色器程序。

  • 一旦你将着色器对象链接到着色器程序对象之后,并且通过glLinkProgram函数成功链接了着色器程序,那么着色器对象就不再需要了。着色器程序已经包含了编译后的着色器代码,并且可以被OpenGL使用。

  • 删除着色器对象的目的是为了释放资源和减少内存占用。当你不再需要着色器对象时,通过调用glDeleteShader函数可以删除它们。这样,OpenGL就会释放与着色器对象相关的内存和资源。

需要注意的是,删除着色器对象不会影响已经链接的着色器程序。着色器程序仍然可以正常使用,因为它已经将着色器对象中的代码复制到了自己的内部。删除着色器对象只是告诉OpenGL,你不再需要这些对象了,它可以释放相关资源。

02 VAO(Vertex Array Object)

VAO(Vertex Array Object)是OpenGL中的一个对象,用于管理顶点数据的状态和配置。它可以看作是一个容器,用于存储与顶点数据相关的状态信息。

VAO对象的作用如下:

  • 封装顶点数据配置: VAO对象可以存储顶点缓冲对象(VBO)的绑定状态、顶点属性指针的配置以及其他与顶点数据配置相关的状态信息。通过将这些配置封装在VAO中,可以在需要时快速切换和重用这些配置,从而简化代码并提高效率。

  • 简化顶点数据设置: VAO对象允许你将顶点数据的配置与顶点数组的绑定状态关联起来。一旦绑定了VAO对象,之后的顶点数据配置操作都会自动应用到当前绑定的VAO对象上,而不需要重复配置和绑定每个顶点数组。

  • 提高性能: 使用VAO对象可以提高渲染性能。因为在绘制过程中,只需要绑定适当的VAO对象,并启用顶点属性,OpenGL就可以立即获取所有必要的顶点数据配置,而不需要重复查询和设置各种状态,从而减少了开销。

  • 代码可读性和可维护性: 通过使用VAO对象,可以将顶点数据的配置和状态信息集中管理,并在需要时进行切换和重用。这样可以使代码更具可读性、可维护性和组织性,尤其是在涉及多个顶点数组和不同的渲染状态时。

总之,VAO对象是一个方便的机制,用于存储和管理顶点数据的状态和配置信息。它简化了顶点数据的设置过程,提高了渲染性能,并提供了更好的代码组织和可读性。在实际的OpenGL编程中,使用VAO对象通常是一个良好的实践。

03 VBO(Vertex Buffer Object)

VBO(Vertex Buffer Object)是OpenGL中的一个对象,用于存储和管理顶点数据。它可以看作是一个内存缓冲区,用于存储顶点数据,如顶点坐标、颜色、法线、纹理坐标等。

VBO对象的作用如下:

  • 存储顶点数据: VBO对象提供了一种高效的方式来存储顶点数据。你可以将顶点数据传输到VBO中,从而将数据存储在GPU的内存中,以供后续渲染使用。这样可以避免频繁地从CPU向GPU传输数据,提高数据传输的效率。

  • 提高渲染性能: 通过将顶点数据存储在VBO中,可以减少数据传输的次数和开销。一旦顶点数据存储在VBO中,你可以重复使用它进行多次渲染操作,而不需要每次都重新传输数据。这样可以减少CPU到GPU之间的数据传输,提高渲染性能。

  • 支持批量渲染: VBO对象支持批量渲染,即一次绘制多个顶点的操作。通过配置VBO和索引缓冲对象(Index Buffer Object,IBO),你可以绘制多个顶点、多个三角形或其他几何图元,从而实现高效的批量渲染。

  • 灵活的数据格式: VBO对象可以存储各种类型的顶点数据,如顶点坐标、颜色、法线、纹理坐标等。你可以根据需要定义顶点的数据格式,灵活地配置VBO对象,以满足不同的渲染需求。

  • 支持动态更新: VBO对象支持动态更新顶点数据。你可以通过映射VBO到CPU内存的方式,直接在CPU端修改顶点数据,然后将修改后的数据传输到GPU中。这使得你可以实时更新顶点数据,实现动画效果或实时交互。

总之,VBO对象是一种高效的方式来存储和管理顶点数据。它提供了性能优化和灵活性,可以减少数据传输的开销,支持批量渲染,以及动态更新顶点数据。在实际的OpenGL编程中,使用VBO对象通常是一个常见的做法,以提高渲染性能和效率。

04 EBO(Element Buffer Object)

EBO(Element Buffer Object)是OpenGL中用于存储索引数据的对象,用于指定绘制顶点的顺序。与VBO和VAO不同,EBO对象在配置完成后通常不需要解绑

因为存储索引对象,所以需要使用VBO存储顶点数据。

以下是一些原因解释为什么EBO不需要解绑:

  • 不会被意外修改: 一旦将索引数据存储在EBO中并配置完成后,通常不会再对EBO进行进一步的修改。索引数据指定了顶点绘制的顺序,不会随着渲染操作而改变。因此,解绑EBO并不是必需的。

  • 保持状态: EBO对象是在VAO中配置的一部分。一旦你绑定了VAO并配置了EBO,VAO将会记住EBO的绑定状态。所以,当你绑定VAO时,它会自动绑定关联的EBO,不需要显式解绑EBO。

  • 避免冗余操作: 解绑EBO并重新绑定它是一种冗余操作,可能会增加代码的复杂性和开销。由于EBO通常不需要在绘制过程中进行修改,保持其绑定状态可以避免额外的操作,提高渲染的效率。

虽然解绑EBO不是必须的,但如果你希望在绘制过程中切换到不同的EBO,你可以显式地将另一个EBO绑定到当前的VAO中。这将覆盖原有的EBO绑定状态,并在后续的绘制调用中使用新的索引数据。

总而言之,EBO不需要解绑的主要原因是它通常不会被修改,并且在VAO的绑定状态中保持有效。解绑EBO是一种冗余操作,而且会增加代码的复杂性。只有在需要切换到不同的EBO时,才需要显式地重新绑定新的EBO对象。

05 解绑VAO和VBO

在OpenGL中,解绑VAO(Vertex Array Object)和VBO(Vertex Buffer Object)是一个良好的实践,尽管并不是必需的。

解绑VAO和VBO的主要原因是为了避免对它们的误操作。一旦VAO或VBO被绑定到OpenGL上下文,后续的操作将会影响到当前绑定的对象。如果你不再需要对VAO或VBO进行进一步的修改,解绑它们可以防止意外的修改,确保代码的可靠性和一致性。

下面是解绑VAO和VBO的一些考虑:

  • 代码的清晰性: 解绑VAO和VBO可以使代码更加清晰和易读。它明确地指示了你已经完成了对VAO和VBO的配置,并且不再需要对它们进行修改。这有助于其他开发人员更好地理解你的意图和代码逻辑。

  • 避免误操作: 解绑VAO和VBO可以防止在不需要的情况下对它们进行误操作。如果VAO或VBO仍然处于绑定状态,后续的操作可能会不经意地修改它们的配置或数据。解绑可以防止这种情况的发生,确保你的VAO和VBO保持原样。

  • 可移植性: 某些OpenGL上下文或驱动程序可能对绑定的对象状态有特定的要求或限制。解绑VAO和VBO可以增加代码的可移植性,使其在不同的OpenGL实现中具有更好的兼容性。

请注意,解绑VAO和VBO并不会删除它们或释放任何资源。它们仍然存在于OpenGL上下文中,可以在需要时重新绑定和使用。解绑只是一种良好的编程习惯,有助于确保代码的正确性和可维护性。

综上所述,尽管解绑VAO和VBO不是必需的,但它是一种良好的实践,可以提高代码的可靠性和可读性,避免误操作,并增加代码的可移植性。

06 一般的创建流程

  • 1 生成VAO对象: 调用glGenVertexArrays函数生成一个或多个VAO对象,并将它们的标识符存储在变量中。

  • 2 绑定VAO对象: 调用glBindVertexArray函数将一个VAO对象绑定到OpenGL上下文中。

  • 3 生成缓冲对象: 调用glGenBuffers函数生成一个或多个缓冲对象,并将它们的标识符存储在变量中。

  • 4 绑定缓冲对象: 调用glBindBuffer函数将一个缓冲对象绑定到OpenGL上下文中。

  • 5 配置缓冲对象: 调用glBufferData函数或glBufferSubData函数配置缓冲对象,将数据传输到GPU中。

  • 6 配置顶点属性: 调用glVertexAttribPointer函数配置顶点属性指针,使得GPU可以正确解析缓冲对象中的数据。

  • 7 启用顶点属性: 调用glEnableVertexAttribArray函数启用顶点属性,使得GPU可以使用缓冲对象中的数据进行渲染。

  • 8 解绑VAO、VBO对象: 调用glBindBuffer(GL_ARRAY_BUFFER, 0)和glBindVertexArray(0)

  • 9 绘制物体:

    glUseProgram(shaderProgram);

    glBindVertexArray(VAO);

    someOpenGLFunctionThatDrawsOurTriangle();