【OpenGL学习】(二)OpenGL渲染简单图形

文章目录

【OpenGL学习】(二)OpenGL渲染简单图形

OpenGL渲染图形流程

cpp 复制代码
CPU(中央处理器)
  ↓
初始化窗口与OpenGL上下文
  ↓
编译并链接着色器程序 
  ↓
准备顶点数据(顶点位置、颜色、法线、纹理坐标等)
  ↓
创建并绑定:
  - VAO(顶点数组对象)← 用于记录 VBO/EBO 的绑定状态和顶点属性配置
    ↓
    创建并绑定:
      - VBO(顶点缓冲对象)← 存储所有顶点属性数据(位置信息、颜色、纹理坐标等)
      - EBO(索引缓冲对象,可选)← 存储索引数据,减少冗余顶点
    设置顶点属性指针(glVertexAttribPointer)← 告诉 OpenGL 如何解析 VBO 中的顶点数据
    启用顶点属性数组(glEnableVertexAttribArray)
    ↑(这些设置会被 VAO 记录下来)

调用:
  - glBufferData → 将顶点数据或索引数据从 CPU 传输到 GPU 显存中(显卡缓冲区)
  ↓
====================  GPU 开始接管渲染流程 ====================
顶点着色器(Vertex Shader)
  - 每个顶点执行一次
  - 坐标变换:模型矩阵 × 视图矩阵 × 投影矩阵
  - 传出数据供后续阶段使用(如颜色、纹理坐标)
  ↓
图元装配(Primitive Assembly)
  - 将一组顶点组装为图元(如三角形、线段)
  ↓
(可选)几何着色器(Geometry Shader)
  - 每个图元执行一次
  - 可动态生成新的顶点或图元
  ↓
光栅化(Rasterization)
  - 将图元转换为片元(像素候选)
  - 生成每个片元的屏幕位置
  ↓
片段着色器(Fragment Shader)
  - 每个片元执行一次
  - 计算颜色值(光照、纹理采样、颜色混合等)
  ↓
测试与混合阶段(由固定功能单元执行)
  - 深度测试、模板测试
  - α混合、遮挡判断
  ↓
帧缓冲(Framebuffer)
  - 最终图像写入帧缓冲 → 显示在屏幕

图片来源:https://geekdaxue.co/read/Learn-OpenGL-CN/01-Getting-Started-04-Hello-Triangle.md

顶点,图元和片元

顶点(Vertex)是图形的基本构建单位,表示图形的一个顶点,通常包括:

  • 位置坐标(Position):如三维空间中的 (x, y, z)
  • 颜色信息(Color)
  • 纹理坐标(UV)
  • 法向量(Normal)
  • 其他自定义属性(如切线、位移等)

图元(Primitive)是由多个顶点组成的几何形状单元,比如:

  • 点(GL_POINTS)→ 每个顶点单独成图元
  • 线段(GL_LINES)→ 每两个顶点组成一条线
  • 三角形(GL_TRIANGLES)→ 每三个顶点组成一个三角形
  • 更多复合图元(如 GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN)

片元(Fragment)是每个图元经过光栅化后生成的像素候选者,这些片元会传入片段着色器进行着色计算(颜色、纹理、光照等)。在经过深度测试、模板测试、混合等处理之后,部分片元会变成屏幕上的像素。

VAO,VBO ,EBO

VAO(顶点数组对象,Vertex Array Object)用于 存储顶点属性配置状态,如顶点属性指针、VBO绑定状态、EBO绑定状态等。每次绘制时只需绑定一次 VAO,而不用重复设置顶点属性。

VBO(顶点缓冲对象,Vertex Buffer Object)用于在 GPU 显存中存储顶点数据(如位置、颜色、法线、纹理坐标等),避免每次绘制时从 CPU 向 GPU 频繁传输数据,提升效率。

EBO(元素缓冲对象,Element Buffer Object)用于在 绘制图形时按照索引重用顶点数据。节省存储空间,避免重复顶点。

着色器

着色器(Shader)是运行在 GPU 上的小程序,用于控制图形渲染的每个阶段。它是实现可编程渲染管线的核心。

着色器的主要类型:

着色器类型 作用
顶点着色器 (Vertex Shader) 处理每个顶点的位置变换、法线、纹理坐标等
片段着色器 (Fragment Shader) 处理每个像素(片元)的颜色计算、纹理映射、光照等
几何着色器(可选) (Geometry Shader) 处理图元(点、线、三角形),可生成新图元
曲面细分控制/评估着色器(可选) 用于对几何细分
计算着色器(Compute Shader) 用于并行计算任务,不用于图形绘制

着色器使用的语言是GLSL(OpenGL Shading Language)。GLSL的语法类似于 C 语言,且可以运行于 GPU 上。我们需要在 C++ 中编写 GLSL 代码(字符串形式),然后使用 OpenGL API 编译和链接着色器。

示例:使用OpenGL渲染三角形

