OpenGL 学习实录2: 基础绘制初试

OpenGL 学习实录2: 基础绘制初试

正文

完成前一篇OpenGL 学习实录1: 基于 MacOS + Clion 配置 OpenGL 运行环境的运行环境配置,第二篇记录关于第一次尝试绘制的步骤

0. 概念 & 步骤

  • OpenGL 与 Web 的 Canvas 相似,都是透过所谓的上下文的绘图模式(Canvas2D 的 RenderingContext,OpenGL 的全局空间等)
  • 数据对象
    • OpenGL 操作的对象为缓冲对象(Buffer Object),需要透过如 glGenBuffers 方法来创建
    • 缓冲对象分为顶点缓冲对象(Vertex Buffer Object = VBO)元素/索引缓冲对象(Element Buffer Object = EBO / Index Buffer Object = IBO)
    • 使用顶点数列对象(Vertex Array Object = VAO)来记录缓冲对象、索引对象的集合
  • 渲染对象
    • 顶点着色器(VertexShader)
    • 片段着色器(FragmentShader)
    • 着色器程序(ShaderProgram)

1. 实做记录

1.1 项目目录结构 & CmakeLists 配置

参照 OpenGL 学习实录1: 基于 MacOS + Clion 配置 OpenGL 运行环境 配置完成后的目录结构

1.2 程序结构

操作步骤伪代码

int main() {
    初始化 GLFW
    检查 OpenGL 版本
    创建 GLFW 窗口

    使用 glad 检查 OpenGL 接口绑定
    创建顶点着色器
    创建片段着色器
    创建着色器程序 & 与前两个着色器绑定
    绑定完成后可清理着色器
    
    创建顶点数据并写入缓冲区
    
    while(不应关闭窗口) {
        处理用户输入

        具体绘制流程
            清理屏幕
            绑定着色器程序
            绑定绘制缓冲对象
            绘制图形

        接受用户输入事件
        交换视频缓冲区(渲染上屏)
    }

    释放缓冲对象
    结束 GLFW 窗口
    return 0;
}

1.3 初始化 GLFW & 建立窗口

  • 程序头文件
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>

using namespace std;

int main() {
    // ...
}
  • 初始化 GLFW
int main() {
//    1. 初始化 glfw
    if (glfwInit() == GLFW_FALSE) {
        cout << "Fail to initialize GLFW" << endl;
        return -1;
    }

//    2. glfw 配置项(OpenGL 版本号、绘制模式)
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

//    3. 创建 glfw 窗口
    GLFWwindow *window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
    // 3.1 检查窗口是否创建成功
    if (window == NULL) {
        cout << "Failed to create GLFW window" << endl;
        glfwTerminate();
        return -1;
    }
    // 3.2 加入到当前上下文
    glfwMakeContextCurrent(window);
    // 3.3 窗口大小更新
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

// more
}
// 窗口大小更新处理函数
void framebuffer_size_callback(GLFWwindow *window, int width, int height) {
    // 重制可见视图
    glViewport(0, 0, width, height);
}

1.4 构建着色器程序

第二部分在于定制 OpenGL 开放的着色器程序,主要定制顶点着色器和片段着色器

  • 顶点着色器
#version 410 core
layout (location = 0) in vec3 aPos;
void main() {
    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
  • 片段着色器
#version 410 core
out vec4 color;
void main() {
    color = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

作为 C++ 字符串

// 顶点着色器 glsl 源码
const char *vertexShaderSource = "#version 410 core\n"
                                 "layout (location = 0) in vec3 aPos;\n"
                                 "void main() {\n"
                                 "    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
                                 "}\0";

// 片段着色器 glsl 源码
const char *fragmentShaderSource = "#version 410 core\n"
                                   "out vec4 color;\n"
                                   "void main() {\n"
                                   "    color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
                                   "}\0";
  • 在程序中创建 & 编译
int main() {
    // ...

//    4. 使用 glad 初始化 OpenGL 接口(检查)
    if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
        cout << "Failed to initialize GLAD" << endl;
        return -1;
    }

//    5. 顶点着色器
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    checkShaderCompile(vertexShader);

//    6. 片段着色器
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    checkShaderCompile(fragmentShader);

//    7. 着色器程序
    unsigned int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    checkProgramCompile(shaderProgram);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // ...
}

Link 成功之后就能够 Delete 释放着色器了

着色器、着色程序的编译结果检查函数如下

void checkShaderCompile(unsigned int shader) {
    //    检查着色器编译结果
    int success;
    char infoLog[512];
    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(shader, 512, NULL, infoLog);
        cout << "ERROR::SHADER::COMPILATION_FAILED\n" << infoLog << endl;
    }
}

void checkProgramCompile(unsigned int program) {
    //    检查着色程序编译结果
    int success;
    char infoLog[512];
    glGetProgramiv(program, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(program, 512, NULL, infoLog);
        cout << "ERROR::PROGRAM::COMPILATION_FAILED\n" << infoLog << endl;
    }
}

1.5 创建数据缓冲对象

输入设备存在各种图形表示结构,需要将其写入缓冲对象才能够交由 OpenGL 进行操作和控制

int main() {
    // ...

//    8. 顶点数据
    float vertices[] = {
            0.5f, 0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
            0.0f, 0.0f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            -0.5f, 0.5f, 0.0f,
    };

    // 顶点索引
    unsigned int indices[] = {
            0, 1, 2,
            2, 3, 4
    };

//    顶点数组对象 VAO、顶点缓冲对象 VBO、EBO
    unsigned int VAO, VBO, EBO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);
//    三角形顶点数组写入缓冲对象 VBO
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

//    设置顶点属性指针
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) 0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    // ...
}

1.6 绘制程序 & 退出时的资源清理

现在我们有了绑定好着色器的着色器程序,以及保存了顶点数据的缓冲对象,最后一部分就是进行图形的绘制和每帧的刷新实现了

int main() {
    // ...

    while (!glfwWindowShouldClose(window)) {
        processInput(window);

//    刷新屏幕
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

//    绑定着色器程序
        glUseProgram(shaderProgram);
//    基于缓冲对象绘制图形
        glBindVertexArray(VAO);
//        glDrawArrays(GL_TRIANGLES, 0, 3); // 简单绘制
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

//    获取用户操作 & 缓冲上屏
        glfwPollEvents();
        glfwSwapBuffers(window);
    }

// 清理缓冲对象 & 着色器程序资源
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    glDeleteProgram(shaderProgram);

    glfwTerminate();
    return 0;
}

其他资源

参考连接

TitleLink
OpenGL 学习实录1: 基于 MacOS + Clion 配置 OpenGL 运行环境https://blog.csdn.net/weixin_44691608/article/details/120806889
你好,三角形 - LearnOpenGL CNhttps://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/

完整代码示例

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值