Qt Quick 3D - カスタムジオメトリの例

C++およびQMLからカスタム頂点データを指定する方法を示します。

カスタムジオメトリを示す、Qtロゴのテクスチャが適用された三角形

この例では、ModelのQQuick3DGeometry およびthe geometry property を使用して、あらかじめ作成されたアセットの代わりに、C++およびQMLから指定された頂点、法線、テクスチャ座標を持つメッシュをレンダリングします。

さらに、GridGeometry の使用例も示します。GridGeometry は、グリッドの表示に適した線プリミティブを持つメッシュを提供する、QQuick3DGeometry の組み込み実装です。

この例では、カスタムジオメトリを提供するコードに焦点を当てますので、まずExampleTriangleGeometry のC++ヘッダーファイルを見てみましょう:

class ExampleTriangleGeometry : public QQuick3DGeometry
{
    Q_OBJECT
    QML_NAMED_ELEMENT(ExampleTriangleGeometry)
    Q_PROPERTY(bool normals READ normals WRITE setNormals NOTIFY normalsChanged)
    Q_PROPERTY(float normalXY READ normalXY WRITE setNormalXY NOTIFY normalXYChanged)
    Q_PROPERTY(bool uv READ uv WRITE setUV NOTIFY uvChanged)
    Q_PROPERTY(float uvAdjust READ uvAdjust WRITE setUVAdjust NOTIFY uvAdjustChanged)

public:
    ExampleTriangleGeometry();

    bool normals() const { return m_hasNormals; }
    void setNormals(bool enable);

    float normalXY() const { return m_normalXY; }
    void setNormalXY(float xy);

    bool uv() const { return m_hasUV; }
    void setUV(bool enable);

    float uvAdjust() const { return m_uvAdjust; }
    void setUVAdjust(float f);

signals:
    void normalsChanged();
    void normalXYChanged();
    void uvChanged();
    void uvAdjustChanged();

private:
    void updateData();

    bool m_hasNormals = false;
    float m_normalXY = 0.0f;
    bool m_hasUV = false;
    float m_uvAdjust = 0.0f;
};

最も重要な点は、ExampleTriangleGeometry クラスがQQuick3DGeometry を継承しており、QML_NAMED_ELEMENT(ExampleTriangleGeometry) マクロを呼び出すことで、QMLからこのクラスにアクセスできるようになっていることです。また、Q_PROPERTY マクロを通じて定義されたいくつかのプロパティがあり、これらはQMLオブジェクト内で自動的に公開されます。それでは、QMLモデルを見てみましょう:

Model {
    id: triangleModel
    visible: false
    scale: Qt.vector3d(100, 100, 100)
    geometry: ExampleTriangleGeometry {
        normals: cbNorm.checked
        normalXY: sliderNorm.value
        uv: cbUV.checked
        uvAdjust: sliderUV.value
    }
    materials: [
        PrincipledMaterial {
            Texture {
                id: baseColorMap
                source: "qt_logo_rect.png"
            }
            cullMode: PrincipledMaterial.NoCulling
            baseColorMap: cbTexture.checked ? baseColorMap : null
            specularAmount: 0.5
        }
    ]
}

geometry プロパティでExampleTriangleGeometry クラスを使用するように指定し、関連するプロパティも指定している点に注意してください。カスタムジオメトリを使用するためにQML側で必要な処理は、これだけです。

次に、C++コードのもう一つの重要な部分、すなわちupdateData() メソッドを見てみましょう。このメソッドは、ExampleTriangleGeometry クラスが作成されたとき、またはそのQMLプロパティのいずれかが更新されたときに、カスタムジオメトリ用のデータを作成してアップロードします。

void ExampleTriangleGeometry::updateData()
{
    clear();

    int stride = 3 * sizeof(float);
    if (m_hasNormals)
        stride += 3 * sizeof(float);
    if (m_hasUV)
        stride += 2 * sizeof(float);

    QByteArray vertexData(3 * stride, Qt::Initialization::Uninitialized);
    float *p = reinterpret_cast<float *>(vertexData.data());

    // a triangle, front face = counter-clockwise
    *p++ = -1.0f; *p++ = -1.0f; *p++ = 0.0f;
    if (m_hasNormals) {
        *p++ = m_normalXY; *p++ = m_normalXY; *p++ = 1.0f;
    }
    if (m_hasUV) {
        *p++ = 0.0f + m_uvAdjust; *p++ = 0.0f + m_uvAdjust;
    }
    *p++ = 1.0f; *p++ = -1.0f; *p++ = 0.0f;
    if (m_hasNormals) {
        *p++ = m_normalXY; *p++ = m_normalXY; *p++ = 1.0f;
    }
    if (m_hasUV) {
        *p++ = 1.0f - m_uvAdjust; *p++ = 0.0f + m_uvAdjust;
    }
    *p++ = 0.0f; *p++ = 1.0f; *p++ = 0.0f;
    if (m_hasNormals) {
        *p++ = m_normalXY; *p++ = m_normalXY; *p++ = 1.0f;
    }
    if (m_hasUV) {
        *p++ = 1.0f - m_uvAdjust; *p++ = 1.0f - m_uvAdjust;
    }

    setVertexData(vertexData);
    setStride(stride);
    setBounds(QVector3D(-1.0f, -1.0f, 0.0f), QVector3D(+1.0f, +1.0f, 0.0f));

    setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);

    addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
                 0,
                 QQuick3DGeometry::Attribute::F32Type);

    if (m_hasNormals) {
        addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,
                     3 * sizeof(float),
                     QQuick3DGeometry::Attribute::F32Type);
    }

    if (m_hasUV) {
        addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic,
                     m_hasNormals ? 6 * sizeof(float) : 3 * sizeof(float),
                     QQuick3DGeometry::Attribute::F32Type);
    }
}

このメソッドはまず、clear() を呼び出して、以前にアップロードされたデータをすべてクリアします。次に、法線やUV座標の存在を考慮して、頂点のストライドを計算します。 次に、頂点バッファを格納するためのバイト配列が作成され、そこに(-1, -1, 0)、(1, -1, 0)、(0, 1, 0)を頂点とする単一の三角形の頂点データが格納されます。

続いて、setVertexData() およびsetStride() を呼び出して、頂点データをアップロードし、ストライドを設定します。ジオメトリの境界は、setBounds を呼び出して設定します。この例では使用されていませんが、影を正しく描画するためには境界の設定が必要です。 次に、setPrimitiveType() を呼び出してプリミティブタイプを設定します。最後に、各属性についてaddAttribute() を呼び出し、先にアップロードしたバッファ内での位置、法線、UV座標の属性のメモリ配置を指定します。

サンプルプロジェクト @ code.qt.io

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