Qt3DRender: Как визуализировать изоповерхность из четырехугольных патчей? - PullRequest
0 голосов
/ 04 января 2019

Я извлек изоповерхность с помощью алгоритма двойных марширующих кубов .

Из алгоритма я получил следующие данные:

# cube.obj wavefront file
## shared vertices (cartesian coordinates)
v 1.0 1.0 0.0
v 0.0 1.0 0.0
v 0.0 0.0 0.0
v 1.0 0.0 0.0
v 1.0 0.0 1.0
v 0.0 0.0 1.0
v 0.0 1.0 1.0
v 1.0 1.0 1.0
## faces (vertex indices are forming quad patches)
f 1 4 3 2
f 5 6 3 4
f 7 2 3 6
f 8 5 4 1
f 8 1 2 7
f 8 7 6 5

(Объяснение построения триангулированных поверхностей здесь ).

Основной вопрос: Можно ли визуализировать изоповерхность из данных четырехугольной грани с помощью Qt3d?

Бонусный вопрос: Как сделать поверхность прозрачной или построить ее в виде каркаса?

Вот как далеко я дошел:

#include <QApplication>
#include <QWidget>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DExtras/QOrbitCameraController>
#include <Qt3DRender/QCamera>
#include <Qt3DCore/QEntity>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QGeometryRenderer>
#include <Qt3DRender/QAttribute>
#include <Qt3DRender/QBuffer>
#include <Qt3DExtras/QPhongAlphaMaterial>

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    // Root entity
    auto *rootEntity = new Qt3DCore::QEntity();

    // Window container
    auto qt3DWindow = new Qt3DExtras::Qt3DWindow();
    qt3DWindow->setRootEntity(rootEntity);
    auto widget = QWidget::createWindowContainer(qt3DWindow);

    // Camera
    auto *camController = new Qt3DExtras::QOrbitCameraController(rootEntity);

    qt3DWindow->setRootEntity(rootEntity);
    qt3DWindow->camera()->lens()->setPerspectiveProjection(45.0f, 16.0f / 9.0f, 0.1f, 100.0f);
    qt3DWindow->camera()->setPosition(QVector3D(2.5, -8, 0.0));
    qt3DWindow->camera()->setViewCenter(QVector3D(0, 0, 0));

    // For camera controls
    camController->setLinearSpeed(50.f);
    camController->setLookSpeed(180.f);
    camController->setCamera(qt3DWindow->camera());

    // Material
    auto *material = new Qt3DExtras::QPhongAlphaMaterial(rootEntity);
    material->setSpecular(Qt::white);
    material->setShininess(0);
    material->setAmbient(Qt::red);
    material->setAlpha(0.5);

    // Transform
    auto *transform = new Qt3DCore::QTransform;
    transform->setScale(1.0f);

    auto *customMeshEntity = new Qt3DCore::QEntity(rootEntity);

    // Custom Mesh
    auto *customMeshRenderer = new Qt3DRender::QGeometryRenderer;
    auto *customGeometry = new Qt3DRender::QGeometry(customMeshRenderer);

    auto *vertexDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, customGeometry);
    auto *indexDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::IndexBuffer, customGeometry);

    // Vertices
    auto nVertices = 8;
    auto nCoordinates = 3; // cartesian coordinates
    QByteArray vertexBufferData;
    vertexBufferData.resize(nVertices * nCoordinates * sizeof(float));

    auto *rawVertexArray = reinterpret_cast<float *>(vertexBufferData.data());
    // Vertex 1
    rawVertexArray[0*nCoordinates+0] = 1.0f;
    rawVertexArray[0*nCoordinates+1] = 1.0f;
    rawVertexArray[0*nCoordinates+2] = 0.0f;
    // Vertex 2
    rawVertexArray[1*nCoordinates+0] = 0.0f;
    rawVertexArray[1*nCoordinates+1] = 1.0f;
    rawVertexArray[1*nCoordinates+2] = 0.0f;
    // Vertex 3
    rawVertexArray[2*nCoordinates+0] = 0.0f;
    rawVertexArray[2*nCoordinates+1] = 0.0f;
    rawVertexArray[2*nCoordinates+2] = 0.0f;
    // Vertex 4
    rawVertexArray[3*nCoordinates+0] = 1.0f;
    rawVertexArray[3*nCoordinates+1] = 0.0f;
    rawVertexArray[3*nCoordinates+2] = 0.0f;
    // Vertex 5
    rawVertexArray[4*nCoordinates+0] = 1.0f;
    rawVertexArray[4*nCoordinates+1] = 0.0f;
    rawVertexArray[4*nCoordinates+2] = 1.0f;
    // Vertex 6
    rawVertexArray[5*nCoordinates+0] = 0.0f;
    rawVertexArray[5*nCoordinates+1] = 0.0f;
    rawVertexArray[5*nCoordinates+2] = 1.0f;
    // Vertex 7
    rawVertexArray[6*nCoordinates+0] = 0.0f;
    rawVertexArray[6*nCoordinates+1] = 1.0f;
    rawVertexArray[6*nCoordinates+2] = 1.0f;
    // Vertex 8
    rawVertexArray[7*nCoordinates+0] = 1.0f;
    rawVertexArray[7*nCoordinates+1] = 1.0f;
    rawVertexArray[7*nCoordinates+2] = 1.0f;

    vertexDataBuffer->setData(vertexBufferData);

    // Faces
    unsigned nFaces = 6;
    unsigned nIndicesPerFace = 4;
    QByteArray indexBufferData;
    indexBufferData.resize(nFaces * nIndicesPerFace * sizeof(ushort));

    auto *rawIndexArray = reinterpret_cast<ushort *>(indexBufferData.data());
    // Face 1
    rawIndexArray[0*nIndicesPerFace+0] = 1;
    rawIndexArray[0*nIndicesPerFace+1] = 4;
    rawIndexArray[0*nIndicesPerFace+2] = 3;
    rawIndexArray[0*nIndicesPerFace+3] = 2;
    // Face 2
    rawIndexArray[1*nIndicesPerFace+0] = 5;
    rawIndexArray[1*nIndicesPerFace+1] = 6;
    rawIndexArray[1*nIndicesPerFace+2] = 3;
    rawIndexArray[1*nIndicesPerFace+3] = 4;
    // Face 3
    rawIndexArray[2*nIndicesPerFace+0] = 7;
    rawIndexArray[2*nIndicesPerFace+1] = 2;
    rawIndexArray[2*nIndicesPerFace+2] = 3;
    rawIndexArray[2*nIndicesPerFace+3] = 6;
    // Face 4
    rawIndexArray[3*nIndicesPerFace+0] = 8;
    rawIndexArray[3*nIndicesPerFace+1] = 5;
    rawIndexArray[3*nIndicesPerFace+2] = 4;
    rawIndexArray[3*nIndicesPerFace+3] = 1;
    // Face 5
    rawIndexArray[4*nIndicesPerFace+0] = 8;
    rawIndexArray[4*nIndicesPerFace+1] = 1;
    rawIndexArray[4*nIndicesPerFace+2] = 2;
    rawIndexArray[4*nIndicesPerFace+3] = 7;
    // Face 6
    rawIndexArray[5*nIndicesPerFace+0] = 8;
    rawIndexArray[5*nIndicesPerFace+1] = 7;
    rawIndexArray[5*nIndicesPerFace+2] = 6;
    rawIndexArray[5*nIndicesPerFace+3] = 5;

    indexDataBuffer->setData(indexBufferData);

    // Attributes
    auto *positionAttribute = new Qt3DRender::QAttribute();
    positionAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
    positionAttribute->setBuffer(vertexDataBuffer);
    positionAttribute->setDataType(Qt3DRender::QAttribute::Float);
    positionAttribute->setDataSize(3);
    positionAttribute->setByteOffset(0);
    positionAttribute->setByteStride(3 * sizeof(float));
    positionAttribute->setCount(nVertices);
    positionAttribute->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());

    auto *indexAttribute = new Qt3DRender::QAttribute();
    indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute);
    indexAttribute->setBuffer(indexDataBuffer);
    indexAttribute->setDataType(Qt3DRender::QAttribute::UnsignedShort);
    indexAttribute->setDataSize(1);
    indexAttribute->setByteOffset(0);
    indexAttribute->setByteStride(0);
    indexAttribute->setCount(nFaces*nIndicesPerFace);

    customGeometry->addAttribute(positionAttribute);
    customGeometry->addAttribute(indexAttribute);

    customMeshRenderer->setInstanceCount(1);
    customMeshRenderer->setFirstVertex(0);
    customMeshRenderer->setIndexOffset(1); // first index is 1
    customMeshRenderer->setFirstInstance(0);
    customMeshRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::Patches);
    customMeshRenderer->setVerticesPerPatch(4);
    customMeshRenderer->setGeometry(customGeometry);
    customMeshRenderer->setVertexCount(nFaces * nIndicesPerFace);

    customMeshEntity->addComponent(customMeshRenderer);
    customMeshEntity->addComponent(transform);
    customMeshEntity->addComponent(material);

    qt3DWindow->setRootEntity(rootEntity);
    widget->show();

    return QApplication::exec();
}

