Qt移动应用开发(八):实现跨平台的QML和OpenGL混合渲染

Qt移动应用开发(八):实现跨平台的QML和OpenGL混合渲染

 

         上一篇文章讲到了利用C++这个桥梁,我们实现了QML和Java的交互。Qt 5大力推崇的QML/JS开发,让轻量、高速开发的QML/JS打头阵,让重量的C++撑腰,差点儿什么技术都可以实现。接下来的这篇文章讲的是我们使用QML。借助Qt库和OpenGL。实现了使用着色器定义OpenGL的渲染方式,为大家呈现混合渲染的效果。

原创文章,反对未声明的引用。

原博客地址:http://blog.csdn.net/gamesdev/article/details/38024327

         本文难度偏大。适合有经验的Qt开发同行学习交流。

         演示程序下载地址:这里

         源码下载地址:这里

         演示程序的截图例如以下(Android):

         首先我们来看简单的QML代码。本例非常easy。仅仅有一个界面。没有不论什么界面的跳转。我们在前面显示一个矩形,上面写了”您好世界!

”的文字。后面显示的是一个旋转的矩形。依照规定。先显示的内容在最底层显示。于是我们将Cube放在前面,Rectangle放在了后面。

import QtQuick 2.2
import QtQuick.Window 2.2
import OpenGLCube 1.0Window
{id: rootwidth: Qt.platform.os === "android"? Screen.width: 320height: Qt.platform.os === "android"? Screen.height: 480visible: trueCube{id: cubeanchors.fill: parentParallelAnimation{running: trueNumberAnimation{target: cubeproperty: "rotateAngle"from: 0to: 360duration: 5000}Vector3dAnimation{target: cubeproperty: "axis"from: Qt.vector3d( 0, 1, 0 )to: Qt.vector3d( 1, 0, 0 )duration: 5000}loops: Animation.Infinite}}Rectangle{anchors.centerIn: parentwidth: textField.width * 1.2height: textField.height * 1.5radius: textField.height / 3color: "lightsteelblue"border.color: "white"border.width: 2Text{id: textFieldanchors.centerIn: parenttext: "您好世界!"font.pixelSize: root.width / 20}}
}

我们发现Cube类并非Qt Quick自带的,而是我们自己定义的一个QML模块OpenGLCube。

依照第六篇文章上面的方法,我们通过在C++注冊QML类实现了让QML訪问C++代码。以下是主函数的实现:

#include <QApplication>
#include <QQmlApplicationEngine>
#include "Cube.h"int main( int argc, char** argv )
{QApplication app( argc, argv );qmlRegisterType<Cube>( "OpenGLCube", 1, 0, "Cube" );QQmlApplicationEngine engine;engine.load( QUrl( QStringLiteral( "qrc:///main.qml" ) ) );return app.exec( );
}

         主函数中通过qmlRegisterType函数向QML环境注冊了一个QML类。接下来就是Cube类的定义和实现了。

Cube.h

