Qtクイック3D入門とglTFアセット

Qt Quick 3D - Introduction のサンプルでは、Qt Quick 3D を使って QML ベースのアプリケーションを作成する方法を簡単に紹介していますが、球や円柱などの組み込みプリミティブのみを使用しています。このページでは、Khronos glTF Sample Models リポジトリにあるモデルのいくつかを使って、glTF 2.0アセットを使った入門を提供します。

スケルトン・アプリケーション

次のアプリケーションから始めましょう。このコードスニペットは、qml コマンドラインツールでそのまま実行できます。何もない緑色の3Dビューができあがります。

import QtQuick
import QtQuick3D
import QtQuick3D.Helpers

Item {
    width: 1280
    height: 720

    View3D {
        anchors.fill: parent

        environment: SceneEnvironment {
            backgroundMode: SceneEnvironment.Color
            clearColor: "green"
        }

        PerspectiveCamera {
            id: camera
        }

        WasdController {
            controlledObject: camera
        }
    }
}

アセットのインポート

Sample Modelsリポジトリから2つのglTF 2.0モデルを使用します:SponzaとSuzanneです。

SponzaとSuzanneです。これらのモデルは通常、.gltfファイルに加えて、多くのテクスチャマップとメッシュ(ジオメトリ)データが別のバイナリファイルに保存されています:

これをQt Quick 3Dシーンに取り込むにはどうすればいいでしょうか?

いくつかのオプションがあります:

  • シーンでインスタンス化できるQMLコンポーネントを生成する。この変換を行うコマンドラインツールがBalsamツールです。.qmlファイル(実質的にサブシーン)を生成するだけでなく、メッシュ(ジオメトリ)データを最適化された高速ロード可能なフォーマットに再パックし、テクスチャマップ画像ファイルもコピーします。
  • BalsamのGUIフロントエンドであるbalsamui
  • Qt Design Studio を使用する場合、アセットのインポート処理はビジュアルデザインツールに統合されます。インポートは、例えば、.gltf ファイルを適切なパネルにドラッグ&ドロップすることでトリガーできます。
  • 特に glTF 2.0 アセットの場合、RuntimeLoader タイプというランタイムオプションもあります。これは、.gltfファイル(および関連するバイナリとテクスチャデータファイル)を、Balsamのようなツールで前処理をすることなく、ランタイムで読み込むことを可能にします。これは、ユーザーが提供したアセットを開いてロードしたいアプリケーションで非常に便利です。一方、このアプローチはパフォーマンスに関してはかなり効率が悪いです。そのため、この紹介ではこのアプローチには焦点を当てません。このアプローチの例については、Qt Quick 3D - RuntimeLoader Exampleを参照してください。

balsambalsamui の両アプリケーションは Qt に同梱されており、Qt Quick 3D がインストールまたはビルドされていることを前提に、他の同様の実行可能ツールと一緒にディレクトリに存在するはずです。多くの場合、コマンドラインから.gltfファイルに対してbalsamを実行するだけで、引数を指定する必要はありません。しかし、コマンドラインや、balsamui または Qt Design Studio を使用している場合はインタラクティブなオプションが多数あることに注意する必要があります。例えば、静的なグローバルイルミネーションを提供するためにベイクドライトマップを使用する場合、実行時に消費する可能性のある処理を実行する代わりに、アセットインポート時に生成された追加のライトマップUVチャンネルを取得するために、--generateLightmapUV 。同様に、--generateMeshLevelsOfDetail は、シーンで自動 LOD を有効にするために、メッシュの単純化されたバージョンを生成することが望ましい場合に不可欠です。その他のオプションにより、欠損データの生成(例:--generateNormals )やさまざまな最適化を行うことができます。

balsamui では、コマンドラインオプションはインタラクティブな要素にマッピングされています:

バルサム経由でインポートする

さっそく始めましょう!https://github.com/KhronosGroup/glTF-Sample-Modelsgit リポジトリがどこかにチェックアウトされていると仮定すると、.gltf ファイルへの絶対パスを指定して、サンプルアプリケーションのディレクトリから balsam を実行するだけです:

