macOS下基于Qt/C++的OpenGL開發環境的搭建

系統配置

  • 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,minor0 。但是前文所述,默認情況下,得到的是major是 2,minor1 。為什么?而且這是我的機器運行的結果,別人的機器呢?
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 CoreOpenGL 3.2 CoreOpenGL 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);的原因了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/95707.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/95707.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/95707.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

當液態玻璃計劃遭遇反叛者:一場 iOS 26 界面的暗戰

引子 在硅谷的地下代碼俱樂部里&#xff0c;流傳著一個關于 “液態玻璃” 的傳說 —— 那是 Apple 秘密研發的界面改造計劃&#xff0c;如同電影《變臉》中那張能改變命運的面具&#xff0c;一旦啟用&#xff0c;所有 App 都將被迫換上流光溢彩的新面孔。 而今天&#xff0c;我…

探究Linux系統的SSL/TLS證書機制

一、SSL/TLS證書的基本概念 1.1 SSL/TLS協議簡介 SSL/TLS是一種加密協議&#xff0c;旨在為網絡通信提供機密性、完整性和身份驗證。它廣泛應用于HTTPS網站、電子郵件服務、VPN以及其他需要安全通信的場景。SSL&#xff08;安全套接字層&#xff09;是TLS&#xff08;傳輸層安全…

python和java爬蟲優劣對比

Python和Java作為爬蟲開發的兩大主流語言&#xff0c;核心差異源于語法特性、生態工具鏈、性能表現的不同&#xff0c;其優勢與劣勢需結合具體場景&#xff08;如開發效率、爬取規模、反爬復雜度&#xff09;判斷。以下從 優勢、劣勢、適用場景 三個維度展開對比&#xff0c;幫…

Unity 槍械紅點瞄準器計算

今天突然別人問我紅點瞄準器在鏡子上如何計算&#xff0c;之前的吃雞項目做過不記得&#xff0c;今天寫個小用例整理下。 主體思想記得是目標位置到眼睛穿過紅點瞄準器獲取當前點的位置就可以。應該是這樣吧&#xff0c;&#xff1a;&#xff09; 武器測試結構 首先整個結構&am…

題解 洛谷P13778 「o.OI R2」=+#-

文章目錄題解代碼居然沒有題解&#xff1f;我來寫一下我的抽象做法。 題解 手玩一下&#xff0c;隨便畫個他信心的折線圖&#xff0c;如下&#xff1a; 可以發現&#xff0c;如果我們知道終止節點&#xff0c;那么我們就可以知道中間有多少個上升長度。&#xff08;因為它只能…

RTSP流端口占用詳解:TCP模式與UDP模式的對比

在音視頻傳輸協議中&#xff0c;RTSP&#xff08;Real-Time Streaming Protocol&#xff0c;實時流傳輸協議&#xff09;被廣泛用于點播、直播、監控等場景。開發者在實際部署或調試時&#xff0c;常常會遇到一個問題&#xff1a;一路 RTSP 流到底占用多少個端口&#xff1f; 這…

websocket的key和accept分別是多少個字節

WebSocket的Sec-WebSocket-Key是24字節&#xff08;192位&#xff09;的Base64編碼字符串&#xff0c;解碼后為16字節&#xff08;128位&#xff09;的原始隨機數據&#xff1b;Sec-WebSocket-Accept是28字節&#xff08;224位&#xff09;的Base64編碼字符串&#xff0c;解碼后…

單片機開發----一個簡單的Boot

文章目錄一、設計思路**整體框架設計****各文件/模塊功能解析**1. main.c&#xff08;主程序入口&#xff0c;核心控制&#xff09;2. 隱含的核心模塊&#xff08;框架中未展示但必備&#xff09;**設計亮點**二、代碼bootloader.hbootloader.cflash.cmain.c一、設計思路 整體…

Day2p2 夏暮客的Python之路

day2p2 The Hard Way to learn Python 文章目錄day2p2 The Hard Way to learn Python前言一、提問和提示1.1 關于raw_input()1.2 關于input()二、參數、解包、變量2.1 解讀參數2.2 解讀解包2.3 解讀變量2.4 實例2.5 模塊和功能2.6 練習前言 author&#xff1a;SummerEnd date…

【C++設計模式】第二篇:策略模式(Strategy)--從基本介紹,內部原理、應用場景、使用方法,常見問題和解決方案進行深度解析

C設計模式系列文章目錄 【第一篇】C單例模式–懶漢與餓漢以及線程安全 【C設計模式】第二篇&#xff1a;策略模式&#xff08;Strategy&#xff09;--從基本介紹&#xff0c;內部原理、應用場景、使用方法&#xff0c;常見問題和解決方案進行深度解析一、策略模式的基本介紹1.…