#ifndef CUBE_H
#define CUBE_H#include <QVector3D>
#include <QMatrix4x4>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QQuickItem>
#include <QQuickWindow>#define DECLRARE_Q_PROPERTY( aType, aProperty ) protected:\aType m_ ## aProperty; public:\aType aProperty( void ) { return m_ ## aProperty; } \void set ## aProperty( aType _ ## aProperty ) \{\m_ ## aProperty = _ ## aProperty;\if ( window( ) != Q_NULLPTR )\{\window( )->update( );\}\}class Cube: public QQuickItem
{Q_OBJECTQ_PROPERTY( qreal rotateAngle READ RotateAngleWRITE setRotateAngle NOTIFY RotateAngleChanged )Q_PROPERTY( QVector3D axis READ AxisWRITE setAxis NOTIFY AxisChanged )
public:explicit Cube( void );
signals:void RotateAngleChanged( void );void AxisChanged( void );
protected slots:void Render( void );void OnWindowChanged( QQuickWindow* pWindow );void Release( void );
protected:bool RunOnce( void );QMatrix4x4                  m_ModelViewMatrix;QMatrix4x4                  m_ProjectionMatrix;QOpenGLBuffer               m_VertexBuffer, m_IndexBuffer;QOpenGLBuffer               m_ColorBuffer;QOpenGLShaderProgram        m_ShaderProgram;DECLRARE_Q_PROPERTY( qreal, RotateAngle )DECLRARE_Q_PROPERTY( QVector3D, Axis )
};#endif // CUBE_H

         在Cube.h中,我们让Cube继承QQuickItem。由于Cube也是一个Qt Quick的显示对象。这里顺便说一下,C++的QQuickItem相应QML的Item类。而C++的QObject则是相应QML的QtObject类。在C++中,QQuickItem继承于QObject,在QML中。Item继承QtObject。在类的定义中。我使用了QOpenGLBuffer来保持各种画图缓存(缓冲区),使用QOpenGLShaderProgram来方便地加载着色器数据。最后我使用了一个方便的宏来定义受QML属性系统控制的成员变量。当这些变量发生变化的时候,让其通知父窗体(QQuickWindow)进行更新。

Cube.cpp

