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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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;存在数据处理效率低、统计分析功能薄弱、数据可视化缺失等问题…