balsam c:\work\glTF-Sample-Models\2.0\Sponza\glTF\Sponza.gltf

これで、Sponza.qmlmeshes サブディレクトリの下の.mesh ファイル、maps の下にコピーされたテクスチャマップが得られます。

注意: このqmlファイルは単体では実行できません。これはコンポーネントであり、View3D に関連付けられた3Dシーン内でインスタンス化されるべきものです。

アセットqmlファイルはメインの.qmlシーンのすぐ隣にあるので、プロジェクトの構造はとてもシンプルです。これにより、標準的なQMLコンポーネントシステムを使用して、Sponzaタイプをインスタンス化することができます。(実行時に、ファイルシステム内のSponza.qmlを探します)

しかし、モデル(サブシーン)を追加するだけでは意味がありません。デフォルトでは、マテリアルは完全なPBRライティング計算を備えているため、DirectionalLightPointLightSpotLight などのライトを使用するか、the environment を使用して画像ベースのライティングを有効にしないと、シーンからは何も表示されません。

とりあえず、デフォルト設定でDirectionalLight を追加することにします。(つまり、色はwhite で、光はZ軸の方向に照射されます)。

import QtQuick
import QtQuick3D
import QtQuick3D.Helpers

Item {
    width: 1280
    height: 720

    View3D {
        anchors.fill: parent

        environment: SceneEnvironment {
            backgroundMode: SceneEnvironment.Color
            clearColor: "green"
        }

        PerspectiveCamera {
            id: camera
        }

        DirectionalLight {
        }

        Sponza {
        }

        WasdController {
            controlledObject: camera
        }
    }
}

qml ツールでこれを実行すると、ロードして実行されますが、スポンザモデルがカメラの後ろにあるため、デフォルトではシーンはすべて空です。また、スケールも理想的ではありません。例えば、WASDキーやマウス(WasdController で有効)で移動しても、しっくりきません。

これを改善するために、スポンザ・モデル(サブシーン)をX、Y、Z軸に沿って100 。さらに、カメラの開始Y位置を100にバンプします。

import QtQuick
import QtQuick3D
import QtQuick3D.Helpers

Item {
    width: 1280
    height: 720

    View3D {
        anchors.fill: parent

        environment: SceneEnvironment {
            backgroundMode: SceneEnvironment.Color
            clearColor: "green"
        }

        PerspectiveCamera {
            id: camera
            y: 100
        }

        DirectionalLight {
        }

        Sponza {
            scale: Qt.vector3d(100, 100, 100)
        }

        WasdController {
            controlledObject: camera
        }
    }
}

これを実行すると

マウスとWASDRFキーで移動できる:

注: "model "の代わりにsubscene 。これはなぜか?Sponza アセットは、glTF 形式では、103 個のサブメッシュを持つ 1 つのモデルであり、そのmaterials list 内に 103 個の要素を持つ 1 つのModel オブジェクトにマッピングされますが、アセットには、複数のサブメッシュと関連マテリアルを持つmodels をいくつでも含めることができます。これらのモデルは親子関係を形成することができ、トランスレート、ローテート、スケールなどのトランスフォームを実行するために、追加のnodes と組み合わせることができます。したがって、インポートされたアセット は、レンダリング結果が単一のモデルとして視覚的に認識されたとしても、完全なサブシーン、nodes の任意のツリーとして見る方が適切です。生成されたSponza.qmlや、そのようなアセットから生成された他のQMLファイルをプレーンテキストエディタで開くと、構造の印象を得ることができます(当然ながら、ソースアセット(この場合はglTFファイル)がどのように設計されたかに常に依存します)。

balsamui経由でインポートする

つ目のモデルには、代わりにbalsam のグラフィカルユーザーインターフェースを使いましょう。

balsamui を実行するとツールが開きます:

Suzanneモデルをインポートしましょう。これは2つのテクスチャマップを持つ、よりシンプルなモデルです。

追加の設定オプションは必要ないので、Convert(変換)するだけです。結果はbalsam を実行したのと同じです。Suzanne.qmlと、特定の出力ディレクトリに生成されたいくつかの追加ファイルです。

