系統配置
- MacBook Pro 2015 Intel macOS 12
- Xcode 14
Qt開發環境搭建
Qt Creator的下載與安裝
在Qt官網的下載頁面上下載,即Download Qt Online Installer for macOS。下載完成就得到一個文件名類似于qt-online-installer-macOS-x64-x.y.z.dmg的安裝包。
下一步
選中自定義安裝,下一步
點擊右上角顯示下拉框,并選中Archive
按上方圖示選中對應組件,下一步
安裝。這里需要花費較長的一段時間。
新建Qt Widget項目
打開已經安裝完成的Qt Creator IDE
點擊右上角Create Project…
選中Application(Qt)–>Qt Widgets Application
按上方圖示,Base class選擇QWidget,不選中Generate form
持續下一步
點擊左下角綠色三角形,即運行按鈕
生成了一個空白窗體。好,現在關閉這個空白窗體。至此,macOS下基于Qt/c++的開發環境搭建完成。
OpenGL開發環境搭建
基本配置
打開并編輯first_project.pro,添加如下代碼:
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
greaterThan(QT_MAJOR_VERSION, 5): QT += opengl openglwidgets
保存。
打開并編輯widget.h,變更代碼為如下所示:
#ifndef WIDGET_H
#define WIDGET_H#include <QOpenGLWidget>
#include <QWidget>class Widget : public QOpenGLWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();
};
#endif // WIDGET_H
保存。
打開并編輯widget.cpp,變更代碼為如下所示:
#include "widget.h"Widget::Widget(QWidget *parent): QOpenGLWidget(parent)
{}Widget::~Widget() {}
保存。運行一下。
生成了一個空白窗體,效果比上一次的空白窗體更黑,因為現在它是一個OpenGL的運行環境了。好,現在關閉這個空白窗體。
OpenGL版本選擇
根據使用 OpenCL 和 OpenGL 圖形處理程序的 Mac 電腦這篇Apple官方文章可知,我的MacBook Pro支持OpenGL 4.1。既然如此,那么我就選擇OpenGL 4.1作為我的OpenGL學習版本。具體如何操作呢?
回顧一下剛才那個更黑的空白窗體,它已經是一個OpenGL的運行環境了。既然如此,這個OpenGL運行環境運行的是哪一個OpenGL版本呢?是OpenGL 4.1嗎?不知道。怎么辦呢?
打開并編輯main.cpp,變更代碼為如下所示:
#include "widget.h"#include <QApplication>
#include <QOpenGLContext>
#include <QSurfaceFormat>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();QOpenGLContext *cxt = w.context();QSurfaceFormat fmt{ cxt->format() };qDebug("%d.%d", fmt.majorVersion(), fmt.minorVersion());return a.exec();
}
保存。運行一下。還是剛才那個更黑的空白窗體。好,現在關閉這個空白窗體。
點擊一下Qt Creator IDE最下方的Application Output
現在看來,當前使用的是OpenGL 2.1版本。
現在嘗鮮,運行第1條OpenGL 2.1的命令,即const GLubyte *glGetString( GLenum name);
。
打開并編輯widget.h,變更代碼為如下所示:
#ifndef WIDGET_H
#define WIDGET_H#include <QOpenGLFunctions_2_1>
#include <QOpenGLWidget>
#include <QWidget>class Widget : public QOpenGLWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();protected:virtual void initializeGL() override;virtual void paintGL() override;private:QOpenGLFunctions_2_1 *glf;
};
#endif // WIDGET_H
保存。
打開并編輯widget.cpp,變更代碼為如下所示:
#include "widget.h"#include <QOpenGLContext>
#include <QOpenGLVersionFunctionsFactory>Widget::Widget(QWidget *parent): QOpenGLWidget(parent)
{}Widget::~Widget() {}void Widget::initializeGL()
{QOpenGLContext *cxt = context();glf = QOpenGLVersionFunctionsFactory::get<QOpenGLFunctions_2_1>(cxt);
}void Widget::paintGL()
{const GLubyte *version = glf->glGetString(GL_VERSION);QString versionStr;for (size_t i = 0; version[i] != 0; ++i){versionStr.append(QChar(version[i]));}qDebug() << versionStr;
}
保存。運行一下。
好,現在關閉這個空白窗體。
如何將當前的OpenGL運行環境從OpenGL 2.1切換到OpenGL 4.1?
打開并編輯main.cpp,變更代碼為如下所示:
#include "widget.h"#include <QApplication>
#include <QOpenGLContext>
#include <QSurfaceFormat>int main(int argc, char *argv[])
{QSurfaceFormat default_fmt{ QSurfaceFormat::defaultFormat() };default_fmt.setVersion(4, 1);QSurfaceFormat::setDefaultFormat(default_fmt);QApplication a(argc, argv);Widget w;w.show();QOpenGLContext *cxt = w.context();QSurfaceFormat fmt{ cxt->format() };qDebug("%d.%d", fmt.majorVersion(), fmt.minorVersion());return a.exec();
}
保存。運行一下。好,現在關閉這個空白窗體。發現效果和上一張圖片完全一樣。為什么沒有做到將當前的OpenGL運行環境從OpenGL 2.1切換到OpenGL 4.1?哦!原來widget.cpp文件中的那個工廠得到的就是OpenGL 2.1的函數對象,代碼如下所示:
glf = QOpenGLVersionFunctionsFactory::get<QOpenGLFunctions_2_1>(cxt);
打開并編輯widget.h,變更代碼為如下所示:
#ifndef WIDGET_H
#define WIDGET_H#include <QOpenGLFunctions_4_1_Core>
#include <QOpenGLWidget>
#include <QWidget>class Widget : public QOpenGLWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();protected:virtual void initializeGL() override;virtual void paintGL() override;private:QOpenGLFunctions_4_1_Core *glf;
};
#endif // WIDGET_H
保存。
打開并編輯widget.cpp,變更代碼為如下所示:
#include "widget.h"#include <QOpenGLContext>
#include <QOpenGLVersionFunctionsFactory>Widget::Widget(QWidget *parent): QOpenGLWidget(parent)
{}Widget::~Widget() {}void Widget::initializeGL()
{QOpenGLContext *cxt = context();glf = QOpenGLVersionFunctionsFactory::get<QOpenGLFunctions_4_1_Core>(cxt);
}void Widget::paintGL()
{const GLubyte *version = glf->glGetString(GL_VERSION);QString versionStr;for (size_t i = 0; version[i] != 0; ++i){versionStr.append(QChar(version[i]));}qDebug() << versionStr;
}
保存。運行一下。
程序崩潰了!怎么辦?調試一下。
圖中右下角指出:函數對象glf
是空指針。說明在當前這個OpenGL環境上下文下沒有正常地拿到函數對象。怎么辦?
從OpenGL 2.1切換到OpenGL 4.1相關的所有代碼如下所示:
widget.h
QOpenGLFunctions_4_1_Core *glf;
widget.cpp
glf = QOpenGLVersionFunctionsFactory::get<QOpenGLFunctions_4_1_Core>(cxt);
main.cpp
QSurfaceFormat default_fmt{ QSurfaceFormat::defaultFormat() };
default_fmt.setVersion(4, 1);
QSurfaceFormat::setDefaultFormat(default_fmt);
我們先來查看一下已經下載到本地的Qt QSurfaceFormat的源代碼。
qsurfaceformat.cpp
explicit QSurfaceFormatPrivate(QSurfaceFormat::FormatOptions _opts = { })// 已省略, profile(QSurfaceFormat::NoProfile), major(2), minor(0)// 已省略 {}
qsurfaceformat.h
// 已省略
enum OpenGLContextProfile {NoProfile,CoreProfile,CompatibilityProfile};
// 已省略
是不是端倪已出?打開并編輯main.cpp,變更代碼為如下所示:
// 已省略
QSurfaceFormat default_fmt{ QSurfaceFormat::defaultFormat() };
default_fmt.setVersion(4, 1);
default_fmt.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
QSurfaceFormat::setDefaultFormat(default_fmt);
// 已省略
保存。運行一下。
好,現在關閉這個空白窗體。至此,macOS下基于Qt/c++的OpenGL 4.1開發環境搭建完成。
細枝末節
細心的話會發現一個現象:
qsurfaceformat.cpp源代碼如下所示:
explicit QSurfaceFormatPrivate(QSurfaceFormat::FormatOptions _opts = { })// 已省略, profile(QSurfaceFormat::NoProfile), major(2), minor(0)// 已省略 {}
其中,major
是 2,minor
是 0 。但是前文所述,默認情況下,得到的是major
是 2,minor
是 1 。為什么?而且這是我的機器運行的結果,別人的機器呢?
Qt是一個支持跨平臺的大型c++類庫。那么,它是如何跨進macOS的呢?以OpenGL為例:在Mac平臺上,通過objective-c編寫基于OpenGL的應用,相關的API會涉及到NSOpenGLContext這個類,即OpenGL運行環境上下文,而Qt就可以通過插件的形式調用到此API 。
我們再來查看一下已經下載到本地的qcocoaglcontext.mm的源代碼。
// 已省略
m_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:m_shareContext];
// 已省略
NSOpenGLPixelFormat *QCocoaGLContext::pixelFormatForSurfaceFormat(const QSurfaceFormat &format)
{QVector<NSOpenGLPixelFormatAttribute> attrs;attrs << NSOpenGLPFAOpenGLProfile;if (format.profile() == QSurfaceFormat::CoreProfile) {if (format.version() >= qMakePair(4, 1))attrs << NSOpenGLProfileVersion4_1Core;else if (format.version() >= qMakePair(3, 2))attrs << NSOpenGLProfileVersion3_2Core;elseattrs << NSOpenGLProfileVersionLegacy;} else {attrs << NSOpenGLProfileVersionLegacy;}// 已省略
根據上方代碼可知,對于Qt來說,原來所有Mac系的OpenGL都支持OpenGL 4.1 Core、OpenGL 3.2 Core、OpenGL Legacy,根據前文描述的運行結果可知,此處的OpenGL Legacy應該就是指OpenGL 2.1了。不過還是不要忘記前文所示的使用 OpenCL 和 OpenGL 圖形處理程序的 Mac 電腦這篇Apple官方文章。
另外一個問題是為什么在main.cpp中,以下部分代碼:
QSurfaceFormat default_fmt{ QSurfaceFormat::defaultFormat() };
default_fmt.setVersion(4, 1);
default_fmt.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
QSurfaceFormat::setDefaultFormat(default_fmt);
出現在QApplication a(argc, argv);
之前?因為在它之后會出現問題。
我們再來查看一下已經下載到本地的Qt QSurfaceFormat的源代碼。
// 已省略
void QSurfaceFormat::setDefaultFormat(const QSurfaceFormat &format)
{
#ifndef QT_NO_OPENGLif (qApp) {QOpenGLContext *globalContext = QOpenGLContext::globalShareContext();if (globalContext && globalContext->isValid()) {qWarning("Warning: Setting a new default format with a different version or profile ""after the global shared context is created may cause issues with context ""sharing.");}}
#endif*qt_default_surface_format() = format;
}
// 已省略
這樣看似乎還是不夠明顯。我們再來查看一下已經下載到本地的Qt qguiapplication.h的源代碼,如下所示:
// 已省略
#if defined(qApp)
#undef qApp
#endif
#define qApp (static_cast<QGuiApplication *>(QCoreApplication::instance()))
// 已省略
這樣就解釋了在QApplication a(argc, argv);
創建應用對象之前調用QSurfaceFormat::setDefaultFormat(default_fmt);
的原因了。