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.0

Window
{
    id: root
    width: Qt.platform.os === "android"? Screen.width: 320
    height: Qt.platform.os === "android"? Screen.height: 480
    visible: true

    Cube
    {
        id: cube
        anchors.fill: parent
        ParallelAnimation
        {
            running: true
            NumberAnimation
            {
                target: cube
                property: "rotateAngle"
                from: 0
                to: 360
                duration: 5000
            }

            Vector3dAnimation
            {
                target: cube
                property: "axis"
                from: Qt.vector3d( 0, 1, 0 )
                to: Qt.vector3d( 1, 0, 0 )
                duration: 5000
            }
            loops: Animation.Infinite
        }
    }

    Rectangle
    {
        anchors.centerIn: parent
        width: textField.width * 1.2
        height: textField.height * 1.5
        radius: textField.height / 3
        color: "lightsteelblue"
        border.color: "white"
        border.width: 2
        Text
        {
            id: textField
            anchors.centerIn: parent
            text: "您好世界!"
            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_OBJECT
    Q_PROPERTY( qreal rotateAngle READ RotateAngle
                WRITE setRotateAngle NOTIFY RotateAngleChanged )
    Q_PROPERTY( QVector3D axis READ Axis
                WRITE 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平台上也可以顺利地执行。





本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5386780.html,如需转载请自行联系原作者

相关文章
|
20小时前
|
机器学习/深度学习 开发框架 Android开发
移动应用开发的未来:跨平台框架与原生系统的融合
【5月更文挑战第10天】 随着移动设备成为日常生活和工作的核心,移动应用的开发已经迅速成为一个不断进化的技术领域。本文将探讨当前移动应用开发的新趋势,特别是跨平台开发框架的兴起以及它们与传统移动操作系统之间的互动。我们将分析如何通过结合跨平台工具的便捷性和原生系统的性能优势,来构建更加高效、可靠且具有前瞻性的移动应用。
3 0
|
2天前
|
人工智能 前端开发 物联网
移动应用开发的未来:跨平台框架与原生系统之争
【5月更文挑战第8天】 在本文中,我们探讨了移动应用开发的现状和未来趋势,特别是跨平台框架与原生系统之间的竞争。我们将分析这两种方法的优缺点,以及它们如何影响开发者和企业的选择。我们还将讨论一些新兴的技术,如人工智能和物联网,以及它们如何改变移动应用开发的未来。
24 10
|
2天前
|
前端开发 开发工具 Android开发
探索移动应用开发的未来:跨平台工具与原生系统的挑战
【5月更文挑战第8天】随着移动设备日益普及,移动应用成为日常生活和业务操作不可或缺的组成部分。本文深入分析了移动应用开发领域的两大趋势——跨平台开发工具的兴起与原生操作系统的优化挑战。通过对比Flutter、React Native等跨平台工具的技术特点,以及iOS和Android各自在性能、安全性和用户体验方面的最新进展,文章旨在为开发者和企业决策者提供洞见,帮助他们在快速变化的移动市场中做出明智的选择。
|
2天前
|
开发框架 前端开发 Android开发
移动应用开发的未来:跨平台框架与原生操作系统的融合
【5月更文挑战第8天】 随着移动技术的迅猛发展,移动应用已成为日常生活不可或缺的一部分。本文将探讨移动应用开发的最新趋势,特别是跨平台开发框架与原生操作系统之间的融合。我们将分析如何通过利用跨平台工具如React Native和Flutter,同时不失原生性能及用户体验,来提升开发效率并扩大应用的可及性。此外,文章还将着眼于移动操作系统的演进,尤其是它们如何适应新的应用开发需求,以及开发者如何应对操作系统更新带来的挑战。
|
3天前
|
机器学习/深度学习 人工智能 前端开发
移动应用开发的未来:跨平台框架与原生系统协同进化
【5月更文挑战第7天】 在当今快速发展的数字时代,移动应用已成为日常生活的延伸。本文旨在探讨移动应用开发领域的最新趋势,特别是跨平台开发框架的兴起以及它们如何与原生移动操作系统相互作用。我们将分析Flutter、React Native等流行的跨平台解决方案,并考察它们在性能、用户体验和生态系统中的地位。同时,我们也将关注Android和iOS两大阵营的最新进展,以及开发者应如何在这两者间做出选择或寻求平衡。最后,文章将提供对未来移动应用开发可能走向的见解,尤其是人工智能和机器学习技术对移动应用的影响。
|
3天前
|
人工智能 前端开发 搜索推荐
移动应用开发的未来:跨平台框架与原生系统的融合
【5月更文挑战第7天】 随着移动互联网的蓬勃发展,移动应用已成为日常生活与商业活动不可或缺的组成部分。开发者面临着不断演进的技术栈和用户需求,而移动操作系统的多样化又加剧了这一挑战。本文深入探讨移动应用开发领域的最新趋势,特别是跨平台开发框架与原生系统之间的相互作用与融合。通过分析当前市场上流行的跨平台工具如Flutter、React Native以及原生系统iOS和Android的最新特性,我们旨在为开发者提供一个关于如何利用这些技术来构建更高效、更具创新性和响应性的移动应用的见解。
18 5
|
3天前
|
开发框架 前端开发 Android开发
移动应用开发的未来:跨平台框架与原生系统的融合
【5月更文挑战第7天】随着移动设备用户群体的不断扩大,移动应用的开发已成为技术前沿的热点。本文将深入探讨移动应用开发的新趋势——跨平台开发框架与原生系统间的融合,以及这一趋势如何影响开发者构建更高效、更具兼容性的应用程序。通过分析当前市场上流行的跨平台工具如Flutter、React Native和Xamarin,结合最新的操作系统特性,本文旨在为读者提供一个关于未来移动应用开发趋势的清晰视角。
|
5天前
|
机器学习/深度学习 Android开发 开发者
移动应用开发的未来趋势:跨平台与原生之争
【5月更文挑战第5天】随着移动设备的普及,移动应用开发已成为技术创新的前沿阵地。本文将探讨移动应用开发的两大主流模式——跨平台与原生开发,分析各自的优势与局限,并预测未来发展趋势。文章还将深入讨论移动操作系统的演进如何影响开发策略,以及开发者如何在快速变化的市场中保持竞争力。
|
6天前
|
机器学习/深度学习 开发框架 前端开发
探索移动应用开发的未来:跨平台框架与原生操作系统的融合
【5月更文挑战第4天】在本文中,我们将深入探讨移动应用开发的当前趋势和未来展望,特别关注跨平台开发框架和原生操作系统之间的相互作用。随着技术的不断进步,开发者面临着选择最佳开发策略的挑战,以确保他们的应用能够在不同的移动设备上提供无缝的用户体验。我们将分析跨平台工具如React Native、Flutter和Xamarin的优势与局限,同时考察它们如何与iOS和Android等原生系统协同工作,以及这种协同对移动应用生态系统的潜在影响。
|
6天前
|
人工智能 安全 物联网
移动应用开发的未来趋势:跨平台与原生之争
【5月更文挑战第4天】 随着移动互联网的高速发展,移动应用已成为日常生活的重要组成部分。本文探讨了当前移动应用开发领域的两个主要方向——跨平台与原生开发——的优势与挑战,并预测了未来可能的技术走向。通过分析不同移动操作系统对开发实践的影响,文章指出了移动应用开发在性能、用户体验和技术选择上的权衡。同时,探讨了新兴技术如人工智能和物联网如何重塑移动应用开发的生态。最后,提供了对未来移动应用开发者的建议。

推荐镜像

更多