この時点から、生成されたアセットでの作業は前のセクションと同じです。

import QtQuick
import QtQuick3D
import QtQuick3D.Helpers

Item {
    width: 1280
    height: 720

    View3D {
        anchors.fill: parent

        environment: SceneEnvironment {
            backgroundMode: SceneEnvironment.Color
            clearColor: "green"
        }

        PerspectiveCamera {
            id: camera
            y: 100
        }

        DirectionalLight {
        }

        Sponza {
            scale: Qt.vector3d(100, 100, 100)
        }

        Suzanne {
            y: 100
            scale: Qt.vector3d(50, 50, 50)
            eulerRotation.y: -90
        }

        WasdController {
            controlledObject: camera
        }
    }
}

ここでも、インスタンス化されたSuzanneノードにスケールが適用され、モデルがSponzaビルの床に入らないようにY位置が少し変更されます。

Qt Quickと同じように、すべてのプロパティを変更したり、バインドしたり、アニメーションさせたりすることができます。例えば、Suzanneモデルに連続回転を適用してみましょう:

Suzanne {
    y: 100
    scale: Qt.vector3d(50, 50, 50)
    NumberAnimation on eulerRotation.y {
        from: 0
        to: 360
        duration: 3000
        loops: Animation.Infinite
    }
}

見栄えを良くする

もっと明るく

さて、このシーンは少し暗いですね。もう1つライトを追加しましょう。今度はPointLight 、影を落とすものです。

import QtQuick
import QtQuick3D
import QtQuick3D.Helpers

Item {
    width: 1280
    height: 720

    View3D {
        anchors.fill: parent

        environment: SceneEnvironment {
            backgroundMode: SceneEnvironment.Color
            clearColor: "green"
        }

        PerspectiveCamera {
            id: camera
            y: 100
        }

        DirectionalLight {
        }

        Sponza {
            scale: Qt.vector3d(100, 100, 100)
        }

        PointLight {
            y: 200
            color: "#d9c62b"
            brightness: 5
            castsShadow: true
            shadowFactor: 75
        }

        Suzanne {
            y: 100
            scale: Qt.vector3d(50, 50, 50)
            NumberAnimation on eulerRotation.y {
                from: 0
                to: 360
                duration: 3000
                loops: Animation.Infinite
            }
        }

        WasdController {
            controlledObject: camera
        }
    }
}

このシーンを起動し、カメラを少し動かしてみると、確かに以前より見栄えが良くなっていることがわかります:

ライトのデバッグ

PointLight は、Suzanne モデルの少し上に配置されています。Qt Design Studio などのビジュアルツールを使ってシーンをデザインする場合、これは明らかですが、デザインツールを使わずに開発する場合、lights や他のnodes の位置を素早く視覚化できると便利です。

これは、PointLight に子ノードであるModel を追加することで実現できます。子ノードの位置は親ノードからの相対位置なので、この場合、デフォルトの(0, 0, 0) は実質的にPointLight の位置になります。このシステムにはオクルージョンの概念がないため、標準的なリアルタイムのライティング計算では、あるジオメトリ(この場合はビルトインキューブ)内に光を囲むことは問題ではありません。レイトレーシングを使用してライティングが計算される、プリベイクされたライトマップを使用する場合は、話が違ってきます。その場合、デバッグ用のキューブをライトの少し上に移動させるなどして、キューブが光を遮っていないことを確認する必要があります。

PointLight {
    y: 200
    color: "#d9c62b"
    brightness: 5
    castsShadow: true
    shadowFactor: 75
    Model {
        source: "#Cube"
        scale: Qt.vector3d(0.01, 0.01, 0.01)
        materials: PrincipledMaterial {
            lighting: PrincipledMaterial.NoLighting
        }
    }
}

ここで使用するもう1つのトリックは、キューブで使用するマテリアルのライティングをオフにすることです。これにより、ライティングの影響を受けずに、デフォルトのベースカラー(白)で表示されます。これは、デバッグや視覚化の目的で使用するオブジェクトに便利です。