cpp 复制代码
#include <glad/glad.h>  // 加载 OpenGL 函数指针
#include <GLFW/glfw3.h> // GLFW 用于创建窗口和处理输入

#include <iostream>   

// 回调函数声明:当窗口大小发生改变时调用
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
// 处理输入的函数声明
void processInput(GLFWwindow* window);

// 设置窗口宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// 顶点着色器GLSL源码
const char* vertexShaderSource = "#version 330 core\n" // 使用 OpenGL 3.3 对应的 GLSL 版本(即 GLSL 3.30)
"layout (location = 0) in vec3 aPos;\n"  // 顶点位置属性,位置值为0
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"  // 设置顶点位置
"}\0";

// 片段着色器GLSL源码
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"  // 片段输出颜色
"void main()\n"
"{\n"
"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"  // 设置输出颜色为橙色
"}\n\0";

int main()
{
    // 初始化并配置 GLFW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // OpenGL 主版本号
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // OpenGL 次版本号
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // MacOS 需要加这句
#endif

    // 创建 GLFW 窗口
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate(); // 初始化失败,退出程序
        return -1;
    }
    glfwMakeContextCurrent(window); // 将窗口上下文设为当前线程上下文
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // 设置窗口大小改变时的回调

    // 初始化 GLAD,用于加载 OpenGL 函数
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // ------------------构建并编译着色器程序------------------
    // 顶点着色器
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER); // 创建着色器对象
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);   // 附加源码
    glCompileShader(vertexShader);                                // 编译着色器
    // 检查编译错误
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }

    // 片段着色器
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);// 创建一个片段着色器对象
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); // 将片段着色器源码附加到着色器对象上
    glCompileShader(fragmentShader);   // 编译片段着色器源码
    // 检查编译错误
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }

    // 着色器程序
    unsigned int shaderProgram = glCreateProgram();           // 创建程序对象
    glAttachShader(shaderProgram, vertexShader);              // 附加着色器
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);                             // 链接程序
    // 检查链接错误
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    }
    // 删除已编译的着色器对象,已链接到程序中不再需要
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    // --------------------------------------------------------


    // 设置顶点数据和缓冲,并配置顶点属性
    float vertices[] = {
        -0.5f, -0.5f, 0.0f, // 左下角
         0.5f, -0.5f, 0.0f, // 右下角
         0.0f,  0.5f, 0.0f  // 顶部
    };
    // 
    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO); // 创建顶点数组对象
    glGenBuffers(1, &VBO);      // 创建顶点缓冲对象

    glBindVertexArray(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, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0); // 启用顶点属性

 
      
    glBindBuffer(GL_ARRAY_BUFFER, 0); // 解绑 VBO,为了安全,非必须
    glBindVertexArray(0);            // 解绑 VAO

    // 可以取消注释以使用线框模式绘制:也就是不填充图形,只画出边框线
    // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    // 渲染循环
    while (!glfwWindowShouldClose(window))
    {
        // 处理输入
        processInput(window);

        // 清屏并设置背景颜色
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // 绘制三角形
        glUseProgram(shaderProgram); // 使用着色器程序
        glBindVertexArray(VAO);      // 绑定 VAO
        glDrawArrays(GL_TRIANGLES, 0, 3); // 从第0个顶点绘制3个顶点构成的三角形

        // 交换缓冲区并查询IO事件
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // 可选:释放所有资源
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteProgram(shaderProgram);

    // 释放 GLFW 资源
    glfwTerminate();
    return 0;
}

// 处理输入:如果按下 ESC 键,则关闭窗口
void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// 当窗口大小发生改变时,自动调整视口大小
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height); // 设置 OpenGL 视口大小
}

只画边框线:

参考:
https://learnopengl-cn.github.io/01 Getting started/04 Hello Triangle/

相关推荐
虾球xz13 分钟前
游戏引擎学习第280天:精简化的流式实体sim
数据库·c++·学习·游戏引擎
虾球xz1 小时前
游戏引擎学习第281天:在房间之间为摄像机添加动画效果
c++·人工智能·学习·游戏引擎
扶尔魔ocy1 小时前
【Linux C/C++开发】轻量级关系型数据库SQLite开发(包含性能测试代码)
linux·数据库·c++·sqlite
ptu小鹏1 小时前
list重点接口及模拟实现
数据结构·c++·list
__BMGT()2 小时前
C++ QT 打开图片
前端·c++·qt
顾子茵2 小时前
c++从入门到精通(五)--异常处理,命名空间,多继承与虚继承
开发语言·c++
YueiL4 小时前
基于RK3588的智慧农场系统开发|RS485总线|华为云IOT|node-red|MQTT
c++·物联网·华为云·rk3588·rs485
Dream it possible!4 小时前
LeetCode 热题 100_寻找重复数(100_287_中等_C++)(技巧)(暴力解法;哈希集合;二分查找)
c++·leetcode·哈希算法
丶Darling.5 小时前
Day119 | 灵神 | 二叉树 | 二叉树的最近共公共祖先
数据结构·c++·算法·二叉树