Skip to main content

GL-PipeLine

·467 words·3 mins
课程 OpenGL 计算机图形学

本文介绍用cpp和OpenGL绘制一个简单的三角形。

GLAD #

OpenGL是图形标准,而不是具体实现,它定义了一套接口规范,但是每个操作系统的实现都不一样,所以我们需要一个库来帮助我们加载OpenGL的函数指针,这个库就是GLAD。

GLAD能帮我们找到函数的具体实现,这样我们才能调用各种OpenGL的函数。

Alt text

创建窗口 #

创建窗口需要使用GLFW库,GLFW能够在不同操作系统上创建窗口,接收用户输入,处理用户输入,以及与系统交互。如果不用这个库, 就需要自己找操作系统的API,这样就会很麻烦。

我们有三个地方要用GLFW的库,分别是初始化,循环,和终止。由于整体框架完全一致,我们可以封装一下窗口的类,初始化就放在构造函数里,终止放在析构函数里。

循环部分比较特殊,需要接收OpenGL真正画图的部分,我们要提供一个接口,让外部的函数能传进来,这样就可以在循环里调用了。

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <functional>

class GLFWWindowFactory
{
public:
    GLFWWindowFactory(int width, int height, const char *title)
    {
        // Same window initialization logic here
        glfwInit();
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

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

        this->window = glfwCreateWindow(width, height, title, NULL, NULL);

        if (window == NULL)
        {
            std::cout << "Failed to create GLFW window" << std::endl;
            glfwTerminate();
            exit(-1);
        }

        glfwMakeContextCurrent(this->window);
        glfwSetFramebufferSizeCallback(this->window, framebuffer_size_callback);
        // glad: load all OpenGL function pointers
        // ---------------------------------------
        if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
        {
            std::cout << "Failed to initialize GLAD" << std::endl;
        }
    }

    void processInput()
    {
        if (glfwGetKey(this->window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
            glfwSetWindowShouldClose(this->window, true);
    }

    GLFWwindow *getWindow()
    {
        return this->window;
    }
    void run(std::function<void()> updateFunc)
    {
        while (!glfwWindowShouldClose(this->window))
        {
            // Input
            processInput();

            // Custom update function
            updateFunc();

            // Swap buffers and poll events
            glfwSwapBuffers(this->window);
            glfwPollEvents();
        }

        glfwTerminate();
    }
    static void framebuffer_size_callback(GLFWwindow *window, int width, int height)
    {
        glViewport(0, 0, width, height);
    }

private:
    GLFWwindow *window;
};

有了这个类,在主函数里我们就能很方便的创建窗口了。

#include "./utils/windowFactory.cpp"
//#include "models/Model.h"
int main()
{
    GLFWWindowFactory myWindow(800, 600, "LearnOpenGL");
    myWindow.run([&]()
                 {
                     // Custom model updates and rendering logic here.
                     // For example:
                     // model->update();
                     // renderer->draw(model);
                 });
    return 0;
}

画一个三角形 #

要画一个东西我们要定义它的顶点:

float vertices[] = {
    0.0f,  0.5f, 0.0f,  // 顶部顶点
   -0.5f, -0.5f, 0.0f,  // 左下角顶点
    0.5f, -0.5f, 0.0f   // 右下角顶点
};

然后初始化一个顶点缓冲对象(VBO)和一个顶点数组对象(VAO)来存储这些数据,并把它们传给OpenGL。

unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);

// 绑定和设置顶点缓冲
glBindVertexArray(VAO);

glBindBuffer(GL_ARRAY_BUFFER, 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);
glBindVertexArray(0);

然后我们就可以在循环里画出来了。

glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);

以上代码都能封装模型类里,Triangle.h

#pragma once

#include <glad/glad.h>

class TriangleModel
{
public:
    TriangleModel();
    ~TriangleModel();

    void draw();

private:
    unsigned int VAO;
    unsigned int VBO;
    unsigned int shaderProgram;

    void compileShaders();
    void setupBuffers();
};

Triangle.cpp:

#include "TriangleModel.h"
#include <iostream>

const int VERTEX_ATTR_POSITION = 0;
const int NUM_COMPONENTS_PER_VERTEX = 3;

TriangleModel::TriangleModel()
{
    compileShaders();
    setupBuffers();
}

TriangleModel::~TriangleModel()
{
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
}

void TriangleModel::draw()
{
    glUseProgram(shaderProgram);
    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 6);
}

void TriangleModel::compileShaders()
{
    const char *vertexShaderSource = "#version 330 core\n"
                                     "layout (location = 0) in vec3 aPos;\n"
                                     "void main()\n"
                                     "{\n"
                                     "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
                                     "}\0";
    const char *fragmentShaderSource = "#version 330 core\n"
                                       "out vec4 FragColor;\n"
                                       "void main()\n"
                                       "{\n"
                                       "   FragColor = vec4(0.0f, 0.5f, 0.2f, 1.0f);\n"
                                       "}\n\0";

    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
}

void TriangleModel::setupBuffers()
{
    float vertices[] = {
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f, 0.5f, 0.0f,

        1.0f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.25f, 0.0f, 0.0f

    };

    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(VERTEX_ATTR_POSITION, NUM_COMPONENTS_PER_VERTEX, GL_FLOAT, GL_FALSE, NUM_COMPONENTS_PER_VERTEX * sizeof(float), (void *)0);
    glEnableVertexAttribArray(VERTEX_ATTR_POSITION);
}

更新我们的主函数:

#include "./utils/windowFactory.cpp"
#include "models/Triangle.h"
int main()
{
    GLFWWindowFactory myWindow(800, 600, "LearnOpenGL");
    TriangleModel triangle;
    myWindow.run([&]()
                 {
                        triangle.draw();
                 });
    return 0;
}

Alt text

Related

vscode学opengl
·264 words·2 mins
课程 OpenGL vscode
BST Traversal
·133 words·1 min
刷题 二叉树
Attention Implementation
·118 words·1 min
Paper Reading Neuron Network attention mechanism