その結果、小さな白い立方体が現れ、PointLight の位置が視覚化されます:

スカイボックスと画像ベースのライティング

もう1つの明らかな改善点は、背景を何とかすることです。あの緑色のクリアな色は理想的とは言い難い。照明にも貢献するような環境はどうだろう?

適切なHDRIパノラマ画像があるとは限らないので、プロシージャル生成されたハイダイナミックレンジの空画像を使いましょう。これは、ProceduralSkyTextureData と、Texture の、ファイルベースではない、動的に生成された画像データのサポートを使えば簡単にできます。source を指定する代わりに、textureData プロパティを使用します。

environment: SceneEnvironment {
    backgroundMode: SceneEnvironment.SkyBox
    lightProbe: Texture {
        textureData: ProceduralSkyTextureData {
        }
    }
}

注: このコード例では、オブジェクトをインラインで定義することを好んでいます。これは必須ではありません。SceneEnvironmentProceduralSkyTextureData オブジェクトをオブジェクト・ツリーの別の場所で定義し、id で参照することもできます。

その結果、スカイボックスと改善されたライティングの両方が得られました。(前者は、backgroundMode が SkyBox に設定され、light probe が有効なTexture に設定されているためです。後者は、light probe が有効なTexture に設定されているためです。)

基本的なパフォーマンス調査

シーンのリソースとパフォーマンスに関する基本的な洞察を得るには、開発プロセスの早い段階で、インタラクティブなDebugView アイテムを表示する方法を追加することをお勧めします。ここでは、DebugView をトグルするButton を追加することにします。どちらも右上隅に固定されています。

import QtQuick
import QtQuick.Controls
import QtQuick3D
import QtQuick3D.Helpers

Item {
    width: 1280
    height: 720

    View3D {
        id: view3D
        anchors.fill: parent

        environment: SceneEnvironment {
            backgroundMode: SceneEnvironment.SkyBox
            lightProbe: Texture {
                textureData: ProceduralSkyTextureData {
                }
            }
        }

        PerspectiveCamera {
            id: camera
            y: 100
        }

        DirectionalLight {
        }

        Sponza {
            scale: Qt.vector3d(100, 100, 100)
        }

        PointLight {
            y: 200
            color: "#d9c62b"
            brightness: 5
            castsShadow: true
            shadowFactor: 75
            Model {
                source: "#Cube"
                scale: Qt.vector3d(0.01, 0.01, 0.01)
                materials: PrincipledMaterial {
                    lighting: PrincipledMaterial.NoLighting
                }
            }
        }

        Suzanne {
            y: 100
            scale: Qt.vector3d(50, 50, 50)
            NumberAnimation on eulerRotation.y {
                from: 0
                to: 360
                duration: 3000
                loops: Animation.Infinite
            }
        }

        WasdController {
            controlledObject: camera
        }
    }

    Button {
        anchors.right: parent.right
        text: "Toggle DebugView"
        onClicked: debugView.visible = !debugView.visible
        DebugView {
            id: debugView
            source: view3D
            visible: false
            anchors.top: parent.bottom
            anchors.right: parent.right
        }
    }
}

このパネルは、ライブタイミングを表示し、テクスチャマップとメッシュのライブリストを調べることができ、最終的なカラーバッファをレンダリングする前に実行する必要があるレンダリングパスについての洞察を与えます。

PointLight 、影を落とすライトにするため、複数のレンダーパスが必要です:

Textures のセクションでは、SuzanneとSponzaアセットのテクスチャマップ(後者にはたくさんのテクスチャマップがあります)と、プロシージャルで生成された空のテクスチャを見ることができます。

Models ページでは、驚きはありません:

Tools ページには、wireframe mode とさまざまなmaterial overrides を切り替えるインタラクティブなコントロールがあります。

ここでは、ワイヤーフレームモードを有効にし、マテリアルのbase color コンポーネントのみを使用するようにレンダリングを強制しています:

これで、インポートしたアセットを使ってQt Quick 3Dシーンをビルドする基本的な説明は終わりです。

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