Qt 3D の概要

Qt 3D は、開発者が必要とするあらゆるレンダリングパイプラインを迅速に実装できる、完全に設定可能なレンダラーを提供します。さらに、Qt 3D はレンダリング以外のニアリアルタイムシミュレーションのための汎用フレームワークを提供します。

Qt 3D は、コアと、任意の機能を実装できる任意の数のアスペクトにきれいに分離されています。アスペクトはコンポーネントや エンティティと相互作用し、機能の一部を提供します。アスペクトの例としては、物理、オーディオ、コリジョン、人工知能(AI)、経路探索などがあります。

3Dの基本機能

Qt 3Dは3Dフレームワークで、3D図形の描画や移動、カメラの移動が可能です。以下の基本機能をサポートしています:

マテリアル

Qt 3Dには、複数のレベルのカスタマイズを可能にする、堅牢で非常に柔軟なマテリアルシステムがあります。異なるプラットフォームやOpenGLバージョンでの異なるレンダリングアプローチに対応し、異なるステートセットで複数のレンダリングパスを可能にし、異なるレベルでパラメータをオーバーライドするメカニズムを提供し、シェーダを簡単に切り替えることができます。これらはすべて、C++またはQMLプロパティバインディングを使用して行います。

Material タイプのプロパティは、参照されるエフェクトプロパティで指定される GLSL シェーダプログラムのユニフォーム変数に簡単にマッピングできます。

マテリアルの使用例については、以下の例を参照してください:

シェーダ

Qt 3D は、OpenGL のプログラム可能なレンダリングパイプラインの全てのステージをサポートしています:頂点シェーダ、テッセレーションコントロール、テッセレーション評価、ジオメトリ、フラグメントシェーダです。コンピュート・シェーダは将来のリリースを予定しています。

シェーダの使用例については、Qt 3D: Wireframe QML Exampleを参照してください。

シャドウマッピング

シャドウはOpenGLでは直接サポートされていませんが、シャドウを生成するためのテクニックは無数にあります。シャドウマッピングは、見栄えの良いシャドウを生成するのに使いやすく、パフォーマンスコストも非常に小さくなります。

シャドウマッピングは通常、2パスレンダリングを使用して実装されます。最初のパスでは、シャドウ情報が生成されます。2番目のパスでは、特定のレンダリング技術を使用してシーンを生成すると同時に、1番目のパスで収集した情報を使用して影を描画します。

シャドウマッピングの背景にある考え方は、光に最も近いフラグメントだけが点灯するというものです。他のフラグメントの後ろにあるフラグメントはオクルードされるため、影になります。

したがって、最初のパスでは、光の視点からシーンを描画します。保存される情報は、単純に、このライト空間で最も近いフラグメントの距離です。OpenGLの用語では、これは、深度テクスチャがアタッチされたフレームバッファオブジェクト(FBO)を持つことに相当します。実際、目からの距離は深度の定義であり、OpenGLによって行われるデフォルトの深度テストは、実際には最も近いフラグメントの深度のみを保存します。

フラグメントに陰影をつける必要はなく、深度を計算するだけなので、カラーテクスチャのアタッチメントも必要ありません。

次の画像は、セルフシャドウの平面と三つ葉の結び目のあるシーンを表示しています:

次の画像は、シーンの誇張されたシャドウマップテクスチャを示しています:

この画像は、光の視点からシーンをレンダリングするときに保存される深度を示しています。暗い色は、浅い深度(つまり、カメラに近い)を表します。このシーンでは、ライトはシーン内のオブジェクトのどこか上、メインカメラに対して右側に配置されています(最初のスクリーンショットと比較してください)。これは、おもちゃの平面が他のオブジェクトよりもカメラに近いという事実と一致します。

シャドウマップが生成されると、2回目のレンダリングパスが行われます。この2回目のパスでは、ノーマルシーンのカメラを使ってレンダリングします。ここでは、フォンシェーディングなど、どのようなエフェクトでも使用できます。シャドウマップアルゴリズムがフラグメントシェーダで適用されることが重要です。つまり、光に最も近いフラグメントは照らされて描画され、それ以外のフラグメントは影になって描画されます。

最初のパスで生成されたシャドウマップは、フラグメントとライトの距離に関する必要な情報を提供します。次に、フラグメントを光空間に再マッピングし、それによって光から見た深さと、シャドウマップテクスチャ上の座標を計算すれば十分です。次に、シャドウマップテクスチャを与えられた座標でサンプリングし、フラグメントの深さをサンプリングの結果と比較することができます。フラグメントがより遠くにある場合は影になり、そうでない場合は照明されます。

インスタンスレンダリング

