Qt Quick 3D:簡単・高速な新しい3D API

Technical Vision for Qt 6 blog postの記事の中でCTOのLarsが述べたように、私たちはQt Quickの中で3D表現を実現するより良い方法の研究を続けてきました。その結果、Qt Quick 3Dという新しいプロジェクトを立ち上げるに至りました。Qt Quick 3Dは、Qt Quickの中でユーザーインターフェイス用の3Dコンテンツを作成するための、ハイレベルなAPIを提供します。

アニメーションの同期の不具合やソフトウェアの複雑化につながりうる外部エンジンを使うのではなく、Qt Quick シーングラフ自体の3Dコンテンツ向け拡張および拡張されたシーングラフノードの描画エンジンを提供しています。

これは、Qtにこれまでとは異なる新しい3Dソリューションを作ったということなのでしょうか?

いいえ、実は必ずしもそういうわけではありません。コアとなる3D描画エンジンはQt 3D Studioの描画エンジンから派生したものだからです。この描画エンジンは、Qtのマルチプラットフォーム対応のために移植され、Qtプロジェクトのコーディングスタイルに合わせてリファクタリングされています。

Complex 3D scene visualized with Qt Quick 3D

Qt Quick 3Dで描画された”San Miguel”テストシーン

新しい3Dソリューションを提供する五つの理由

2D/3Dグラフィックスの一体化

究極の目標は2D/3Dにかかわらずシームレスな描画を実現することです。現在Qtでは、2Dおよび3Dのそれぞれにスムースなユーザーインターフェイスを作成するための異なるソリューションを提供しており、それぞれに対応するツールがあります。その一つが2D用のQt Quickで、もう一つが3D用のQt 3D Studioです。
このどちらか一方しか使用しないのであれば、全く問題はありません。しかし、これらを併用した場合、実行時のパフォーマンスや開発効率に問題があることがわかりました。

そのため、私達は一つの言語(Qt Quick)、一つの描画エンジン(Qt Quick Scenegraph)、一つのデザインツール(Qt Design Studio)というシンプルなソリューションを目標にすることにしました。これにより、機能やパフォーマンスや開発効率といった問題をすべて解決することができます。さらに私たちの開発リソースを集中することができるので、より多くの機能やより早いバグ修正も実現可能になります。

直感的で簡単なAPI

Qt Quick 3Dのもう一つの目標は、コンピュータグラフィックスの細かな知識がなくても3Dコンテンツを定義できるAPIを提供することです。ほとんどの場合、アプリケーションごとに3D描画エンジンの細かな調整は必要はなく、どちらかというと2Dと並べてちょっとした3Dコンテンツを表示させたいだけであったりします。私たちは、このことを念頭に置いて、Qt Quick 3Dを開発してきました。

とはいえ、パワーユーザーの要望にも応えるべく、より高度なケースに使えるレンダリングAPIを更に公開していく予定でもあります。

現時点では、QML APIのみを提供していますが、将来的にはC++ APIを公開することを目標としています。

Qt Quick開発ツールの統合

Qt Quick 3DはQt 3D Studioの後継として企画されています。当面の間、Qt 3D Studioは引き続き開発が行われますが、長期的にはQt QuickとQt Design Studioに置き換えられることになります。

その際には、Qt 3D Studioの優れた部分を抜き出し、Qt QuickおよびQt Design Studioに組み込む予定です。そのため、Qt Quickまたは3D用に別々のツールを必要とするのではなく、Qt Design Studio内で両方を扱うことができるようになります。
現在、この実装に取り組んでおり、近日中にプレビューを公開する予定です。

Qt 3D Studioの既存のユーザー向けには、プロジェクトをQt Quick 3Dに変換するための移植ツール開発に取り組んでいます。それについてはまた後程お伝え出来ればと思います。

自動アセット最適化

3Dシーンを扱う場合、使用されるアセットの種類が増え、全体的に巨大になる傾向があるため、アセットの最適化がより重要になります。

そのため、Qt Quick 3D開発作業の一環として、コンテンツをできるだけ簡単にインポートし、Qt Quickで効率的に扱える形式にする方法を検討してきました。

たとえば、設計時には、アセット作成ツールが生成するものに基づいて使用するアセットを指定する必要があります(3Dモデルの場合はMayaのFBXファイル、テクスチャの場合はPhotoshopのPSDファイルなど)。しかし、実行時にこれらの形式を使用することは望ましくありません。