四十歲編程:熱愛、沉淀與行業的真相-優雅草卓伊凡

四十歲編程&#xff1a;熱愛、沉淀與行業的真相-優雅草卓伊凡今日卓伊凡收到一個問題&#xff1a;「如何看待40歲還在擼代碼的程序員&#xff1f;」這讓我不禁思考&#xff1a;從何時起&#xff0c;年齡成了程序員職業中的敏感詞&#xff1f;在互聯網的某些角落&#xff0c;彌漫…

pycharm解釋器使用anaconda建立的虛擬環境里面的python,無需系統里面安裝python。

Anaconda建立的虛擬環境可以在虛擬環境里設置任何的python版本&#xff0c;pycharm解釋器使用anaconda建立的虛擬環境里面的python&#xff0c;比如anaconda建立的虛擬環境1、虛擬環境2&#xff0c;pycharm解釋器使用anaconda建立虛擬環境1也可以使用虛擬環境2&#xff0c;根本…

機器學習:后篇

目錄 一、KNN算法-分類 樣本距離 KNN算法原理 缺點 API 二、模型選擇與調優 交叉驗證 保留交叉驗證(HoldOut) k-折交叉驗證(K-fold) 分層k-折交叉驗證(Stratified k-fold) 其他交叉驗證 三、樸素貝葉斯-分類 理論介紹 拉普拉斯平滑系數 API 四、決策樹-分類 理論…

C++17無鎖編程實戰

在多線程編程里&#xff0c;“鎖” 這東西就像把雙刃劍 —— 用好了能保數據安全&#xff0c;用不好就麻煩了&#xff1a;大粒度的鎖把并發度壓得死死的&#xff0c;稍不注意加錯鎖還可能搞出死鎖&#xff0c;程序直接 “僵住”。 但如果能擺脫鎖&#xff0c;搞出支持安全并發…

SVT-AV1 svt_aom_motion_estimation_kernel 函數分析

void *svt_aom_motion_estimation_kernel(void *input_ptr) // 運動估計內核主函數&#xff0c;接收線程輸入參數{// 從輸入參數中獲取線程上下文指針EbThreadContext * thread_ctx (EbThreadContext *)input_ptr;// 從線程上下文中獲取運動估計上下文指針MotionEstimationCon…

關于NET Core jwt Bearer Token 驗證的大坑,浪費3個小時,給各位兄弟搭個橋。

net core 使用jwt Bearer Token 認證獲取接口訪問權限&#xff0c;前期一陣操作沒任何問題&#xff0c;等認證接口寫的好了&#xff0c;通過PostMan測試的時候&#xff0c;總是報一個 IDX14102: Unable to decode the header eyJhbGciOiJIUzI1NiIsInR5cCI6 &#xff0c;錯誤&a…

系統架構設計師備考第14天——業務處理系統(TPS)

一、TPS的核心概念與定位 1. 定義與演進 定義&#xff1a;TPS&#xff08;Transaction Processing System&#xff09;又稱電子數據處理系統&#xff08;EDPS&#xff09;&#xff0c;是處理企業日常事務的信息系統&#xff0c;如財務、庫存、銷售等局部業務管理。歷史地位&…

目標檢測系列-Yolov5下載及運行

由于項目需要&#xff0c;最近一直在看目標檢測相關的資料&#xff0c;不過紙上得來終覺淺&#xff0c;絕知此事要躬行啊。從今日起&#xff0c;將學習的過程記錄一下&#xff0c;作為以后用來復習的材料吧。 我想最快的學習便是直接動手做項目&#xff0c;因此今天就將yolov5模…

Linux內核進程管理子系統有什么第四十二回 —— 進程主結構詳解(38)

接前一篇文章&#xff1a;Linux內核進程管理子系統有什么第四十一回 —— 進程主結構詳解&#xff08;37&#xff09; 本文內容參考&#xff1a; Linux內核進程管理專題報告_linux rseq-CSDN博客 《趣談Linux操作系統 核心原理篇&#xff1a;第三部分 進程管理》—— 劉超 《…

基于飛算JavaAI的學生成績綜合統計分析系統

第一章&#xff1a;項目概述與背景 1.1 項目背景與意義 在教育信息化飛速發展的今天&#xff0c;學生成績管理已成為學校教學管理的核心環節。傳統的學生成績管理多依賴于手工操作或基礎的信息管理系統&#xff0c;存在數據處理效率低、統計分析功能薄弱、數據可視化缺失等問題…