从零开始:在Qt中使用OpenGL绘制指南

从零开始:在Qt中使用OpenGL绘制指南

本文只介绍基本的 QOpenGLWidget 和 QOpenGLFunctions 的使用,想要学习 OpenGL 的朋友,建议访问经典 OpenGL 学习网站:LearnOpenGL CN

本篇文章,我们将以绘制一个经典的三角形为例,讲一讲,怎么在 Qt 中使用 OpenGL 来进行 GPU 绘制。

前言

在高性能渲染场景中,CPU资源常被过度消耗,导致界面卡顿。而OpenGL作为业界标准的图形API,能通过GPU硬件加速显著降低CPU负载。本文将以绘制三角形为例,教你如何通过Qt的QOpenGLWidget和QOpenGLFunctions实现跨平台GPU渲染。

QOpenGLFunctions

OpenGL函数在不同平台(Windows/Linux/Mac)的实现存在差异。例如:

平台

函数加载方式

Windows

wglGetProcAddress

Linux

glXGetProcAddress

Qt通过QOpenGLFunctions封装了这些底层差异,开发者只需继承此类,即可用glClear() 等统一接口调用OpenGL函数,无需编写平台特定代码。通过这样,我们就可以用一套代码,在不同平台下使用 OpenGL 相。要使用这个类也很简单,让我们的类直接继承 QOpenGLFuntions 就好了。同时也可以配合 QOpenGLWidget 来使用,在 initializeGL 函数里,调用 initializeOpenGLFunctions 后,就可以直接使用 OpenGL 的函数。

Windows 下加载(wglGetProcAddress)

例如在 Windows 下,我们使用 wglGetProcAddress来动态加载这些函数(例如 glClear),下面是加载代码:

包含必要的头文件

#include

#include

#include // 提供 OpenGL 扩展声明

定义函数指针类型

// 示例:定义 glClear 的函数指针类型

typedef void (APIENTRY *PFNGLCLEARPROC)(GLbitfield);

PFNGLCLEARPROC glClear;

加载 OpenGL 函数

// 初始化 OpenGL 函数

void initOpenGLFunctions() {

// 1. 加载 OpenGL 1.1 函数(由 opengl32.dll 提供)

glClear = (PFNGLCLEARPROC)wglGetProcAddress("glClear");

// 2. 检查是否加载成功

if (!glClear) {

// 如果失败,可能是驱动不支持该函数

MessageBoxA(NULL, "Failed to load glClear", "Error", MB_OK);

exit(1);

}

// 3. 类似方式加载其他函数...

// glDrawArrays = (PFNGLDRAWARRAYSPROC)wglGetProcAddress("glDrawArrays");

// ...

}

使用加载的函数

glClear(GL_COLOR_BUFFER_BIT); // 现在可以正常调用

Linux 下加载(glXGetProcAddress )

而在 linux 下,加载的函数变成了:glXGetProcAddress ,对应的代码是:

包含必要的头文件

#include

#include // X11 的 OpenGL 扩展

#include

定义函数指针类型

// 示例:定义 glClear 的函数指针类型

typedef void (*PFNGLCLEARPROC)(GLbitfield);

PFNGLCLEARPROC glClear;

加载 OpenGL 函数

void initOpenGLFunctions() {

// 1. 加载 glClear

glClear = (PFNGLCLEARPROC)glXGetProcAddress((const GLubyte*)"glClear");

// 2. 检查是否加载成功

if (!glClear) {

fprintf(stderr, "Failed to load glClear\n");

exit(1);

}

// 3. 类似方式加载其他函数...

// glDrawArrays = (PFNGLDRAWARRAYSPROC)glXGetProcAddress((const GLubyte*)"glDrawArrays");

// ...

}

使用加载的函数

glClear(GL_COLOR_BUFFER_BIT); // 现在可以正常调用

QOpenGLWidget

QOpenGLWidget 是 Qt 提供的一个 widget 类,用于在 Qt 应用程序中嵌入 OpenGL 渲染内容。它继承自 QWidget,内部管理了一个 OpenGL 上下文(例如 windows 下调用 wglMakeCurrent / wglDoneCurrent)和帧缓冲区,并提供了与 Qt 窗口系统无缝集成的能力。详细内容可看:QOpenGLWidget Class

