Qt 内使用原生OpenGL API + glad方案

这段时间自己想弄个小引擎玩玩,发现图形界面glfw功能还是太弱了点,加上想学点主流的GUI框架,于是上手了Qt,折腾了好久,终于是弄出了这个方案

先说明一点,其实Qt早就提供了包装好的OpenGL库,VBO,VAO,shader这些都是给你封装好的,

用法与原生的API差不了多少,但是我还是想多用用原生的OpenGL练习一下

目标:窗口GUI由Qt搭建,仅有OpenGLWidget桥接OpenGL的渲染输出,渲染不用到任何Qt封装好的OpenGL库,可以看做 Qt 对 learnopengl 中 glfw的平替

以下是步骤

mainwindows 主窗口没什么好说的

这是ui,很简单,一个mainwindow 里面嵌入一个OpenGLWidget,用来显示我们的渲染结果

首先创建oglwidget类

///oglwidget.hpp


#include "renderApplication.hpp"

#include <QOpenGLWidget>
#include <memory>



class oglwidget : public QOpenGLWidget
{
Q_OBJECT

public:
    explicit oglwidget(QWidget *parent = nullptr);
    ~oglwidget();

    void setRender(std::shared_ptr<zh::RenderApplication> ptr);


protected:
    void initializeGL();
    void resizeGL(int width, int height);
    void paintGL();


private:
    std::shared_ptr<zh::RenderApplication> render; //智能指针写法
    // 或者直接 Render render
};
#include "oglwidget.hpp"


oglwidget::oglwidget(QWidget *parent) :
        QOpenGLWidget(parent)
{

}

oglwidget::~oglwidget()
{

}

void oglwidget::initializeGL()
{
    render->init_base();
}

void oglwidget::paintGL()
{
    render->update_base();
}

void oglwidget::resizeGL(int width, int height) {

}

void oglwidget::setRender(std::shared_ptr<zh::RenderApplication> ptr) {
    render = ptr;
}

接下来把我们的类与ui上面的控件关联起来,右键OpenGLWidget 提升为

类和头文件就写我们刚刚创建的oglwidget类,然后提升就OK

这样控件就与我们的类关联在一起了

oglwidget继承于 QOpenGLWidget,我们需要重写三个函数

init 就是我们的初始化GPU资源那些,paintGL 就是渲染循环里面的那些

这里需要注意一点,渲染逻辑一定要用一个渲染类包装起来,Qt我们至始至终只用Widget控件部分,不涉及到任何的QOpenGL封装的api,其实就是所有glad有关的代码,不要写到这个oglwidget里,只调用我们渲染类封装好的方法。

render的内容开始画三角形

#include <glad/glad.h>

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(1.0f, 0.5f, 0.2f, 1.0f);\n"
                                   "}\n\0";

class Render{

bool init() 
    {

        //glad 初始化
        if(!gladLoadGL())
        {
            LOG_ERROR("glad init failed!");
            return false;
        }

        unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        // check for shader compile errors
        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;
        }
        // fragment shader
        unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);
        // check for shader compile errors
        glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
            std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
        }
        shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);
        // check for linking errors
        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);

        // set up vertex data (and buffer(s)) and configure vertex attributes
        // ------------------------------------------------------------------
        float vertices[] = {
                0.5f,  0.5f, 0.0f,  // top right
                0.5f, -0.5f, 0.0f,  // bottom right
                -0.5f, -0.5f, 0.0f,  // bottom left
                -0.5f,  0.5f, 0.0f   // top left
        };
        unsigned int indices[] = {  // note that we start from 0!
                0, 1, 3,  // first Triangle
                1, 2, 3   // second Triangle
        };
        unsigned int VBO, EBO;
        glGenVertexArrays(1, &VAO);
        glGenBuffers(1, &VBO);
        glGenBuffers(1, &EBO);
        // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
        glBindVertexArray(VAO);

        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);

        // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        // remember: do NOT unbind the EBO while a VAO is active as the bound element buffer object IS stored in the VAO; keep the EBO bound.
        //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

        // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
        // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
        glBindVertexArray(0);


        return true;
    }

    void update(double delta) 
    {
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        // draw our first triangle
        glUseProgram(shaderProgram);
        glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
        //glDrawArrays(GL_TRIANGLES, 0, 6);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

    }

private:
    unsigned int shaderProgram;
    unsigned int VAO;

};

oglwidget 里的render类 你可以用一个render实例,也可以用一个智能指针,在初始化的时候传入

最终结果

接下来可以在Render类,专注于你原生OpenGL的代码啦!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是使用 GLFW 和 GLAD 库绘制四边形的示例代码: ```c++ #include <glad/glad.h> #include <GLFW/glfw3.h> #include <iostream> void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } void processInput(GLFWwindow* window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(window, true); } } int main() { glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); GLFWwindow* window = glfwCreateWindow(800, 600, "Quad", nullptr, nullptr); if (window == nullptr) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; } glViewport(0, 0, 800, 600); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); while (!glfwWindowShouldClose(window)) { processInput(window); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); float vertices[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.5f, 0.5f, 0.0f, -0.5f, 0.5f, 0.0f }; unsigned int indices[] = { 0, 1, 3, 1, 2, 3 }; unsigned int VBO, VAO, EBO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); 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); glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glfwSwapBuffers(window); glfwPollEvents(); } glfwTerminate(); return 0; } ``` 在 `main()` 函数中,我们首先初始化 GLFW 库和 OpenGL 上下文。然后使用 GLAD 库初始化 OpenGL 函数指针。 在主循环中,我们首先处理输入事件,然后清空颜色缓冲区。接着定义四边形的顶点坐标和顶点索引,生成 VBO、VAO 和 EBO,绑定并填充数据。最后绘制四边形并交换缓冲区。 在 `framebuffer_size_callback()` 回调函数中,我们重新设置视口大小。 在 `processInput()` 函数中,我们检查 ESC 键是否被按下,如果是则设置窗口关闭标志,退出主循环。 这段代码使用了现代 OpenGL 的方式,使用了顶点缓冲对象、顶点数组对象和索引缓冲对象。同时也使用GLAD 库来加载 OpenGL 函数指针。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值