// Cube.cpp
#include "Cube.h"Cube::Cube( void ):m_VertexBuffer( QOpenGLBuffer::VertexBuffer ),m_IndexBuffer( QOpenGLBuffer::IndexBuffer ),m_ColorBuffer( QOpenGLBuffer::VertexBuffer ),m_RotateAngle( 0.0f ),m_Axis( 1.0f, 1.0f, 0.0f )
{   // 初始化connect( this, SIGNAL( windowChanged( QQuickWindow* ) ),this, SLOT( OnWindowChanged( QQuickWindow* ) ) );
}void Cube::OnWindowChanged( QQuickWindow* pWindow )
{if ( pWindow == Q_NULLPTR ) return;connect( pWindow, SIGNAL( beforeRendering( ) ),this, SLOT( Render( ) ), Qt::DirectConnection );pWindow->setClearBeforeRendering( false );
}void Cube::Render( void )
{static bool runOnce = RunOnce( );Q_UNUSED( runOnce );// 运动m_ModelViewMatrix.setToIdentity( );m_ModelViewMatrix.translate( 0.0f, 0.0f, -60.0f );m_ModelViewMatrix.rotate( m_RotateAngle, m_Axis.x( ),m_Axis.y( ), m_Axis.z( ) );// 渲染glViewport( 0, 0, window( )->width( ), window( )->height( ) );glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );glEnable( GL_DEPTH_TEST );glEnable( GL_CULL_FACE );glFrontFace( GL_CW );m_ShaderProgram.bind( );m_VertexBuffer.bind( );int posLoc = m_ShaderProgram.attributeLocation( "position" );m_ShaderProgram.enableAttributeArray( posLoc );m_ShaderProgram.setAttributeBuffer( posLoc,                 // 位置GL_FLOAT,               // 类型0,                      // 偏移3,                      // 元大小0 );                    // 迈m_ColorBuffer.bind( );int colorLoc = m_ShaderProgram.attributeLocation( "color" );m_ShaderProgram.enableAttributeArray( colorLoc );m_ShaderProgram.setAttributeBuffer( colorLoc,               // 位置GL_FLOAT,               // 类型0,                      // 偏移4,                      // 元大小0 );                    // 迈m_IndexBuffer.bind( );m_ShaderProgram.setUniformValue( "modelViewMatrix", m_ModelViewMatrix );m_ShaderProgram.setUniformValue( "projectionMatrix", m_ProjectionMatrix );glDrawElements( GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, Q_NULLPTR );m_ShaderProgram.disableAttributeArray( posLoc );m_ShaderProgram.disableAttributeArray( colorLoc );m_IndexBuffer.release( );m_VertexBuffer.release( );m_ShaderProgram.release( );
}bool Cube::RunOnce( void )
{// 初始化着色器m_ShaderProgram.addShaderFromSourceFile( QOpenGLShader::Vertex,":/shader/Shader.vsh" );m_ShaderProgram.addShaderFromSourceFile( QOpenGLShader::Fragment,":/shader/Shader.fsh" );m_ShaderProgram.link( );// 初始化顶点缓存const GLfloat length = 10.0f;const GLfloat vertices[] ={length, -length, length,length, -length, -length,-length, -length, -length,-length, -length, length,length, length, length,length, length, -length,-length, length, -length,-length, length, length};m_VertexBuffer.setUsagePattern( QOpenGLBuffer::StaticDraw );m_VertexBuffer.create( );m_VertexBuffer.bind( );m_VertexBuffer.allocate( vertices, sizeof( vertices ) );// 初始化颜色的缓存const GLfloat colors[] ={1.0f, 0.0f, 1.0f, 1.0f,1.0f, 0.0f, 0.0f, 1.0f,0.0f, 0.0f, 0.0f, 1.0f,0.0f, 0.0f, 1.0f, 1.0f,1.0f, 1.0f, 1.0f, 1.0f,1.0f, 1.0f, 0.0f, 1.0f,0.0f, 1.0f, 0.0f, 1.0f,0.0f, 1.0f, 1.0f, 1.0f};m_ColorBuffer.setUsagePattern( QOpenGLBuffer::StaticDraw );m_ColorBuffer.create( );m_ColorBuffer.bind( );m_ColorBuffer.allocate( colors, sizeof( colors ) );// 初始化索引缓存GLubyte indices[] ={0, 1, 2, 0, 2, 3,// 以下7, 6, 4, 6, 5, 4,// 上面7, 4, 3, 4, 0, 3,// 左面5, 6, 1, 6, 2, 1,// 右面4, 5, 0, 5, 1, 0,// 前面3, 2, 6, 3, 6, 7,// 背面};m_IndexBuffer.setUsagePattern( QOpenGLBuffer::StaticDraw );m_IndexBuffer.create( );m_IndexBuffer.bind( );m_IndexBuffer.allocate( indices, sizeof( indices ) );// 设定模型矩阵和投影矩阵float aspectRatio  = float( window( )->width( ) ) / float( window( )->height( ) );m_ProjectionMatrix.perspective( 45.0f,aspectRatio,0.5f,500.0f );connect( window( )->openglContext( ),SIGNAL( aboutToBeDestroyed( ) ),this, SLOT( Release( ) ),Qt::DirectConnection );return true;
}void Cube::Release( void )
{qDebug( "Vertex buffer and index buffer are to be destroyed." );m_VertexBuffer.destroy( );m_IndexBuffer.destroy( );m_ColorBuffer.destroy( );
}

         类的实现较复杂。大致分为构造阶段、初始化阶段、渲染阶段和释放空间阶段。

这里我们使用了OpenGL ES 2.0经常使用的buffer + attribute array方式来进行高效渲染。

有关上述OpenGL的知识,感兴趣的同行们能够看看《OpenGL ES 2.0 Programming Guide》、Qt书籍有关OpenGL的部分、KDAB博客中有关OpenGL的知识以及我的其他博客以获得相关知识。

         上述程序加载了顶点着色器和片断着色器。它们例如以下所看到的:

// Shader.vsh
attribute highp vec3 position;
attribute highp vec4 color;uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;varying highp vec4 v_Color;void main( void )
{gl_Position = projectionMatrix *modelViewMatrix *vec4( position, 1.0 );v_Color = color;
}

 
// Shader.fsh
varying highp vec4 v_Color;void main( void )
{gl_FragColor = v_Color;
}

         本例在三大桌面平台上执行正常,同一时候在Android平台上也可以顺利地执行。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/285063.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ArcGIS微课1000例】0013:ArcGIS创建色带图例(以GlobeLand30土地覆盖数据为例)