我们可以创建自己的窗口,并继承 QOpenGLWidget,然后重写下面三个函数,来处理一些 OpenGL 相关的工作。

initializeGL

初始化一些 OpenGL 相关的资源或者状态。这个函数在在第一次调用 resizeGL或者 paintGL之前被调用。

paintGL

渲染 OpenGL 的场景,类似于我们平常使用的 QWidget::paintEvent,在窗口需要更新时调用。

resizeGL

调整 OpenGL Viewport 的大小或者投影等,在窗口需要调整大小时调用。

完整代码

#pragma once

#include

#include

#include

#include

#include "FrameObserver.h"

class COpenGLRenderWidget : public QOpenGLWidget, protected QOpenGLFunctions

{

Q_OBJECT

public:

explicit COpenGLRenderWidget(QWidget *parent = nullptr);

~COpenGLRenderWidget() override;

private:

void InitShaders();

private:

void initializeGL() override;

void paintGL() override;

void resizeGL(int w, int h) override;

private:

QOpenGLShaderProgram m_shaderProgram;

QOpenGLBuffer m_vbo;

};

#include "OpenGLRenderWidget.h"

static const GLfloat coordinateBasic[] = {

// 顶点坐标,存储3个xyz坐标

// x y z

-0.5f, -0.5f, 0.0f,

0.5f, -0.5f, 0.0f,

0.0f, 0.5f, 0.0f,

};

constexpr auto VERTEX_SHADER_BASIC = R"(

attribute vec3 vertexIn;

varying vec2 textureOut;

void main(void)

{

gl_Position = vec4(vertexIn, 1.0);

}

)";

constexpr auto FRAGMENT_SHADER_BASIC = R"(

varying vec2 textureOut;

void main(void)

{

gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);

}

)";

COpenGLRenderWidget::COpenGLRenderWidget(QWidget *parent)

: QOpenGLWidget(parent)

{}

COpenGLRenderWidget::~COpenGLRenderWidget()

{}

void COpenGLRenderWidget::initializeGL()

{

initializeOpenGLFunctions();

glDisable(GL_DEPTH_TEST);

m_vbo.create();

m_vbo.bind();

m_vbo.allocate(coordinateBasic, sizeof(coordinateBasic));

InitShaders();

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

glClear(GL_COLOR_BUFFER_BIT);

}

void COpenGLRenderWidget::paintGL()

{

m_shaderProgram.bind();

glDrawArrays(GL_TRIANGLES, 0, 3);

m_shaderProgram.release();

}

void COpenGLRenderWidget::resizeGL(int w, int h)

{

glViewport(0, 0, w, h);

update();

}

void COpenGLRenderWidget::InitShaders()

{

QOpenGLShader vertexShader(QOpenGLShader::Vertex);

if (!vertexShader.compileSourceCode(VERTEX_SHADER_BASIC))

{

qDebug() << "Vertex shader compilation failed. Error: " << vertexShader.log();

return;

}

QOpenGLShader fragmentShader(QOpenGLShader::Fragment);

if (!fragmentShader.compileSourceCode(FRAGMENT_SHADER_BASIC))

{

qDebug() << "Fragment shader compilation failed. Error: " << fragmentShader.log();

return;

}

m_shaderProgram.addShader(&vertexShader);

m_shaderProgram.addShader(&fragmentShader);

m_shaderProgram.link();

m_shaderProgram.bind();

m_shaderProgram.setAttributeBuffer("vertexIn", GL_FLOAT, 0, 3, 3 * sizeof(float));

m_shaderProgram.enableAttributeArray("vertexIn");

}

相关推荐

抚顺医院排名
best365彩票

抚顺医院排名

📅 01-09 👁️ 4330
铁观音和龙井哪个贵
best365彩票

铁观音和龙井哪个贵

📅 09-28 👁️ 5832
哓陇名字寓意,哓陇名字的含义,哓陇取名的寓意和含义是什么