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代码。本例很简单,只有一个界面,没有任何界面的跳转。我们在前面显示一个矩形,上面写了”您好世界!”的文字,后面显示的是一个旋转的矩形。按照规定,先显示的内容在最底层显示,于是我们将Cube放在前面,Rectangle放在了后面。

[css]  view plain copy
  1. import QtQuick 2.2  
  2. import QtQuick.Window 2.2  
  3. import OpenGLCube 1.0  
  4.   
  5. Window  
  6. {  
  7.     id: root  
  8.     width: Qt.platform.os === "android"? Screen.width320  
  9.     height: Qt.platform.os === "android"? Screen.height480  
  10.     visible: true  
  11.   
  12.     Cube  
  13.     {  
  14.         id: cube  
  15.         anchors.fill: parent  
  16.         ParallelAnimation  
  17.         {  
  18.             running: true  
  19.             NumberAnimation  
  20.             {  
  21.                 target: cube  
  22.                 property: "rotateAngle"  
  23.                 from: 0  
  24.                 to: 360  
  25.                 duration: 5000  
  26.             }  
  27.   
  28.             Vector3dAnimation  
  29.             {  
  30.                 target: cube  
  31.                 property: "axis"  
  32.                 from: Qt.vector3d( 010 )  
  33.                 to: Qt.vector3d( 100 )  
  34.                 duration: 5000  
  35.             }  
  36.             loops: Animation.Infinite  
  37.         }  
  38.     }  
  39.   
  40.     Rectangle  
  41.     {  
  42.         anchors.centerIn: parent  
  43.         width: textField.width * 1.2  
  44.         height: textField.height * 1.5  
  45.         radius: textField.height / 3  
  46.         color"lightsteelblue"  
  47.         border.color"white"  
  48.         border.width2  
  49.         Text  
  50.         {  
  51.             id: textField  
  52.             anchors.centerIn: parent  
  53.             text: "您好世界!"  
  54.             font.pixelSize: root.width / 20  
  55.         }  
  56.     }  
  57. }  

我们发现Cube类并不是Qt Quick自带的,而是我们自定义的一个QML模块OpenGLCube。按照第六篇文章上面的方法,我们通过在C++注册QML类实现了让QML访问C++代码。下面是主函数的实现:

[cpp]  view plain copy
  1. #include <QApplication>  
  2. #include <QQmlApplicationEngine>  
  3. #include "Cube.h"  
  4.   
  5. int main( int argc, char** argv )  
  6. {  
  7.     QApplication app( argc, argv );  
  8.   
  9.     qmlRegisterType<Cube>( "OpenGLCube", 1, 0, "Cube" );  
  10.   
  11.     QQmlApplicationEngine engine;  
  12.     engine.load( QUrl( QStringLiteral( "qrc:///main.qml" ) ) );  
  13.   
  14.     return app.exec( );  
  15. }  

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

Cube.h