本文以GlobeLand30土地覆盖数据(2010年)为例,讲解在ArcGIS中创建色带图例的方法。 案例数据: 色带图例: 创建过程: 选择2010年数据,点击添加到右侧的图例项。 在图例向导里面,删除图例标题,点击下一步。

使用.NET IoT驱动超声波测距传感器

背景最近买的一堆传感器到货了&#xff0c;先来把玩一下超声波测距传感器。超声波传感器一般用于机器人&#xff0c;小车的避障&#xff0c;物体的测距&#xff0c;液位检测&#xff0c;停车检测等领域。HC-SR04知识回顾开始之前我们先复习一下高中的物理知识。原理通过上图的原…

2019-nCoV 全国新型肺炎疫情每日动态趋势可视图

传染源: 野生动物&#xff0c;可能为中华菊头蝠 病毒: 新型冠状病毒 2019-nCoV 传播途径: 经呼吸道飞沫传播&#xff0c;亦可通过接触传播 易感人群: 人群普遍易感。老年人及有基础疾病者感染后病情较重&#xff0c;儿童及婴幼儿也有发病 潜伏期: 一般为 3~7 天&#xff0c;最长…

【ArcGIS遇上Python】ArcGIS Python将多个文件夹内的分幅数据整合到同一个文件夹内——以Globeland30数据为例

文章目录 WinRAR解压缩ArcGIS Python批处理从Glabeland30官网下载的全球地覆盖数据包括3年(2000、2010、2020),每一年都是按图幅存储的tif格式栅格数据。以2000的数据为例,全球共812个图幅,每一个图幅对应一个压缩包,如下图所示。 WinRAR解压缩 在进行数据预处理时,必须…

Docker Compose 安装 Superset

前言动手学 dockerSuperset 是一个强大的在线 SQL 查询编辑工具&#xff0c;同时也是一个轻量级的 BI 工具&#xff0c;今天我们就来动手学一下用 docker compose 安装 Superset。安装动手学 docker安装 git 并克隆项目&#xff1a;yum install git -ygit clone https://github…

[转]再见 NoSQL!

为解决大规模数据集合多重数据种类带来的挑战&#xff0c;NoSQL 应运而生&#xff0c;但现在却也遇到了诸多问题&#xff0c;本文作者 Rick Negrin&#xff0c;曾在微软工作 12 年&#xff0c;并在 SQL Server 团队度过大部分光阴&#xff0c;他提出&#xff0c;是时候「和 NoS…

【ArcGIS Pro微课1000例】0008:ArcGIS Pro加载不同来源的在线底图数据

ArcGIS Pro可以很方便的选择不同来源的在线底图数据,如中国地图彩色版、各种形式的天地图等。 打开ArcGIS Pro,点击左下角的【设置】。 点击【选项】。 ArcGIS Pro提供了三种形式的底图:组织的默认底图、自定义底图<

php+mysql实现数据分批插入

上周需要将云端的数据有条件的录入到mysql中&#xff0c;最开始是使用遍历数据然后一条条的插入的笨方法&#xff0c;结果速度慢的要死&#xff0c;所以又随便写了个笨方法2.0&#xff0c;记录一下自己菜鸟的成长历程。同时这也是在博客园的第一篇文章&#xff0c;目的仅仅是单…

[转]敏捷开发之Scrum扫盲,及敏捷开发中XP与SCRUM的区别

敏捷开发之Scrum扫盲篇 现在敏捷开发是越来越火了&#xff0c;人人都在谈敏捷&#xff0c;人人都在学习Scrum和XP… 为了不落后他人&#xff0c;于是我也开始学习Scrum&#xff0c;今天主要是对我最近阅读的相关资料&#xff0c;根据自己的理解&#xff0c;用自己的话来讲述S…

最流行的 .NET 反编译工具合集

编译和反编译 .NET 中的编译是把开发人员写的 C# 代码转化为计算机可理解的代码的过程&#xff0c;也就是中间语言代码&#xff08;IL代码&#xff09;。在这个过程中&#xff0c;C# 源代码被转换为可执行文件&#xff08;exe或者dll 文件&#xff09;。反编译则和编译相反&am…

