学习OpenGL——第三天

32 阅读4分钟

图形渲染管线详解

3D坐标到2D坐标的转换过程由 OpenGL 的图形渲染管线(Graphics Pipeline)负责管理。整个流程可以分为两个主要部分:

  1. 3D 坐标转换为 2D 坐标
  2. 2D 坐标转为实际的有颜色的像素

管线就像一条输送带,原始图形数据在其中经过多步处理,最终呈现在屏幕上。


image.png

图形渲染管线流程

  1. 顶点数据输入
    • 以数组形式传递 3 个 3D 坐标,表示一个三角形。这个数组称为顶点数据(Vertex Data),每个顶点包含 3D 坐标等属性。
  1. 顶点着色器(Vertex Shader)
    • 以单个顶点为输入,主要作用是将 3D 坐标转换为另一种 3D 坐标,并能对顶点属性进行基本处理。
  1. 几何着色器(Geometry Shader)(可选)
    • 以一组顶点为输入(形成图元),可以生成新顶点或新的图元。例如,从一个三角形生成另一个三角形。
  1. 图元装配(Primitive Assembly)
    • 将顶点着色器或几何着色器输出的顶点组装成具体的图元(如三角形、线段等)。
  1. 光栅化阶段(Rasterization Stage)
    • 将图元映射为屏幕上的像素,生成片段(Fragment),供片段着色器使用。
    • 在此之前会进行裁切(Clipping),丢弃超出视图范围的像素以提升效率。
  1. 片段着色器(Fragment Shader)
    • 计算每个像素的最终颜色,是实现高级渲染效果(如光照、阴影等)的关键阶段。
  1. Alpha测试与混合(Blending)阶段
    • 检查像素的深度和模板值,决定是否丢弃像素,并根据 alpha 值进行透明度混合,实现物体的前后遮挡和透明效果。

VBO(Vertex Buffer Object)- 顶点缓冲对象

  • 定义:VBO 是存储顶点数据的 GPU 内存缓冲区,包括位置、颜色、纹理坐标、法线等。
  • 作用:作为 CPU 与 GPU 之间的数据桥梁,通过批量传输机制高效上传顶点数据,避免每帧重复传输,提升渲染效率。
  • 优势:数据持久化,上传后可多次复用,特别适合静态或半静态几何体。

VAO(Vertex Array Object)- 顶点数组对象

  • 定义:VAO 负责保存和管理所有顶点属性的配置,包括如何解释和使用 VBO 数据。
  • 作用:记录顶点属性指针、数据格式、偏移量、步长等参数。
  • 优势:简化状态管理,一次配置,多次绑定,便于频繁切换不同渲染状态,代码更清晰高效。

VBO:存储实际的顶点数据(位置、颜色、纹理、法线等)。

VAO:存储顶点属性的配置信息(数据格式、偏移量、步长等)。


EBO(Element Buffer Object)- 元素缓冲对象

  • 定义:EBO 专门用于存储顶点索引数据,定义顶点如何连接成图元(如三角形、线段等)。
  • 作用:通过索引机制,多个图元可复用同一顶点,避免重复存储,节省内存。
  • 优势:优化数据结构,提高内存和带宽利用率,适合复杂几何体和网格结构,是性能优化的重要工具。

顶点输入与坐标变换

  • 顶点着色器处理后的坐标为标准化设备坐标(NDC),其 x、y、z 范围均为 -1.0 到 1.0。超出范围的坐标会被裁剪,不显示在屏幕上。
  • 通过 glViewport 进行视口变换(Viewport Transform),NDC 会被映射到屏幕空间坐标,再作为片段输入到片段着色器。

OpenGL 关键对象的创建与配置

创建和配置 VBO

// 生成 VBO 并绑定 
unsigned int VBO; 
glGenBuffers(1, &VBO); 
glBindBuffer(GL_ARRAY_BUFFER, VBO); 

// 上传顶点数据到 GPU 
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

创建和配置 EBO

// 生成 EBO 并绑定 
unsigned int EBO; glGenBuffers(1, &EBO); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); 

// 上传索引数据到 GPU 
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

创建和配置 VAO

// 生成 VAO 并绑定 
unsigned int VAO; 
glGenVertexArrays(1, &VAO); 
glBindVertexArray(VAO); 

// 绑定 VBO 和 EBO 到 VAO 
glBindBuffer(GL_ARRAY_BUFFER, VBO); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); 

// 配置位置属性 (location = 0) 
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0); 
glEnableVertexAttribArray(0); 

// 配置颜色属性 (location = 1) 
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, color)); 
glEnableVertexAttribArray(1); 

// 解绑 VAO(保持配置状态) 
glBindVertexArray(0);

渲染循环中的使用示例

while (!glfwWindowShouldClose(window)) { 
    // 清屏 
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
    
    // 使用着色器程序 
    glUseProgram(shaderProgram); 
    
    // 绑定 VAO(恢复顶点属性配置) 
    glBindVertexArray(VAO); 
    
    // 绘制立方体(使用 EBO 索引) 
    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0); 
    
    // 解绑 
    VAO glBindVertexArray(0); 
    
    // 交换缓冲区和处理事件 
    glfwSwapBuffers(window); 
    glfwPollEvents(); 
}

渲染结束后的资源清理

// 渲染完成后清理资源 
glDeleteVertexArrays(1, &VAO); 
glDeleteBuffers(1, &VBO); 
glDeleteBuffers(1, &EBO);