Визуализированная сцена остается пустой, поэтому я предполагаю, что здесь что-то не так. Я благодарен за любую помощь!

Ответы [ 2 ]

0 голосов
/ 07 января 2019

Я разработал решение. Следуя предложению @peppe, я нарисовал треугольники. Я также рассчитал нормали вершин.

Вот код:

#include <QApplication>
#include <QWidget>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DExtras/QOrbitCameraController>
#include <Qt3DRender/QCamera>
#include <Qt3DCore/QEntity>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QGeometryRenderer>
#include <Qt3DRender/QAttribute>
#include <Qt3DRender/QBuffer>
#include <Qt3DExtras/QPhongAlphaMaterial>

#include <iostream>
#include <assert.h>
#include <utility>

struct Vertex{

    Vertex(QVector3D p, ushort id)
    : position(p), normal({0,0,0}), index(id){}

    QVector3D position, normal;
    ushort index;
};

struct Quad{
    Quad() = delete;
    explicit Quad(std::vector<Vertex> v)
            : vertices(v) {
        assert(vertices.size() == 4);
    }

    std::vector<Vertex> vertices;
};
struct Triangle{
    Triangle() = delete;
    explicit Triangle(std::vector<Vertex> v)
    : vertices(v), faceNormal(QVector3D::normal(v[0].position,v[1].position,v[2].position)) {
        assert(vertices.size() == 3);
    }