代わりに、アセットを実行時に効率的な形式に変換し、ソースアセットが変更されるたびに更新するようにします。これを可能な限り自動化して、これをビルドシステムおよびQtのツールに組み込みたいと考えています。

クロスプラットフォームでの性能・互換性向上

また別の目標としては、Qtに追加される新しいRendering Hardware Interface (QtRHI) を使用し、複数のネイティブグラフィックスAPIをサポートするというものもあります。
現在、Qt Quick 3Dは、Qtの他の多くのコンポーネントと同様に、OpenGLを使用したレンダリングのみをサポートしていますが、Qt 6ではQtRHIを使用し、OpenGLに加えてVulkan、Metal、Direct3Dを介したレンダリングもサポートできるようになります。

Qt Quick 3Dとは?

Qt Quick 3DはQt 3Dを置き換えるものではなく、より簡単なAPIを使用して3Dコンテンツを描画するためのQt Quickの機能拡張です。

非常にシンプルなプロジェクトだと、下記のようになります。

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick3D 1.0
  
Window {
  id: window
  visible: true
  width: 1280
  height: 720

  // Viewport for 3D content
  View3D {
    id: view

    anchors.fill: parent
    // Scene to view
    Node {
      id: scene

      Light {

        id: directionalLight

      }

      Camera {
        id: camera
        // It's important that your camera is not inside
        // your model so move it back along the z axis
        // The Camera is implicitly facing up the z axis,
        // so we should be looking towards (0, 0, 0)
        z: -600
      }

      Model {
        id: cubeModel
        // #Cube is one of the "built-in" primitive meshes
        // Other Options are:
        // #Cone, #Sphere, #Cylinder, #Rectangle
        source: "#Cube"

        // When using a Model, it is not enough to have a
        // mesh source (ie "#Cube")
        // You also need to define what material to shade
        // the mesh with. A Model can be built up of
        // multiple sub-meshes, so each mesh needs its own
        // material. Materials are defined in an array,
        // and order reflects which mesh to shade

        // All of the default primitive meshes contain one
        // sub-mesh, so you only need 1 material.

        materials: [

          DefaultMaterial {

            // We are using the DefaultMaterial which
            // dynamically generates a shader based on what
            // properties are set. This means you don't
            // need to write any shader code yourself.
            // In this case we just want the cube to have
            // a red diffuse color.
            id: cubeMaterial
            diffuseColor: "red"
          }
        ]
      }
    }
  }
}

 

3Dコンテンツの定義は、2Dと同じくらい簡単であるべきではないでしょうか。
ライトやカメラ、マテリアルなど、追加で考慮する必要があるものはいくつかありますが、それらは全てグラフィックパイプラインの実装の詳細ではなく、ハイレベルのシーンの概念です。

もちろん、APIをシンプルにすることは、カスタマイズ性を少し犠牲にすることでもあります。
Qt Quick 3Dではマテリアルやシーンのコンテンツをカスタマイズすることは可能ですが、Qt 3Dの完全にカスタマイズ可能なフレームグラフとは異なり、シーンのレンダリング方法を完全にカスタマイズすることはできません。

その代わりに、一般的なフォワードレンダラーがあり、シーンの中の様々なプロパティを使用してレンダリング方法を定義できます。

これは、他の既存のエンジンと同様で、通常、選択可能なレンダリングパイプラインがいくつかあり、それらがロジカルシーンをレンダリングします。

Camera Orbiting Car

軸とグリッド線を備えたスカイボックス中の車のモデルの周りを周回するカメラ

Qt Quick 3Dで出来ること

Qt Quick 3Dでは、以下の6つの要素の組み合わせで様々なシーンを実現することができます。

Node

Nodeは、3Dシーンの基本部品です。 3D空間での座標変換を表しますが、目には見えません。 これは、Qt QuickでのItemタイプの動作と同様に機能します。

Camera

Cameraは、シーンがどのように2Dの面へ投影されるのかを表します。Cameraには3D空間中の位置および投影方法を定義します。シーンをレンダリングするには、少なくとも1つのカメラが必要です。

Light

Lightは、シーン内の照明を定義します。現在、ディレクショナル(デフォルト)、ポイント、エリアの3種類のライトがあります。