インスタンス化とは、GPUにベースオブジェクトの多数のコピー(インス タンス)を描画させる方法です。多くの場合、位置、向き、色、マテリアルプロパティ、スケールなどが異なります。Qt 3D は Qt QuickRepeater エレメントに似た API を提供します。この場合、デリゲートがベースオブジェクトで、モデルがインスタンスごとのデータを提供します。そのため、Mesh コンポーネントがアタッチされたエンティティが最終的に glDrawElements の呼び出しに変換されるのに対し、インスタンス化されたコンポーネントを持つエンティティは glDrawElementsInstanced の呼び出しに変換されます。

インスタンス化レンダリングは将来のリリースで予定されています。

ユニフォームバッファオブジェクト

ユニフォーム・バッファ・オブジェクト(UBO)は、OpenGL シェーダ・プログラムにバインドして、大量のデータをすぐに利用できるようにすることができます。UBO の典型的な使用例は、マテリアルまたはライティングパラメータのセットです。

役に立つヒント

3Dレンダリングに役立つプログラミングのヒントは、こちらのページにあります:Qt 3D Render Pro Tips を参照してください。

設定可能なレンダラ

C++ と QML の両方の API のサポートと、完全に設定可能なレンダラーを組み合わせるために、フレームグラフの概念が導入されました。シーングラフが 何をレンダリングするかのデータ駆動型の記述であるのに対し、フレームグラフどのようにレンダリングするかのデータ駆動型の記述です。

フレームグラフによって、開発者は、Zフィルパスを含む単純なフォワード・レンダラーか、ディファード・レンダラーを使用するかなどを選択することができる。また、透過オブジェクトをいつレンダリングするかなどの制御も可能です。これはすべて純粋にデータから設定されるため、C++ コードに触れることなく、実行時に動的に変更することも非常に簡単です。カスタムレンダリングアルゴリズムを実装した独自のフレームグラフを作成することで、Qt 3D を拡張することができます。

3D 拡張機能

Qt 3D は、3D コンテンツを画面に表示するという基本的な機能だけでなく、3D オブジェクトに関連する次のような拡張機能のホストとして機能する拡張性と柔軟性を備えています:

  • 物理シミュレーション
  • 衝突検出
  • 3D 位置オーディオ
  • リジッドボディ、スケルトン、モーフターゲットアニメーション
  • 経路探索とその他のAI
  • ピッキング
  • パーティクル
  • オブジェクトのスポーン

パフォーマンス

Qt 3D は、利用可能な CPU コア数に応じて性能が向上し、スケールアップするように設計されています。多くのタスクは互いに独立しているため、複数のコアを使用することは効果的です。例えば、パス検索モジュールが実行する処理は、デバッグ情報や統計情報をレンダリングする場合を除いて、レンダラーが実行するタスクと強く重なることはありません。

Qt 3D アーキテクチャ

Qt 3D の主な使用例は、ほぼリアルタイムでオブジェクトをシミュレートし、それらのオブジェクトの状態を画面にレンダリングすることです。スペースインベーダーの例では、以下のオブジェクトが含まれています:

  • プレイヤーの地上大砲
  • 地面
  • 防御ブロック
  • 敵のスペースインベーダー船
  • 敵のボスの空飛ぶ円盤
  • 敵やプレイヤーが撃つ弾丸

伝統的なC++の設計では、これらのタイプのオブジェクトは、通常、ある種の継承ツリーに配置されたクラスとして実装されます。継承ツリーのさまざまな分岐によって、ルート・クラスに次のような機能が追加されます:

  • ユーザー入力を受け付ける
  • サウンドを再生する
  • アニメーションする
  • 他のオブジェクトと衝突する
  • 画面に描画される

スペースインベーダーの例にある型は、これらの特徴に対して分類することができる。しかし、このような単純な例でさえ、エレガントな継承ツリーを設計するのは容易ではありません。

このアプローチや他の継承のバリエーションには、多くの問題がある:

  • 深く広い継承階層は、理解、維持、拡張が難しい。
  • 継承の分類法はコンパイル時に決まってしまう。
  • クラス継承ツリーの各レベルは、単一の基準や軸でしか分類できません。
  • 共有機能は、時間の経過とともにクラス階層を上昇する傾向がある。
  • 開発者が何をしたいかを予測することは不可能です。

深く広い継承ツリーを拡張するには、通常、元の作者が使用した分類法を理解し、それに同意する必要があります。そのため、Qt 3D では、オブジェクトのインスタンスに機能を付与する手段として、継承の代わりに集約に重点を置いています。具体的には、Qt 3D はエンティティ・コンポーネント・システム(ECS)を実装しています。

ECSの使用

ECSでは、エンティティはシミュレートされたオブジェクトを表しますが、それ自体には特定の動作や特性はありません。エンティティに1つまたは複数のコンポーネントを集約させることで、エンティティに追加の動作を追加することができます。各コンポーネントは、オブジェクトタイプのビヘイビアの垂直スライスです。