    std::vector<Vertex> vertices;
    QVector3D faceNormal;
};

using trianlgePair = std::pair<Triangle,Triangle>;

trianlgePair quadToTriangle(Quad quad) {
    return {Triangle({quad.vertices[0], quad.vertices[1], quad.vertices[2]}),
            Triangle({quad.vertices[2], quad.vertices[3], quad.vertices[0]})};
}

int main(int argc, char* argv[])
{
    std::vector<Vertex>vertices({
        Vertex({1.0f, 1.0f, 0.0f}, 1-1),
        Vertex({0.0f, 1.0f, 0.0f}, 2-1),
        Vertex({0.0f, 0.0f, 0.0f}, 3-1),
        Vertex({1.0f, 0.0f, 0.0f}, 4-1),
        Vertex({1.0f, 0.0f, 1.0f}, 5-1),
        Vertex({0.0f, 0.0f, 1.0f}, 6-1),
        Vertex({0.0f, 1.0f, 1.0f}, 7-1),
        Vertex({1.0f, 1.0f, 1.0f}, 8-1)
    });

    std::vector<Quad> quads({
        Quad({vertices[1-1], vertices[4-1], vertices[3-1], vertices[2-1]}),
        Quad({vertices[5-1], vertices[6-1], vertices[3-1], vertices[4-1]}),
        Quad({vertices[7-1], vertices[2-1], vertices[3-1], vertices[6-1]}),
        Quad({vertices[8-1], vertices[5-1], vertices[4-1], vertices[1-1]}),
        Quad({vertices[8-1], vertices[1-1], vertices[2-1], vertices[7-1]}),
        Quad({vertices[8-1], vertices[7-1], vertices[6-1], vertices[5-1]})
    });
    unsigned nCoordinates = 3; // cartesian coordinates

    // Triangles
    std::vector<Triangle> triangles;
    for(const auto& quad : quads){

        auto trianglePair = quadToTriangle(quad);

        triangles.push_back(trianglePair.first);
        triangles.push_back(trianglePair.second);
    }
    unsigned nIndicesPerTriangle= 3;

    // vertex normals
    for (auto it = vertices.begin(); it != vertices.end(); ++it) {

        QVector3D vertexNormal = {0,0,0};

        // find triangles that contain the vertex
        for(const auto& t : triangles) {
            if(std::find_if(t.vertices.begin(), t.vertices.end(),
                    [it](const Vertex& v)->bool { return v.index == it.base()->index; }
                    ) != t.vertices.end())
                vertexNormal += t.faceNormal;
        }

        it.base()->normal = vertexNormal.normalized();
    }


    QApplication app(argc, argv);

    // Root entity
    auto *rootEntity = new Qt3DCore::QEntity();

    // Window container
    auto qt3DWindow = new Qt3DExtras::Qt3DWindow();
    qt3DWindow->setRootEntity(rootEntity);
    auto widget = QWidget::createWindowContainer(qt3DWindow);

    // Camera
    auto *camController = new Qt3DExtras::QOrbitCameraController(rootEntity);

    qt3DWindow->setRootEntity(rootEntity);
    qt3DWindow->camera()->lens()->setPerspectiveProjection(45.0f, 16.0f / 9.0f, 0.1f, 100.0f);
    qt3DWindow->camera()->setPosition(QVector3D(2.5, -8, 0.0));
    qt3DWindow->camera()->setViewCenter(QVector3D(0, 0, 0));

    // For camera controls
    camController->setLinearSpeed(50.f);
    camController->setLookSpeed(180.f);
    camController->setCamera(qt3DWindow->camera());

    // Material
    auto *material = new Qt3DExtras::QPhongAlphaMaterial(rootEntity);
    material->setSpecular(Qt::white);
    material->setShininess(0);
    material->setAmbient(Qt::red);
    material->setAlpha(0.5);

    // Transform
    auto *transform = new Qt3DCore::QTransform;
    transform->setScale(1.0f);

    auto *customMeshEntity = new Qt3DCore::QEntity(rootEntity);

    // Custom Mesh
    auto *customMeshRenderer = new Qt3DRender::QGeometryRenderer;
    auto *customGeometry = new Qt3DRender::QGeometry(customMeshRenderer);

    auto *vertexDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, customGeometry);
    auto *indexDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::IndexBuffer, customGeometry);

    // Vertices
    auto vertexDataPackageSize = nCoordinates*2; // position + normal
    QByteArray vertexBufferData;
    vertexBufferData.resize(vertices.size() * vertexDataPackageSize * sizeof(float));
    auto *rawVertexArray = reinterpret_cast<float *>(vertexBufferData.data());

    int idx = 0;
    for (const auto & v : vertices) {
        rawVertexArray[idx++] = float(v.position[0]);
        rawVertexArray[idx++] = float(v.position[1]);
        rawVertexArray[idx++] = float(v.position[2]);

        rawVertexArray[idx++] = float(v.normal[0]);
        rawVertexArray[idx++] = float(v.normal[1]);
        rawVertexArray[idx++] = float(v.normal[2]);
    }
    vertexDataBuffer->setData(vertexBufferData);

    auto *positionAttribute = new Qt3DRender::QAttribute();
    positionAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
    positionAttribute->setBuffer(vertexDataBuffer);
    positionAttribute->setDataType(Qt3DRender::QAttribute::Float);
    positionAttribute->setDataSize(nCoordinates);
    positionAttribute->setByteOffset(0);
    positionAttribute->setByteStride(vertexDataPackageSize  * sizeof(float));
    positionAttribute->setCount(vertices.size());
    positionAttribute->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());

    auto *normalAttribute = new Qt3DRender::QAttribute();
    normalAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
    normalAttribute->setBuffer(vertexDataBuffer);
    normalAttribute->setDataType(Qt3DRender::QAttribute::Float);
    normalAttribute->setDataSize(nCoordinates);
    normalAttribute->setByteOffset(nCoordinates * sizeof(float));
    normalAttribute->setByteStride(vertexDataPackageSize * sizeof(float));
    normalAttribute->setCount(vertices.size());
    normalAttribute->setName(Qt3DRender::QAttribute::defaultNormalAttributeName());

    QByteArray indexBufferData;
    indexBufferData.resize(triangles.size() * nIndicesPerTriangle * sizeof(ushort));
    auto *rawIndexArray = reinterpret_cast<ushort *>(indexBufferData.data());
    idx = 0;
    for (const auto& t : triangles) {

        rawIndexArray[idx++] = t.vertices[0].index;
        rawIndexArray[idx++] = t.vertices[1].index;
        rawIndexArray[idx++] = t.vertices[2].index;
        //std::cout << t.vertices[0].index <<", "<< t.vertices[1].index <<", "<< t.vertices[2].index << std::endl;
    }
    indexDataBuffer->setData(indexBufferData);

    auto *indexAttribute = new Qt3DRender::QAttribute();
    indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute);
    indexAttribute->setBuffer(indexDataBuffer);
    indexAttribute->setDataType(Qt3DRender::QAttribute::UnsignedShort);
    indexAttribute->setDataSize(1);
    indexAttribute->setByteOffset(0);
    indexAttribute->setByteStride(0);
    indexAttribute->setCount(triangles.size()*nIndicesPerTriangle);

    customMeshRenderer->setInstanceCount(1);
    customMeshRenderer->setFirstVertex(0);
    customMeshRenderer->setIndexOffset(0);
    customMeshRenderer->setFirstInstance(0);
    customMeshRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);
    customMeshRenderer->setGeometry(customGeometry);
    customMeshRenderer->setVertexCount(triangles.size()*nIndicesPerTriangle);
    customGeometry->addAttribute(positionAttribute);
    customGeometry->addAttribute(normalAttribute);
    customGeometry->addAttribute(indexAttribute);

    customMeshEntity->addComponent(customMeshRenderer);
    customMeshEntity->addComponent(transform);
    customMeshEntity->addComponent(material);

    qt3DWindow->setRootEntity(rootEntity);
    widget->show();

    return QApplication::exec();
}
0 голосов
/ 05 января 2019

«Патч» в OpenGL имеет очень специфическое значение (патчи имеют отношение к тесселяционным шейдерам , которые вы не используете).

Здесь вы просто имеете дело с лицами, которые являются четырехугольниками. Для их рендеринга вам нужно разделить их на два треугольника (современный OpenGL больше не рендерит квады). Обратите внимание, что если у вас есть файл .obj, вы можете использовать QMesh / QSceneLoader для прямой загрузки файла ...

...