Qt mobile application development (8): Implements cross-platform QML and OpenGL hybrid rendering, and mobile application qml
Qt mobile application development (8): cross-platform QML and OpenGL hybrid Rendering
In the previous article, we talked about the interaction between QML and Java by using C ++. The QML/JS development highly praised by Qt 5 enables lightweight and rapid development of QML/JS to Support lightweight C ++ and almost any technology. In the following article, we will use QML and use the Qt library and OpenGL to implement the rendering method that defines OpenGL using the coloring tool, so as to present the mixed rendering effect for everyone.
Original article, opposed to unstated reference. Original blog address: http://blog.csdn.net/gamesdev/article/details/38024327
This article is too difficult and suitable for experienced Qt developers to learn and communicate with each other.
Demo program: here
Source code: here
The demo program is as follows (Android ):
First, let's look at the simple QML code. This example is very simple. There is only one interface and there is no interface jump. We will display a rectangle in front of it, with "Hello World!" written on it !" Text, followed by a rotating rectangle. According to the regulations, the first display content is displayed at the bottom layer, so we put the Cube in front and the Rectangle in the back.
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. w Idth * 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: "Hello world! "Font. pixelSize: root. width/20 }}}
We found that the Cube class is not built-in with Qt Quick, but a custom QML module OpenGLCube. According to the above method in the sixth article, we registered the QML class in C ++ to enable QML to access the C ++ code. The implementation of the main function is as follows:
#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( );}
The main function registers a QML class to the QML environment through the qmlRegisterType function. The next step is the definition and implementation of the Cube class.
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
In Cube. h, let Cube inherit QQuickItem. Because Cube is also a display object of Qt Quick. By the way, the QQuickItem of C ++ corresponds to the Item class of QML, while the QObject of C ++ corresponds to the QtObject class of QML. In C ++, QQuickItem inherits from QObject, and in QML, Item inherits from QtObject. In the class definition, I used QOpenGLBuffer to maintain various drawing caches (buffers) and QOpenGLShaderProgram to easily load the shader data. Finally, I used a convenient macro to define the member variables controlled by the QML attribute system. When these variables change, let them notify the parent window (QQuickWindow) for updates.
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) {// initialize 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 ); // motion trim (); m_ModelViewMatrix.translate (0.0f, 0.0f,-601_f); m_ModelViewMatrix.rotate (m_RotateAngle, m_Axis.x (), m_Axis.y (), m_Axis.z ()); // render glViewport (0, 0, window ()-> width (), window ()-> height (); glClearColor (0.0f, 0.0f, 0.0f, 1.0f ); glClear (distinct | distinct); glable (GL_DEPTH_TEST); glable (GL_CULL_FACE); glFrontFace (GL_CW); m_ShaderProgram.bind (); Round (); int posLoc = trim ("position "); m_ShaderProgram.enableAttributeArray (posLoc); m_ShaderProgram.setAttributeBuffer (posLoc, // location GL_FLOAT, // Type 0, // offset 3, // element size 0); // Mai m_ColorBuffer.bind (); int colorLoc = m_ShaderProgram.attributeLocation ("color"); reverse (colorLoc); m_ShaderProgram.setAttributeBuffer (colorLoc, // location GL_FLOAT, // Type 0, // offset 4, // element size 0); // Mai m_IndexBuffer.bind (); values ("modelViewMatrix", m_ModelViewMatrix); values ("projectionMatrix", m_ProjectionMatrix); glDrawElements (GL_TRIANGLES, 36, expires, q_NULLPTR); iterator (posLoc); iterator (colorLoc); m_IndexBuffer.release (); m_VertexBuffer.release (); m_ShaderProgram.release ();} bool Cube: RunOnce (void) {// initialize the shader m_ShaderProgram.addShaderFromSourceFile (QOpenGLShader: Vertex, ":/Shader/shader. vsh "); m_ShaderProgram.addShaderFromSourceFile (QOpenGLShader: Fragment,":/shader/Shader. sh "); m_ShaderProgram.link (); // initialize the vertex cache const GLfloat length = 10.0f; const GLfloat vertices [] = {length,-length,-length, -length, length, -length,-length, length, length}; buffers (QOpenGLBuffer: StaticDraw); m_VertexBuffer.create (); m_VertexBuffer.bind (); m_VertexBuffer.allocate (vertices, sizeof )); // initialize color cache const GLfloat colors [] = {1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.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, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; initialize (QOpenGLBuffer: StaticDraw); m_ColorBuffer.create (); m_ColorBuffer.bind (); initialize (colors, sizeof (colors); // initialize the index cache GLubyte indices [] = {0, 1, 2, 0, 2, 3, // The following 7, 6, 4, 6, 5, 4, // above 7, 4, 3, 4, 0, 3, // 5, 6, 1, 6, 2, 1 on the left, // 4, 5, 0, 5, 1, 0 on the right, // front 3, 2, 6, 3, 6, 7, // back}; reverse (QOpenGLBuffer: StaticDraw); m_IndexBuffer.create (); m_IndexBuffer.bind (); m_IndexBuffer.allocate (indices, sizeof (indices )); // set the model matrix and projection matrix float aspectRatio = float (window ()-> width ()/float (window ()-> height (); m_ProjectionMatrix.perspective (45.0f, aspectRatio, 0.5f, 500366f); 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 ();}
Class implementation is complicated. It can be divided into the construction phase, initialization phase, rendering phase, and space release phase. Here we use the buffer + attribute array method commonly used by OpenGL ES 2.0 for efficient rendering. About OpenGL, if you are interested, you can refer to OpenGL ES 2.0 Programming Guide, Qt books about OpenGL, KDAB blogs about OpenGL, and other blogs to obtain relevant knowledge.
The above program loads the vertex and part pasters. They are as follows:
// Shader.vshattribute 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.fshvarying highp vec4 v_Color;void main( void ){ gl_FragColor = v_Color;}
This example runs normally on the three major desktop platforms and runs smoothly on the Android platform.