スペースインベーダーの例では、地上は、エンティティにレンダリングが必要であることと、どのようなレンダリングが必要であるかをシステムに伝えるコンポーネントが付加されたエンティティです。敵のスペースインベーダー船は、船をレンダリングさせるだけでなく、音を出したり、衝突させたり、アニメーションさせたり、簡単なAIで操作できるようにするコンポーネントがアタッチされた別のエンティティです。

プレイヤーの地上大砲のエンティティは、AIコンポーネントがないことを除けば、敵のスペースインベーダー船とほぼ同様のコンポーネントを持っています。その代わりに、大砲はプレイヤーが動かして弾を発射するための入力コンポーネントを持っている。

ECSバックエンド

Qt 3Dのバックエンドは、ECSパラダイムのシステム部分をアスペクトという形で実装しています。アスペクトは、1つまたは複数の集約されたコンポーネントの組み合わせによって、エンティティに提供される機能の特定の垂直スライスを実装します。

たとえば、レンダラー アスペクトは、メッシュ、マテリアル、およびオプションでトランスフォーム コンポーネントを持つエンティティを探します。レンダラー アスペクトは、そのようなエンティティを見つけると、そのデータをどのように取り込んで、そこから何かを描画するかを判断します。エンティティがこれらのコンポーネントを持っていない場合、レンダラーはそれを無視します。

Qt 3Dは、追加機能を提供するコンポーネントを集約してカスタムエンティティを構築します。Qt 3Dエンジンは、アスペクトを使用して、特定のコンポーネントを持つエンティティを処理し、更新します。

例えば、物理アスペクトは、ある種の衝突体積コンポーネントと、質量、摩擦係数などのシミュレーションに必要な他のプロパティを指定する別のコンポーネントを持つエンティティを探します。サウンドを発するエンティティには、サウンドエミッターであることを指定するコンポーネントと、いつ、どのサウンドを再生するかを指定するコンポーネントがあります。

ECSは継承ではなく集約を採用しているため、コンポーネントを追加または削除するだけで、実行時にオブジェクトの動作を動的に変更することが可能です。

例えば、パワーアップ後に突然壁を通り抜けて走れるようにするには、パワー アップがタイムアウトするまで、エンティティのコリジョンボリュームコンポーネン トを一時的に削除します。PlayerWhoRunsThroughWalls のために特別なサブクラスを作成する必要はありません。

Qt 3D ECSの実装

Qt 3D は、シンプルなクラス階層として ECS を実装しています。Qt 3D の基本クラスはQt3DCore::QNode で、これはQObject のサブクラスです。Qt3DCore::QNodeQObject に、プロパティの変更をアスペクトに自動的に伝達する機能と、アプリケーション全体で一意である ID を追加しています。アスペクトは追加のスレッドに存在し、Qt3DCore::QNode は、ユーザー向けオブジェクトとアスペクト間のデータ転送を簡素化します。

通常、Qt3DCore::QNode のサブクラスは、コンポーネントによって参照される追加のサポートデータを提供します。たとえば、QShaderProgramクラスは、エンティティのセットをレンダリングするときに使用するGLSLコードを指定します。

Qt 3D のコンポーネントは、Qt3DCore::QComponent をサブクラス化し、対応するアスペクトの動作に必要なデータを追加することで実装されます。例えば、メッシュコンポーネントは、OpenGLパイプラインに送られるべき頂点ごとのデータを取得するために、レンダラアスペクトによって使用されます。

最後に、Qt3DCore::QEntity は、ゼロ個以上のQt3DCore::QComponent インスタンスを集約できるオブジェクトです。

Qt 3D の拡張

Qt 3D に機能を追加するには、Qt の一部として、または独自のアプリケーションに特化して、マルチスレッドのバックエンドの利点を享受するために、以下のタスクがあります:

  • 必要なコンポーネントとサポートデータを特定し、実装する。
  • コンポーネントを QML エンジンに登録する(QML API を使用する場合のみ)。
  • QAbstractAspect をサブクラス化し、サブシステムの機能を実装します。

Qt 3D タスクベースエンジン

Qt 3D では、アスペクトは各フレームで、実行するタスクのセットとそれらの間の依存関係を求められます。タスクは、パフォーマンスを向上させるために、スケジューラによって設定されたすべてのコアに分散されます。

Qt 3D のアスペクト

デフォルトでは、Qt 3D は Qt3DRender と Qt3DInput アスペクトを提供します。これらのアスペクトによって提供されるコンポーネントやその他のサポートクラスについては、これらのモジュールのドキュメントで説明されています。

より多くの機能を提供する追加のアスペクトは、Qt 3Dの将来のバージョンで追加される予定です。

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。