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.pswp.cn/news/285063.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/285063.shtml
英文地址,請注明出處:http://en.pswp.cn/news/285063.shtml

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

相關文章

【HTML5初探之form標簽】解放表單驗證、增加文件上傳、集成拖放

導航【初探HTML5之使用新標簽布局】用html5布局我的博客頁&#xff01;【HTML5初探之form標簽】解放表單驗證、增加文件上傳、集成拖放【HTML5初探之繪制圖像&#xff08;上&#xff09;】看我canvas元素引領下一代web頁面【HTML5初探之繪制圖像&#xff08;下&#xff09;】看…

兩時間差

/** * Comments:返回時間差 * param 兩個字符串類型的時間差(time1-time2),type(D天,H時,M分,S秒,Z-S天時分秒) * return */ public final static SimpleDateFormat SF_SIZE19 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//初始化時間格式 public static String …

【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;最長…

C語言試題173之實現插入排序算法

??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款刷算法、筆試、面經、拿大公司offer神器?? 點擊跳轉進入網站 ?作者簡介:大家好,我是碼莎拉蒂,CSDN博客專家(全站排名Top 50),阿里云博客專家、51CTO博客專家、華為云享專家 1、題目 題目:實現排序…

【ArcGIS遇上Python】ArcGIS Python將多個文件夾內的分幅數據整合到同一個文件夾內——以Globeland30數據為例

文章目錄 WinRAR解壓縮ArcGIS Python批處理從Glabeland30官網下載的全球地覆蓋數據包括3年(2000、2010、2020),每一年都是按圖幅存儲的tif格式柵格數據。以2000的數據為例,全球共812個圖幅,每一個圖幅對應一個壓縮包,如下圖所示。 WinRAR解壓縮 在進行數據預處理時,必須…

Delphi中字符串比較大小 VS Oracle-SQL中字符串比較大小

重點注意Delphi和Oracle-SQL中比較字符串時空字符串的根本性的不同Delphi中的字符串比較 Delphi中字符串比較大小的規則&#xff1a;對應位置的字符按照字符編碼值逐個對比&#xff0c;直到遇到可以確定大小關系的就結束比較參考《Delphi的Ord函數和ASCII碼對照表》 常見的一些…

STM8S和STM8L調試串口中斷的注意點

源&#xff1a;STM8S和STM8L調試串口中斷的注意點

C語言試題174之實現快速排序算法

??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款刷算法、筆試、面經、拿大公司offer神器?? 點擊跳轉進入網站 ?作者簡介:大家好,我是碼莎拉蒂,CSDN博客專家(全站排名Top 50),阿里云博客專家、51CTO博客專家、華為云享專家 1、題目 題目:實現快速…

解決slideDown()、slideUp()執行結束后才執行下一次,導致鼠標離開后很久動畫依然在執行的問題...

問題描述&#xff1a; 給一個容器設置了mouseenter時&#xff0c;一個隱藏的box通過slideDown()顯示出來&#xff1b;mouseleave時&#xff0c;通過slideUp()隱藏。 當鼠標不斷在容器上滑過時&#xff0c;會導致鼠標離開很久后&#xff0c;動畫也在不斷執行 解決方法&#xff…

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提供了三種形式的底圖:組織的默認底圖、自定義底圖<

ORA-16198: LGWR received timedout error from KSR

ORA-16198: LGWR received timedout error from KSRORA-16198 意味著主庫上的LOG_ARCHIVE_DEST_2的NET_TIMEOUT設置的太小&#xff0c;導致LNS不能在設置的時間內將日志傳輸到備庫。解決方法是提高NET_TIMEOUT的值到15-20 秒&#xff0c;SQL>ALTER SYSTEM SET LOG_ARCHIVE_D…

php+mysql實現數據分批插入

上周需要將云端的數據有條件的錄入到mysql中&#xff0c;最開始是使用遍歷數據然后一條條的插入的笨方法&#xff0c;結果速度慢的要死&#xff0c;所以又隨便寫了個笨方法2.0&#xff0c;記錄一下自己菜鳥的成長歷程。同時這也是在博客園的第一篇文章&#xff0c;目的僅僅是單…

RIL接聽電話沒有聲音的問題 [ RIL_Answer | RIL_SetAudioDevices ]

沒有什么好說明的&#xff0c;直接上代碼&#xff1a; RIL_Answer(m_hRil); RILAUDIODEVICEINFO audioDeviceInfo; audioDeviceInfo.cbSize sizeof(audioDeviceInfo); audioDeviceInfo.dwParams RIL_PARAM_ADI_ALL; audioDeviceInfo.dwRxDevic…

[轉]敏捷開發之Scrum掃盲,及敏捷開發中XP與SCRUM的區別

敏捷開發之Scrum掃盲篇 現在敏捷開發是越來越火了&#xff0c;人人都在談敏捷&#xff0c;人人都在學習Scrum和XP… 為了不落后他人&#xff0c;于是我也開始學習Scrum&#xff0c;今天主要是對我最近閱讀的相關資料&#xff0c;根據自己的理解&#xff0c;用自己的話來講述S…

C語言試題175之實現選擇排序算法

??個人主頁:個人主頁 ??系列專欄:C語言試題200例 ??推薦一款刷算法、筆試、面經、拿大公司offer神器?? 點擊跳轉進入網站 ?作者簡介:大家好,我是碼莎拉蒂,CSDN博客專家(全站排名Top 50),阿里云博客專家、51CTO博客專家、華為云享專家 1、題目 題目:實現選擇…

最流行的 .NET 反編譯工具合集

編譯和反編譯 .NET 中的編譯是把開發人員寫的 C# 代碼轉化為計算機可理解的代碼的過程&#xff0c;也就是中間語言代碼&#xff08;IL代碼&#xff09;。在這個過程中&#xff0c;C# 源代碼被轉換為可執行文件&#xff08;exe或者dll 文件&#xff09;。反編譯則和編譯相反&am…