Qt Quick glTFアセットを使った3D入門

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 つのモデルであり、103 個の要素を持つ 1 つのModel オブジェクトにマッピングされます。materials list アセットは、任意の数の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シーンをビルドする基本についてのツアーは終わりです。

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.