Model

Modelは、シーン内の物体を表します。ジオメトリ(メッシュ)と1つ以上のマテリアルの組み合わせで定義されます。

ModelのSourceプロパティには、Qt Quick 3Dで使用されるランタイム形式である.meshファイルが必要です。meshファイルは、アセットインポートツールを使用して3Dモデルを変換することで生成することができます。また、いくつかの基本図形が組み込まれていて、Sourceプロパティに次の値を設定することで使うことができます:#Cube、#Cylinder、#Sphere、#Cone、#Rectangle

実行時に独自のジオメトリを定義できるプログラマブルな方法も追加する予定ですが、プレビューではまだ利用できません。

モデルをレンダリングする前に、マテリアルも必要です。これは、メッシュのシェーディング方法を定義します。

DefaultMaterial and Custom Materials

DefaultMaterialは、使いやすい標準のマテリアルです。必要なのは、このマテリアルを記述し、定義したいプロパティを設定するだけです。すると、必要なシェーダーコードはすべて自動的に生成されます。
シーンに設定した他の全てのプロパティも考慮されます。グラフィックスシェーダーコード(頂点シェーダーやフラグメントシェーダーなど)を自分で記述する必要はありません。

独自のシェーダーコードを使用する、いわゆるCustomMaterialを定義することもできます。また、QMLに以下のライブラリのimportを追加すれば、様々なCustomMaterialの例を試すこともできます。

import QtQuick3D.MaterialLibrary 1.0

Texture

Textureは、3Dシーン内のテクスチャおよびメッシュへのマッピング方法を表します。テクスチャのソースは、画像ファイルを指定することができますし、さらに後述するようにQMLを指定することもできます。

利用可能な機能の例

Qt Quick内での3Dの表示

Qt Quick内の3Dコンテンツを表示するには、2D面上で平らに表現する必要があります。これを行うため、View3Dを使用します。
View3Dは、API全体で唯一のQQuickItemベースのコンポーネントです。シーンをView3Dの子要素として定義することが出来、又はsceneプロパティをレンダラーにしたいシーンのルートノードに設定して、既存のシーンを参照することも可能です。

複数のカメラがある場合は、シーンのレンダリングに使用するカメラを選ぶこともできます。デフォルトでは、シーンで定義された最初のアクティブなカメラを使用することになっています。

また、View3Dアイテムは、レンダリングされる前に必ずしもオフスクリーンテクスチャにレンダリングされる必要はないということを覚えておいてください。

以下の4つのレンダリングモードの中からいずれかを設定して、3Dコンテンツをいつレンダリングするかを定義できます。

  1. テクスチャ:View3DはQt Quickで使うテクスチャを生成するもので、FBOを介してコンテンツをテクスチャにレンダリングします
  2. アンダーレイ:View3Dは、Qt Quickの2Dコンテンツがウィンドウに直接レンダリングされる前にレンダリングされます(3Dは常に2Dの下にあります)
  3. オーバーレイ:View3Dは、Qt Quickの2Dコンテンツがウィンドウに直接レンダリングされた後にレンダリングされます(3Dは常に2Dの上にあります)
  4. レンダーノード:View3DはQt Quick 2Dコンテンツと同じ空間の中にレンダリングされます。ただし、Qt Quick 2DがQt 5で深度バッファーを使用するために、想定外の動作につながる可能性があります。

 

3Dシーン中での2D QMLの表現

3Dシーン内でQt Quickコンテンツをレンダリングすることもできます。通常Textureはsourceプロパティに画像ファイルを設定して使いますが、代わりにsourceItemプロパティにQt Quick itemを設定することで、Textureを指定可能なすべてのプロパティ(例えばDefaultMaterialのdiffuseMapなど)において、画像ファイルではなく、設定されたQt Quickコンテンツをテクスチャとしてシーンを描画することが可能になります。

animated_cubes

動的なQt Quick 2D itemをテクスチャとしてマップした例

3D QML Components

Qt Quick 3DはQML上に構築されているため、3D用にも再利用可能なコンポーネントを作成することができます。たとえば、複数のモデルで構成されるCarモデルを作成する場合は、Car.qmlに保存するだけです。他のQMLタイプと同様、Carを再利用するだけで、複数のCarをインスタンス化できます。これは、2Dシーンと3Dシーンの異なるアプローチを処理するのではなく、同じコンポーネントモデルを使用して2Dシーンと3Dシーンを作成できるため、非常に重要です。

