在Qt中使用OpenGL繪制三角形指南
本文只介紹基本的 QOpenGLWidget 和 QOpenGLFunctions 的使用,想要學(xué)習(xí) OpenGL 的朋友,建議訪問經(jīng)典 OpenGL 學(xué)習(xí)網(wǎng)站:LearnOpenGL CN
本篇文章,我們將以繪制一個經(jīng)典的三角形為例,講一講,怎么在 Qt 中使用 OpenGL 來進(jìn)行 GPU 繪制。
前言
在高性能渲染場景中,CPU資源常被過度消耗,導(dǎo)致界面卡頓。而OpenGL作為業(yè)界標(biāo)準(zhǔn)的圖形API,能通過GPU硬件加速顯著降低CPU負(fù)載。本文將以繪制三角形為例,教你如何通過Qt的QOpenGLWidget和QOpenGLFunctions實現(xiàn)跨平臺GPU渲染。
QOpenGLFunctions
OpenGL函數(shù)在不同平臺(Windows/Linux/Mac)的實現(xiàn)存在差異。例如:
平臺 | 函數(shù)加載方式 |
---|---|
Windows | wglGetProcAddress |
Linux | glXGetProcAddress |
Qt通過QOpenGLFunctions
封裝了這些底層差異,開發(fā)者只需繼承此類,即可用glClear()
等統(tǒng)一接口調(diào)用OpenGL函數(shù),無需編寫平臺特定代碼。通過這樣,我們就可以用一套代碼,在不同平臺下使用 OpenGL 相。要使用這個類也很簡單,讓我們的類直接繼承 QOpenGLFuntions
就好了。同時也可以配合 QOpenGLWidget
來使用,在 initializeGL
函數(shù)里,調(diào)用 initializeOpenGLFunctions
后,就可以直接使用 OpenGL 的函數(shù)。
Windows 下加載(wglGetProcAddress)
例如在 Windows 下,我們使用 wglGetProcAddress
來動態(tài)加載這些函數(shù)(例如 glClear
),下面是加載代碼:
包含必要的頭文件
#include <windows.h> #include <GL/gl.h> #include <GL/glext.h> // 提供 OpenGL 擴(kuò)展聲明
定義函數(shù)指針類型
// 示例:定義 glClear 的函數(shù)指針類型 typedef void (APIENTRY *PFNGLCLEARPROC)(GLbitfield); PFNGLCLEARPROC glClear;
加載 OpenGL 函數(shù)
// 初始化 OpenGL 函數(shù) void initOpenGLFunctions() { // 1. 加載 OpenGL 1.1 函數(shù)(由 opengl32.dll 提供) glClear = (PFNGLCLEARPROC)wglGetProcAddress("glClear"); // 2. 檢查是否加載成功 if (!glClear) { // 如果失敗,可能是驅(qū)動不支持該函數(shù) MessageBoxA(NULL, "Failed to load glClear", "Error", MB_OK); exit(1); } // 3. 類似方式加載其他函數(shù)... // glDrawArrays = (PFNGLDRAWARRAYSPROC)wglGetProcAddress("glDrawArrays"); // ... }
使用加載的函數(shù)
glClear(GL_COLOR_BUFFER_BIT); // 現(xiàn)在可以正常調(diào)用
Linux 下加載(glXGetProcAddress )
而在 linux 下,加載的函數(shù)變成了:glXGetProcAddress
,對應(yīng)的代碼是:
包含必要的頭文件
#include <GL/gl.h> #include <GL/glx.h> // X11 的 OpenGL 擴(kuò)展 #include <GL/glext.h>
定義函數(shù)指針類型
// 示例:定義 glClear 的函數(shù)指針類型 typedef void (*PFNGLCLEARPROC)(GLbitfield); PFNGLCLEARPROC glClear;
加載 OpenGL 函數(shù)
void initOpenGLFunctions() { // 1. 加載 glClear glClear = (PFNGLCLEARPROC)glXGetProcAddress((const GLubyte*)"glClear"); // 2. 檢查是否加載成功 if (!glClear) { fprintf(stderr, "Failed to load glClear\n"); exit(1); } // 3. 類似方式加載其他函數(shù)... // glDrawArrays = (PFNGLDRAWARRAYSPROC)glXGetProcAddress((const GLubyte*)"glDrawArrays"); // ... }
使用加載的函數(shù)
glClear(GL_COLOR_BUFFER_BIT); // 現(xiàn)在可以正常調(diào)用
QOpenGLWidget
QOpenGLWidget
是 Qt 提供的一個 widget 類,用于在 Qt 應(yīng)用程序中嵌入 OpenGL 渲染內(nèi)容。它繼承自 QWidget
,內(nèi)部管理了一個 OpenGL 上下文(例如 windows 下調(diào)用 wglMakeCurrent
/ wglDoneCurrent
)和幀緩沖區(qū),并提供了與 Qt 窗口系統(tǒng)無縫集成的能力。詳細(xì)內(nèi)容可看:QOpenGLWidget Class
我們可以創(chuàng)建自己的窗口,并繼承 QOpenGLWidget,然后重寫下面三個函數(shù),來處理一些 OpenGL 相關(guān)的工作。
initializeGL
初始化一些 OpenGL 相關(guān)的資源或者狀態(tài)。這個函數(shù)在在第一次調(diào)用 resizeGL
或者 paintGL
之前被調(diào)用。
paintGL
渲染 OpenGL 的場景,類似于我們平常使用的 QWidget::paintEvent
,在窗口需要更新時調(diào)用。
resizeGL
調(diào)整 OpenGL Viewport 的大小或者投影等,在窗口需要調(diào)整大小時調(diào)用。
完整代碼
#pragma once #include <QOpenGLBuffer> #include <QOpenGLWidget> #include <QOpenGLShaderProgram> #include <QOpenGLFunctions> #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[] = { // 頂點(diǎn)坐標(biāo),存儲3個xyz坐標(biāo) // 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"); }
到此這篇關(guān)于在Qt中使用OpenGL繪制指南的文章就介紹到這了,更多相關(guān)Qt使用OpenGL繪制指南內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳細(xì)分析Android中實現(xiàn)Zygote的源碼
這篇文章主要介紹了詳細(xì)分析Android中實現(xiàn)Zygote的源碼,包括底層的C/C++代碼以及Java代碼部分入口,需要的朋友可以參考下2015-07-07