Qt Quick 3D - Ejemplo de geometría personalizada

Demuestra cómo proporcionar datos de vértices personalizados desde C++ y QML.

Triángulo con textura de logotipo Qt demostrando geometría personalizada

Este ejemplo hace uso de QQuick3DGeometry y the geometry property de Model para renderizar una malla con coordenadas de vértices, normales y texturas especificadas desde C++ y QML en lugar de un asset pre-cocinado.

Además, también se muestra GridGeometry. GridGeometry es una implementación incorporada de QQuick3DGeometry que proporciona una malla con primitivas de línea adecuadas para mostrar una cuadrícula.

En este ejemplo nos centraremos en el código que proporciona la geometría personalizada, así que primero echemos un vistazo al archivo de cabecera 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;
};

Lo más importante a destacar es que nuestra clase ExampleTriangleGeometry hereda de QQuick3DGeometry y que llamamos a la macro QML_NAMED_ELEMENT(ExampleTriangleGeometry), haciendo que nuestra clase sea accesible en QML. También hay algunas propiedades definidas a través de la macro Q_PROPERTY que se exponen automáticamente en nuestro objeto QML. Veamos ahora el modelo 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
        }
    ]
}

Observa que especificamos la propiedad geometry para utilizar nuestra clase ExampleTriangleGeometry, con las propiedades relevantes especificadas. Esto es todo lo que se necesita en QML para utilizar una geometría personalizada.

Ahora, veamos la otra parte importante del código C++, a saber, el método updateData(). Este método crea y carga los datos de nuestra geometría personalizada cada vez que se crea una clase ExampleTriangleGeometry o se actualiza cualquiera de sus propiedades 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);
    }
}

El método comienza llamando a clear() para borrar todos los datos cargados previamente. A continuación calcula el stride de los vértices, teniendo en cuenta la presencia de normales y coordenadas uv. A continuación, se crea una matriz de bytes para contener el búfer de vértices, que se llena con los vértices de un triángulo con esquinas en (-1, -1, 0), (1, -1, 0) y (0, 1, 0).

A continuación se cargan los datos de los vértices y se establece el stride llamando a setVertexData() y setStride(). Los límites de la geometría se establecen llamando a setBounds. Aunque no se utiliza en este ejemplo la configuración de los límites es necesaria para que las sombras funcionen. A continuación, el tipo primitivo se establece llamando a setPrimitiveType(). Por último, especificamos cómo se disponen en memoria los atributos de posición, normal y coordenadas uv en el buffer previamente cargado llamando a addAttribute() para cada atributo.

Proyecto de ejemplo @ 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.