Qt Canvas 3D是Qt 基于WebGL的3D内容运行环境。由于QtQuick本身就是通过类js引擎执行,而且渲染层面基于opengl技术。故结合webgL和Qtquick的优势,利用js高效的特点,给Qtquick增加了3d功能。而且Qt Canvas 3D还可以利用成熟的web 3d框架,如three.js、gl-matrix.js等,大大方便了利用QtQuick编写3d内容。Qt Canvas 3D是由官方支持,比Qt3D模块较成熟。至于两者性能上的差异尚待对比。
最新版QtCreater支持在创建项目时指定Qt Canvas 3D应用,下面我们通过该方式创建一个默认应用,学习其结构组成。通过QtCreater创建一个使用three.js的应用。
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
从上面可以看出,Canvas 3D应用和普通qml应用对于c++后端要求一致,没有额外内容,由下面qml代码可知,Canvas3D即一个普通的QtQuick Item。
main.qml
import QtQuick 2.4 import QtCanvas3D 1.0 import QtQuick.Window 2.2 import "glcode.js" as GLCode Window { title: qsTr("untitled1") width: 1280 height: 768 visible: true Canvas3D { id: canvas3d anchors.fill: parent focus: true onInitializeGL: { GLCode.initializeGL(canvas3d); } onPaintGL: { GLCode.paintGL(canvas3d); } onResizeGL: { GLCode.resizeGL(canvas3d); } } }
上述代码通过Canvas3D元素定义了3d场景,当场景初始化后会发送 initializeGL 信号,从而执on initializeGL 槽。实际的控制逻辑写在了glcode.js中,通过槽函数将要操作的场景对象canvas3d传给js代码。
glcode.js首先导入three.js,然后实现了main.qml中对应的3个槽对应的函数功能。其语法和普通的three.js应用区别不大,故一个基于html的three.js应用可以很方便的移植到QtQuick中。
The Canvas3D is a QML element that, when placed in your Qt Quick 2 scene, allows you to get a 3D rendering context and call 3D rendering API calls through that context object. Use of the rendering API requires knowledge of OpenGL-like rendering APIs.
There are two functions that are called by the Canvas3D implementation:
initializeGL is emitted before the first frame is rendered, and usually during that you get the 3D context and initialize resources to be used later on during the rendering cycle.
paintGL is emitted for each frame to be rendered, and usually during that you submit 3D rendering calls to draw whatever 3D content you want to be displayed.
逻辑代码
Qt.include("three.js") var camera, scene, renderer; var cube; function initializeGL(canvas) { scene = new THREE.Scene(); //custom begain camera = new THREE.PerspectiveCamera(75, canvas.width / canvas.height, 0.1, 1000); camera.position.z = 5; var material = new THREE.MeshBasicMaterial({ color: 0x80c342, ambient: 0x000000, shading: THREE.SmoothShading }); var cubeGeometry = new THREE.BoxGeometry(1, 1, 1); cube = new THREE.Mesh(cubeGeometry, material); cube.rotation.set(0.785, 0.785, 0.0); scene.add(cube); //custom end renderer = new THREE.Canvas3DRenderer( { canvas: canvas, antialias: true, devicePixelRatio: canvas.devicePixelRatio }); renderer.setSize(canvas.width, canvas.height); } function resizeGL(canvas) { camera.aspect = canvas.width / canvas.height; camera.updateProjectionMatrix(); renderer.setPixelRatio(canvas.devicePixelRatio); renderer.setSize(canvas.width, canvas.height); } function paintGL(canvas) { renderer.render(scene, camera); }
从上述js代码可以看出,resizeGL和paintGL一般默认即可,不需要改动,需要我们编写的是initializeGL中//custom begain 和//custom end 之间的内容。