[cpp]  view plain copy
  1. #ifndef CUBE_H  
  2. #define CUBE_H  
  3.   
  4. #include <QVector3D>  
  5. #include <QMatrix4x4>  
  6. #include <QOpenGLFunctions>  
  7. #include <QOpenGLBuffer>  
  8. #include <QOpenGLShaderProgram>  
  9. #include <QQuickItem>  
  10. #include <QQuickWindow>  
  11.   
  12. #define DECLRARE_Q_PROPERTY( aType, aProperty ) protected:\  
  13.     aType m_ ## aProperty; public:\  
  14.     aType aProperty( void ) { return m_ ## aProperty; } \  
  15.     void set ## aProperty( aType _ ## aProperty ) \  
  16.     {\  
  17.         m_ ## aProperty = _ ## aProperty;\  
  18.         if ( window( ) != Q_NULLPTR )\  
  19.         {\  
  20.             window( )->update( );\  
  21.         }\  
  22.     }  
  23.   
  24. class Cube: public QQuickItem  
  25. {  
  26.     Q_OBJECT  
  27.     Q_PROPERTY( qreal rotateAngle READ RotateAngle  
  28.                 WRITE setRotateAngle NOTIFY RotateAngleChanged )  
  29.     Q_PROPERTY( QVector3D axis READ Axis  
  30.                 WRITE setAxis NOTIFY AxisChanged )  
  31. public:  
  32.     explicit Cube( void );  
  33. signals:  
  34.     void RotateAngleChanged( void );  
  35.     void AxisChanged( void );  
  36. protected slots:  
  37.     void Render( void );  
  38.     void OnWindowChanged( QQuickWindow* pWindow );  
  39.     void Release( void );  
  40. protected:  
  41.     bool RunOnce( void );  
  42.   
  43.     QMatrix4x4                  m_ModelViewMatrix;  
  44.     QMatrix4x4                  m_ProjectionMatrix;  
  45.     QOpenGLBuffer               m_VertexBuffer, m_IndexBuffer;  
  46.     QOpenGLBuffer               m_ColorBuffer;  
  47.     QOpenGLShaderProgram        m_ShaderProgram;  
  48.   
  49.     DECLRARE_Q_PROPERTY( qreal, RotateAngle )  
  50.     DECLRARE_Q_PROPERTY( QVector3D, Axis )  
  51. };  
  52.   
  53. #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

[cpp]  view plain copy
  1. // Cube.cpp  
  2. #include "Cube.h"  
  3.   
  4. Cube::Cube( void ):  
  5.     m_VertexBuffer( QOpenGLBuffer::VertexBuffer ),  
  6.     m_IndexBuffer( QOpenGLBuffer::IndexBuffer ),  
  7.     m_ColorBuffer( QOpenGLBuffer::VertexBuffer ),  
  8.     m_RotateAngle( 0.0f ),  
  9.     m_Axis( 1.0f, 1.0f, 0.0f )  
  10. {     
  11.     // 初始化  
  12.     connect( this, SIGNAL( windowChanged( QQuickWindow* ) ),  
  13.              this, SLOT( OnWindowChanged( QQuickWindow* ) ) );  
  14. }  
  15.   
  16. void Cube::OnWindowChanged( QQuickWindow* pWindow )  
  17. {  
  18.     if ( pWindow == Q_NULLPTR ) return;  
  19.     connect( pWindow, SIGNAL( beforeRendering( ) ),  
  20.              this, SLOT( Render( ) ), Qt::DirectConnection );  
  21.     pWindow->setClearBeforeRendering( false );  
  22. }  
  23.   
  24. void Cube::Render( void )  
  25. {  
  26.     static bool runOnce = RunOnce( );  
  27.     Q_UNUSED( runOnce );  
  28.   
  29.     // 运动  
  30.     m_ModelViewMatrix.setToIdentity( );  
  31.     m_ModelViewMatrix.translate( 0.0f, 0.0f, -60.0f );  
  32.     m_ModelViewMatrix.rotate( m_RotateAngle, m_Axis.x( ),  
  33.                               m_Axis.y( ), m_Axis.z( ) );  
  34.   
  35.     // 渲染  
  36.     glViewport( 0, 0, window( )->width( ), window( )->height( ) );  
  37.     glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );  
  38.     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );  
  39.     glEnable( GL_DEPTH_TEST );  
  40.     glEnable( GL_CULL_FACE );  
  41.     glFrontFace( GL_CW );  
  42.   
  43.     m_ShaderProgram.bind( );  
  44.     m_VertexBuffer.bind( );  
  45.     int posLoc = m_ShaderProgram.attributeLocation( "position" );  
  46.     m_ShaderProgram.enableAttributeArray( posLoc );  
  47.     m_ShaderProgram.setAttributeBuffer( posLoc,                 // 位置  
  48.                                         GL_FLOAT,               // 类型  
  49.                                         0,                      // 偏移  
  50.                                         3,                      // 元大小  
  51.                                         0 );                    // 迈  
  52.   
  53.     m_ColorBuffer.bind( );  
  54.     int colorLoc = m_ShaderProgram.attributeLocation( "color" );  
  55.     m_ShaderProgram.enableAttributeArray( colorLoc );  
  56.     m_ShaderProgram.setAttributeBuffer( colorLoc,               // 位置  
  57.                                         GL_FLOAT,               // 类型  
  58.                                         0,                      // 偏移  
  59.                                         4,                      // 元大小  
  60.                                         0 );                    // 迈  
  61.     m_IndexBuffer.bind( );  
  62.     m_ShaderProgram.setUniformValue( "modelViewMatrix", m_ModelViewMatrix );  
  63.     m_ShaderProgram.setUniformValue( "projectionMatrix", m_ProjectionMatrix );  
  64.     glDrawElements( GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, Q_NULLPTR );  
  65.   
  66.     m_ShaderProgram.disableAttributeArray( posLoc );  
  67.     m_ShaderProgram.disableAttributeArray( colorLoc );  
  68.     m_IndexBuffer.release( );  
  69.     m_VertexBuffer.release( );  
  70.     m_ShaderProgram.release( );  
  71. }  
  72.   
  73. bool Cube::RunOnce( void )  
  74. {  
  75.     // 初始化着色器  
  76.     m_ShaderProgram.addShaderFromSourceFile( QOpenGLShader::Vertex,  
  77.                                              ":/shader/Shader.vsh" );  
  78.     m_ShaderProgram.addShaderFromSourceFile( QOpenGLShader::Fragment,  
  79.                                              ":/shader/Shader.fsh" );  
  80.     m_ShaderProgram.link( );  
  81.   
  82.     // 初始化顶点缓存  
  83.     const GLfloat length = 10.0f;  
  84.     const GLfloat vertices[] =  
  85.     {  
  86.         length, -length, length,  
  87.         length, -length, -length,  
  88.         -length, -length, -length,  
  89.         -length, -length, length,  
  90.         length, length, length,  
  91.         length, length, -length,  
  92.         -length, length, -length,  
  93.         -length, length, length  
  94.     };  
  95.   
  96.     m_VertexBuffer.setUsagePattern( QOpenGLBuffer::StaticDraw );  
  97.     m_VertexBuffer.create( );  
  98.     m_VertexBuffer.bind( );  
  99.     m_VertexBuffer.allocate( vertices, sizeof( vertices ) );  
  100.   
  101.     // 初始化颜色的缓存  
  102.     const GLfloat colors[] =  
  103.     {  
  104.         1.0f, 0.0f, 1.0f, 1.0f,  
  105.         1.0f, 0.0f, 0.0f, 1.0f,  
  106.         0.0f, 0.0f, 0.0f, 1.0f,  
  107.         0.0f, 0.0f, 1.0f, 1.0f,  
  108.         1.0f, 1.0f, 1.0f, 1.0f,  
  109.         1.0f, 1.0f, 0.0f, 1.0f,  
  110.         0.0f, 1.0f, 0.0f, 1.0f,  
  111.         0.0f, 1.0f, 1.0f, 1.0f  
  112.     };  
  113.     m_ColorBuffer.setUsagePattern( QOpenGLBuffer::StaticDraw );  
  114.     m_ColorBuffer.create( );  
  115.     m_ColorBuffer.bind( );  
  116.     m_ColorBuffer.allocate( colors, sizeof( colors ) );  
  117.   
  118.   
  119.     // 初始化索引缓存  
  120.     GLubyte indices[] =  
  121.     {  
  122.         0, 1, 2, 0, 2, 3,// 下面  
  123.         7, 6, 4, 6, 5, 4,// 上面  
  124.         7, 4, 3, 4, 0, 3,// 左面  
  125.         5, 6, 1, 6, 2, 1,// 右面  
  126.         4, 5, 0, 5, 1, 0,// 前面  
  127.         3, 2, 6, 3, 6, 7,// 背面  
  128.     };  
  129.   
  130.     m_IndexBuffer.setUsagePattern( QOpenGLBuffer::StaticDraw );  
  131.     m_IndexBuffer.create( );  
  132.     m_IndexBuffer.bind( );  
  133.     m_IndexBuffer.allocate( indices, sizeof( indices ) );  
  134.   
  135.     // 设定模型矩阵和投影矩阵  
  136.     float aspectRatio  = float( window( )->width( ) ) / float( window( )->height( ) );  
  137.     m_ProjectionMatrix.perspective( 45.0f,  
  138.                                     aspectRatio,  
  139.                                     0.5f,  
  140.                                     500.0f );  
  141.   
  142.     connect( window( )->openglContext( ),  
  143.              SIGNAL( aboutToBeDestroyed( ) ),  
  144.              this, SLOT( Release( ) ),  
  145.              Qt::DirectConnection );  
  146.   
  147.     return true;  
  148. }  
  149.   
  150. void Cube::Release( void )  
  151. {  
  152.     qDebug( "Vertex buffer and index buffer are to be destroyed." );  
  153.     m_VertexBuffer.destroy( );  
  154.     m_IndexBuffer.destroy( );  
  155.     m_ColorBuffer.destroy( );  
  156. }  

         类的实现较复杂。大致分为构造阶段、初始化阶段、渲染阶段和释放空间阶段。这里我们使用了OpenGL ES 2.0常用的buffer + attribute array方式来进行高效渲染。有关上述OpenGL的知识,感兴趣的同行们可以看看《OpenGL ES 2.0 Programming Guide》、Qt书籍有关OpenGL的部分、KDAB博客中有关OpenGL的知识以及我的其它博客以获得相关知识。

         上述程序载入了顶点着色器和片断着色器。它们如下所示:

[cpp]  view plain copy
  1. // Shader.vsh  
  2. attribute highp vec3 position;  
  3. attribute highp vec4 color;  
  4.   
  5. uniform mat4 modelViewMatrix;  
  6. uniform mat4 projectionMatrix;  
  7.   
  8. varying highp vec4 v_Color;  
  9.   
  10. void main( void )  
  11. {  
  12.     gl_Position = projectionMatrix *  
  13.             modelViewMatrix *  
  14.             vec4( position, 1.0 );  
  15.     v_Color = color;  
  16. }  

 
[cpp]  view plain copy
  1. // Shader.fsh  
  2. varying highp vec4 v_Color;  
  3.   
  4. void main( void )  
  5. {  
  6.     gl_FragColor = v_Color;  
  7. }  

         本例在三大桌面平台上运行正常,同时在Android平台上也能够顺利地运行。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值