単一シーン複数視点

シーン定義はQt Quickプロジェクト内のどこにでも存在できるため、複数のView3Dからそれらを参照することができます。シーンに複数のカメラがある場合、各カメラから異なるView3Dにレンダリングすることもできます。

teapots

4視点からみた同じティーポット

影の表現

それぞれのLightについて影の有無を指定できます。これを有効にすると、影がシーンに自動でレンダリングされます。ただし、条件にもよりますが、影のレンダリングは非常に高コストになる可能性があるため、各モデルに追加のプロパティを設定することで、どのモデルコンポーネントが影をつくるか、あるいは投影されるかを微調整できます。

Image Based Lighting

標準のLightコンポーネントに加えて、定義されたHDRIマップでシーンを照らすこともできます。このテクスチャは、View3D全体を対象にする場合はSceneEnvironmentプロパティで設定できますし、個別にそれぞれのマテリアルにおいて設定することも可能です。

Animations

Qt Quick 3Dのアニメーションは、Qt Quickと同じアニメーションシステムを使用します。任意のプロパティをアニメーターにバインドすると、期待どおりに変化し、更新されます。 QtQuickTimelineモジュールを使用すると、キーフレームベースのアニメーションを使用することもできます。

コンポーネントモデルと同様に、アニメーションシステムを統一することで、2D/3D間のギャップを小さくし、相互の分断や矛盾を根本的に解決することができます。

なお、今のところリグアニメーションには対応していませんが、将来的には対応する予定です。

実際に使ってみるには?

Qt 5.14のリリースとともにQt Quick 3Dをテクニカルプレビューとしてリリースすることを計画しています。ソースコードはQtレポジトリ中で公開されているので、https://wiki.qt.io/Building_Qt_5_from_Gitを参考に5.14ブランチをチェックアウトしてビルドしてみてください。

ツールサポートについて

Qt Design Studioを使うことで、3Dシーンを表現するために必要な全てが手に入る、というところが、このプロジェクトにとってのゴールです。

つまりそれは、シーンを視覚的にレイアウトし、メッシュ、マテリアル、テクスチャなどの3Dアセットをインポートし、それらのアセットをエンジンで使用される効率的なランタイム形式に変換することを意味します。

designstudio3d開発中のQt Quick 3DのQt Design Studio統合のデモ

3DシーンのQMLへのインポート

Qt Quick 3Dは、QMLコードを手動で記述することでも使用できます。したがって、アセットを変換するための独立したユーティリティもいくつかあります。そのうちの一つが、”balsam”とよばれるアセット変換ツールです。

現在、このツールにBlender、Maya、または3DS Maxなどの3Dアセット作成ツールで作成したアセットを入力すると、シーンを表すQMLコンポーネントと、使用するテクスチャ、メッシュ、マテリアルを生成します。現在、このツールは次の形式からのシーンの生成をサポートしています。

  • FBX
  • Collada (dae)
  • OBJ
  • Blender (blend)
  • GLTF2

ファイルmyTestScene.fbxを変換するには、次のように実行してください。

./balsam -o ~/exportDirectory myTestScene.fbx

これにより、必要なアセットとともにMyTestScene.qmlというファイルが生成されます。そうすることで、シーン内の他のコンポーネントと同じように使用できます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick3D 1.0
 
Window {
  width: 1920
  height: 1080
  visible: true
  color: "black"
 
  Node {
    id: sceneRoot
    Light {
    }
    Camera {
    z: -100
    }
    MyTestScene {
    }
  }
 
  View3D {
    anchors.fill: parent
    scene: sceneRoot
  }
}

このツールで生成されるアセットの改善に取り組んでいるところです。今後数か月で改善されることを期待しています。

Qt 3D Studioで作成したプロジェクトの変換

3Dアセット作成ツールから3D QMLコンポーネントを生成できることに加えて、既存のQt 3D Studioプロジェクトを変換するアセットインポートツール用のプラグインも作成しました。既にQt 3D Studioを使用したことがある場合、そのプロジェクトはXML形式で保存されています。 balsamツールにQt 3D Studioで生成されたUIPまたはUIAプロジェクトを与えると、それに基づいたQt Quick 3Dプロジェクトが生成されます。

