このページでは

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

「Qt Quick 3D - 入門」サンプルでは、Qt Quick 3D を使用した QML ベースのアプリケーション作成について簡単に紹介していますが、その際には球体や円柱などの組み込みプリミティブのみが使用されています。このページでは、Khronos glTF サンプルモデルリポジトリにあるモデルの一部を使用し、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
        }
    }
}

緑色の背景の空の3Dビュー

アセットのインポート

ここでは、Sample Modelsリポジトリにある2つのglTF 2.0モデル、「Sponza」と「Suzanne」を使用します。

これらのモデルには通常、.gltf ファイルに加えて、いくつかのテクスチャマップや、別のバイナリファイルに保存されたメッシュ(ジオメトリ)データが付属しています:

Sponzaのアセットファイルの一覧

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

いくつかの方法があります:

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

balsam およびbalsamui の両アプリケーションはQtに同梱されており、Qt Quick 3Dがインストールまたはビルドされている限り、他の同様の実行可能ツールと同じディレクトリに存在しているはずです。多くの場合、追加の引数を指定することなく、コマンドラインから.gltfファイルに対してbalsamを実行するだけで十分です。 ただし、多くのコマンドラインオプション、あるいはbalsamui やQt Design Studio を使用する場合の対話型オプションについて知っておく価値があります。例えば、ベイクされたライトマップを使用して静的なグローバルイルミネーションを実現する場合、実行時にこの処理を行うとリソースを消費する可能性があるため、アセットのインポート時に追加のライトマップ UV チャンネルを生成するために、--generateLightmapUV を指定したい場合が多いでしょう。 同様に、シーン内で自動LODを有効にするために、生成されたメッシュの簡略化版が必要な場合は、--generateMeshLevelsOfDetail が不可欠です。その他のオプションには、欠落データの生成(例:--generateNormals )や、さまざまな最適化の実行などがあります。

balsamui では、コマンドラインオプションが対話型要素に割り当てられています:

Balsam UI 設定パネル

balsam を使用したインポート

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

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

これにより、meshes サブディレクトリ下にSponza.qml および.mesh ファイルが生成され、テクスチャマップはmaps にコピーされます。

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

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

ただし、モデル(サブシーン)を追加するだけでは意味がありません。デフォルトでは、マテリアルには完全なPBRライティング計算が組み込まれているため、DirectionalLight 、PointLight 、SpotLight などのライトがない場合や、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 ツールでこれを実行すると読み込まれて動作しますが、Sponzaモデルがカメラの後ろにあるため、デフォルトではシーン全体が空っぽになっています。また、スケールも理想的ではありません。例えば、WASDキーやマウス(WasdController で有効化されている)を使って移動しても、違和感があります。

これを改善するために、100 を使用して、Sponzaモデル(サブシーン)をX、Y、Z軸に沿ってスケーリングします。さらに、カメラの初期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形式のSponzaアセットは103個のサブメッシュを持つ単一のモデルであり、materials list に103個の要素を持つ単一のModel オブジェクトにマッピングされます。しかし、アセットには任意の数のmodels を含めることができ、それぞれが複数のサブメッシュと関連するマテリアルを持っています。 これらのモデルは親子関係を形成でき、追加のnodes と組み合わせて、平行移動、回転、拡大縮小などの変形を行うことができます。したがって、レンダリング結果が視覚的には単一のモデルとして認識される場合でも、インポートされたアセットを完全なサブシーン、つまりnodes の任意のツリー構造として捉える方が適切です。 生成された Sponza.qml や、そのようなアセットから生成されたその他の QML ファイルをプレーンテキストエディタで開いて、その構造を把握してください(もちろん、構造はソースアセット、この場合は glTF ファイルがどのように設計されたかによって常に異なります)。

balsamui 経由でのインポート

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

balsamui を実行すると、ツールが起動します:

Balsam UI の起動ウィンドウ

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

スザンヌのアセットファイルを表示しているファイルブラウザ

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

変換処理中の状態を示すBalsam UI

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

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
        }
    }
}

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

その結果、PointLight の位置を可視化する小さな白い立方体が表示されていることに注目してください:

スポンザ宮殿の床に描かれたスザンヌの猿の頭

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

もう1つの明らかな改善点は、背景への対応です。あの緑がかった透明な色は、あまり理想的ではありません。照明にも寄与するような環境を追加してみてはいかがでしょうか?

必ずしも適切なHDRIパノラマ画像が利用できるとは限らないため、手続き的に生成されたハイダイナミックレンジの空の画像を使用しましょう。これは、ProceduralSkyTextureData およびTexture が、ファイルベースではない動的に生成された画像データをサポートしているおかげで簡単に実現できます。source を指定する代わりに、textureData プロパティを使用します。

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

注:このサンプルコードでは 、オブジェクトをインラインで定義することを推奨しています。これは必須ではなく、SceneEnvironment やProceduralSkyTextureData オブジェクトをオブジェクトツリーの別の場所で定義し、id から参照することも可能です。

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

画像に基づく照明を備えたスポンツァ宮殿

IBLの向きが異なるスポンザ宮殿

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

シーンのリソースやパフォーマンスに関する基本的な知見を得るためには、開発プロセスの早い段階で、インタラクティブな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
        }
    }
}

パフォーマンス統計情報を表示するデバッグビューパネルが表示されたSponzaの画面

このパネルでは、リアルタイムのタイミングが表示され、テクスチャマップやメッシュのリアルタイムリストを確認できるほか、最終的なカラーバッファをレンダリングする前に実行する必要があるレンダーパスについて把握することができます。

「PointLight 」を影を投射するライトとして設定したため、複数のレンダリングパスが関与しています:

レンダリングパスを表示したデバッグパネルを前にしたスザンヌ

「Textures 」セクションでは、「Suzanne」および「Sponza」アセットのテクスチャマップ(後者は非常に多くのテクスチャマップを含んでいます)に加え、手続き的に生成された空のテクスチャも確認できます。

テクスチャの統計情報を表示したデバッグパネルが表示されているシーン

「Models 」ページには特に驚くような点はありません:

メッシュの統計情報を表示するデバッグパネルが表示された画面

「Tools 」ページには、「wireframe mode 」や各種「material overrides 」を切り替えるためのインタラクティブなコントロールがいくつかあります。

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

デバッグパネルが表示されたワイヤーフレームモードのSponzaの画面

これで、インポートしたアセットを使用したQt Quick 3Dシーンの構築に関する基本解説は以上です。

© 2026 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.