三款国产计算机日常办公体验对比

近年来&#xff0c;信创事业开展的如火如荼&#xff0c;多款CPU和操作系统均在机关单位和央企批量应用。目前&#xff0c;信创电脑主要应用场景是日常办公&#xff0c;特别是对机关单位而言&#xff0c;文档、表格、PPT、PDF、浏览器等软件堪称办公必备。下面&#xff0c;我们就…

[转]Flask --- 框架快速入门

From&#xff1a;http://docs.jinkan.org/docs/flask/quickstart.html Flask 用户指南&#xff1a;http://docs.jinkan.org/docs/flask/index.html w3cschool.cn 的 Flask 教程&#xff1a;https://www.w3cschool.cn/flask/ 《Flask Web开发&#xff1a;基于Python的Web应用…

MAC usb启动盘制作

1.从App Store 下载OS 2.磁盘工具格式化磁盘默认即可 3. 为啥截图&#xff0c;因为有些是不一样的&#xff0c;建议使用 Tab建&#xff0c; 未命名则是你移动U盘命名的名称。 4.完成&#xff1a;所有的命令完成的话&#xff0c; 终端界面中会出现 Erasing Disk: 0%...10%...…

【ArcGIS Pro微课1000例】0011:ArcGIS Pro范围内汇总工具的巧妙使用——以甘肃省各地区内河流总长度计算为例

文章目录 问题描述范围内汇总工具介绍# 案例实现问题描述 统计甘肃省各个地区界内河流的总长度,如下图所示。 范围内汇总工具介绍 在ArcMap中可以实现河流总长度的统计,具体操作可以参考:【ArcGIS风暴】ArcGIS获取一个省各个地区界内的河流的总长度–以甘肃省为例,在ArcG…

C语言试题177之实现二分查找算法,折半查找算法

📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款刷算法、笔试、面经、拿大公司offer神器👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 题目: 本实例…

30分钟掌握ES6/ES2015核心内容

2019独角兽企业重金招聘Python工程师标准>>> ECMAScript 6&#xff08;以下简称ES6&#xff09;是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的&#xff0c;所以又称ECMAScript 2015。 也就是说&#xff0c;ES6就是ES2015。 虽然目前并不是所有浏…

Windows 11 22H2 22621.290 和 22622.290 推送

面向 Beta 频道的 Windows 预览体验成员&#xff0c;微软现已推送 Windows 11 预览版 Build 22621.290 和 22622.290。微软宣布 Beta 频道Windows 11 预览版启用全新体验&#xff0c;分为两组进行测试。通过两组 Windows 预览体验成员的使用数据和反馈&#xff0c;以更好的测试…

【ArcGIS Pro微课1000例】0012:ArcGIS Pro属性表中文乱码完美解决办法汇总

如图所示,安装完ArcGIS Pro后,由于计算机系统和应用软件字符编码的问题,导致加载矢量数据并打开属性表后,会发现中文字段出现了乱码。 属性表中文乱码: 属性表中文正常: 事实上,ArcMap中也会出现中文属性表乱码的情况。 乱码的出现会给学习和工作带来很大不便,因此本…

dubbo中的Filter顺序是如何确定的

2019独角兽企业重金招聘Python工程师标准>>> 服务提供方的过滤器被调用顺序&#xff1a; EchoFilter->ClassLoaderFilter->GenericFilter->ContextFilter->(这4个是在代码中指定的) ExceptionFilter-> TimeoutFilter ->MonitorFilter-> TraceF…

java提示找不到或无法加载主类

背景 默许jdk的配置大家都没有问题&#xff0c;执行java&#xff0c;javac无报错&#xff0c;但今天在尝试在本地起来kafka的时候&#xff0c;提示java 找不到或无法加载主类&#xff0c;然后日志中提示 Files 找不到或无法加载主类&#xff1b;C:\Program 分析 其实很轻松的猜…