ただし、Qt 3D Studioで使用されるランタイムはQt Quick 3Dとは異なるため、全てが変換されるわけではありません。ただ、既存のプロジェクトを変換するための適切な開始点を提供する必要はあります。既存のQt 3D Studioユーザーのために、このサポートを引き続き改善していきたいと考えています。

qt3dstudio_sampleQt Quick 3Dのインポートツールを使用して移植されたQt 3D Studioサンプルアプリケーション (まだ未完成ではありますが)

Qt 3Dについて

なぜQt 3Dを使用しないのか?
これが恐らく、真っ先に浮かぶ疑問でしょう。私達もこれについて、ここ数年間検討を重ねてきました。

まず一つ目に想定できることとしては、2Dと3Dを混在させたい場合、Qt 3Dの一番上にQt Quickのすべてを構築できるのでは、ということです。Qt 3D Studioの2.3リリースでこれを行うことを目指し、開発を開始しました。 Qt 3Dには、Qt QuickおよびQt 3D Studioで期待される動作を再現するレンダリングエンジンを実装するための強力な抽象化されたAPIがあります。
しかし、Qt 3Dのアーキテクチャでは、エントリレベルの組み込みハードウェアで必要なパフォーマンスを得ることを難しく、また、Qt 3Dは、それ自体の限られたランタイムからだけではなく、Qt Quickとグラフィックスハードウェア間の別のレベルの抽象化からも、特定のオーバーヘッドが伴います。
そのため、ローエンドのデバイスも含む幅広いプラットフォームのサポートを継続しながら、グラフィックバックエンドの統合をしようとしたときに、現状のQt3Dはその基盤として最適ではないのです。

それと同時に、Qt 3D Studioには私たちがちょうど必要としていた機能を備えたレンダリングエンジンがあり、追加の機能を構築するための良い基盤となりました。これには、Qt 3Dに付属する強力なAPIがなくなったという欠点がありますが、もしQt3D上で描画エンジンを実際に構築しようとすると、その挙動を最初に決めてしまわなければならないので、フレームグラフのカスタマイズ性はすでに制限されてしまうのです。
最終的にたどり着いた結論としては、既存のQt 3D Studioレンダリングエンジンをベースとして使用し、それを基に構築するのが一番実用的だということでした。

今後の展望

このリリースは、今後にむけた予告になります。Qt Quick 3Dの完全なサポートはQt 5.15 LTSから提供する計画です。それまでの間、さらにQt Quick 3Dの開発に取り組み、Qt 5.14でテックプレビューをリリースします。

Qt 5シリーズでは、バイナリ互換を維持しなければならないため、2Dと3Dの結合の深さは制限されています。 Qt 6のリリースでは、さらにスムーズな挙動を実現するために、Qt Quick 3DをQt Quickにより深く結合させることを目指しています。

ここでの目標は、3Dを全く使用しないユーザーにオーバーヘッドを追加することなく、2Dコンテンツと3Dコンテンツの混合を可能な限り効率化することです。すべてのQt Quickアプリに新しいレンダラーを強制追加させるようなことは行いません。

Qt 6では、Qt Rendering Hardware Interfaceを使用して3Dを含むQt Quickシーンをレンダリングし、OpenGLアプリケーションをデプロイする際に起こる問題の多くを排除します(WindowsではDirectX、macOSではMetalなどを使用)

また、エンドユーザーが、Qt Quickだけでなく、より一般的に使うことのできるC ++ Rendering APIを利用できるようにしたいと考えています。現在、コードはプライベートAPIとしてすでに存在しますが、Qt 6(およびRHI移植)の進捗をみながら、そのバイナリ互換保証をする前に、パブリックなAPIとして公開します。

Feedback is Very Welcome!

Qt Quick 3Dは現在テックプレビューであるため、現状の多くは変更の可能性があります。例えば、API周りに関しては荒削りな部分も多いので、不足しているもの、意味をなさないもの、機能するもの、しないものもあることと思います。
それらについて、Qt Bug Trackerを通してフィードバックをいただけると幸いです。
バグ報告や提案を作成する際には、Componentの項目にQt Quick:3Dと指定してください。


Blog Topics:

Comments