Qt3D:如何使用Qt C ++在线框模式下渲染网格

时间:2017-08-01 09:30:35

标签: c++ qt wireframe qt3d

Qt3D文档正在增加但仍缺少一些信息,特别是关于如何在没有Qml / QtQuick的情况下执行操作。在大量搜索关于在线框模式下渲染网格的网页之后,我发现了很多有用的提示和示例,这些提示和示例共同产生了一个示例查看器,我想在此处作为对所有撰写文章的人和其他人的贡献搜索了类似的例子。 最有用的链接是这些:

Qt basic shapes example

Qt wireframe example

Qt material documentation

Qt MyCylinder example

有关在Qt3DWindow中使用事件过滤器的Stackoverflow问题和答案:Mouse controls over Qt 3D Window

可以使用鼠标旋转和缩放网格。

观众的屏幕截图

欢迎任何关于如何改进这一点的评论。特别是我对如何编写着色器程序感兴趣,它可以渲染不同颜色的正面和背面,或者渲染每个顶点的颜色。

以下是代码:

// ######### Opening the viewer #########
void MainWindow::import3dMeshInMeshViewer(QString name) 
{
    if (!m_viewer3D)
    {
        m_viewer3D = new Viewer3D(this);
    }
    m_viewer3D->sceneModifier()->addTriangleMeshCustomMaterial(name, m_meshVector);
    m_viewer3D->show();
}

// #########  Viewer class h  #########
class Viewer3D : public QDialog
{
    Q_OBJECT

    public:
        Viewer3D(QWidget *parent = 0);
        SceneModifier* sceneModifier() {return m_sceneModifier;}

    protected:
        bool eventFilter(QObject *obj, QEvent *ev);
        void mouseMoveEvent(QMouseEvent *ev);
        void mousePressEvent(QMouseEvent *ev);
        void mouseReleaseEvent(QMouseEvent *ev);
        void wheelEvent(QWheelEvent *we);
    private:

        QPointer<Qt3DCore::QEntity> m_rootEntity;
        QPointer<SceneModifier> m_sceneModifier;
        Qt3DExtras::Qt3DWindow *m_view;
        QPoint m_moveStartPoint;
        QMatrix4x4 m_cameraMatrix;
};


// ######### Viewer class cpp #########
Viewer3D::Viewer3D(QWidget *parent) :
    QDialog(parent)
{
    setAttribute(Qt::WA_DeleteOnClose);
    m_moveStartPoint.setX(-1);

    m_view = new Qt3DExtras::Qt3DWindow();

    m_view->installEventFilter(this);

    m_view->defaultFrameGraph()->setClearColor(QColor(QRgb(0x4d4d4f)));

    QWidget *container = QWidget::createWindowContainer(m_view);
    QSize screenSize = m_view->screen()->size();
    container->setMinimumSize(QSize(200, 100));
    container->setMaximumSize(screenSize);

    QHBoxLayout *hLayout = new QHBoxLayout(this);
    QVBoxLayout *vLayout = new QVBoxLayout();
    hLayout->addWidget(container, 1);

    setWindowTitle(QStringLiteral("Mesh Viewer"));

    // Root entity
    m_rootEntity = new Qt3DCore::QEntity();

    // Scene modifier
    m_sceneModifier = new SceneModifier(m_rootEntity);

    // Window geometry
    resize(parent->geometry().width() * 0.8, parent->geometry().height() * 0.8);
    move(parent->geometry().center() - QPoint(width() / 2, height() / 2));

    // Camera
    Qt3DRender::QCamera *cameraEntity = m_view->camera();

    //cameraEntity->lens()->setPerspectiveProjection(22.5f, m_view->width()/m_view->height(), 0.01f, 1000.0f);
    cameraEntity->setPosition(QVector3D(0, 0, 500.0f));
    cameraEntity->setUpVector(QVector3D(0, 1, 0));
    cameraEntity->setViewCenter(QVector3D(0, 0, 0));
    cameraEntity->transform()->setScale(1.f);

    // Set root object of the scene
    m_view->setRootEntity(m_rootEntity);
}

bool Viewer3D::eventFilter(QObject *obj, QEvent *ev)
{
    if (ev->type() == QEvent::Wheel)
    {
        wheelEvent(dynamic_cast<QWheelEvent*>(ev));
        return true;
    }
    else if (ev->type() == QEvent::MouseButtonPress)
    {
        mousePressEvent(dynamic_cast<QMouseEvent*>(ev));
        return true;
    }
    else if (ev->type() == QEvent::MouseMove)
    {
        mouseMoveEvent(dynamic_cast<QMouseEvent*>(ev));
        return true;
    }
    else if (ev->type() == QEvent::MouseButtonRelease)
    {
        mouseReleaseEvent(dynamic_cast<QMouseEvent*>(ev));
        return true;
    }

    return QObject::eventFilter(obj, ev);
}

void Viewer3D::wheelEvent(QWheelEvent *we)
{
    Qt3DCore::QTransform* transform = m_view->camera()->transform();

    float scale = transform->scale();
    QPoint delta = we->angleDelta();
    float zoom_distance = scale * static_cast<float>(delta.y()) / 500.f;
    scale -= zoom_distance;
    scale = std::min(10.0000f, scale);
    scale = std::max(0.001f, scale);
    transform->setScale(scale);
}

void Viewer3D::mousePressEvent(QMouseEvent *ev)
{
    if (ev->button() == Qt::LeftButton)
    {
        m_moveStartPoint = ev->pos();
        m_cameraMatrix = m_view->camera()->transform()->matrix();
    }
}

void Viewer3D::mouseMoveEvent(QMouseEvent *ev)
{
    if (m_moveStartPoint.x() > -1)
    {
        QPoint delta = ev->pos() - m_moveStartPoint;
        float angle = static_cast<float>(QPoint::dotProduct(delta, delta)) / 100.f;
        QVector3D axis = QVector3D(delta.y(), delta.x(), 0);

        QMatrix4x4 rotationMatrix = Qt3DCore::QTransform::rotateAround(-m_view->camera()->position(), angle, axis);

        QMatrix4x4 matrix = rotationMatrix * m_cameraMatrix;
        m_view->camera()->transform()->setMatrix(matrix);
    }
}

void Viewer3D::mouseReleaseEvent(QMouseEvent *ev)
{
    if (m_moveStartPoint.x() > -1)
    {
        m_moveStartPoint.setX(-1);
        m_cameraMatrix = m_view->camera()->transform()->matrix();
    }
}


// #########  Scene modifier class h #########
class SceneModifier : public QObject
{
    Q_OBJECT

    public:
        SceneModifier(Qt3DCore::QEntity* rootEntity);
        void addTriangleMeshCustomMaterial(QString name, const std::vector<Import3d::Triangle>& meshVector);

    private:
        Qt3DCore::QEntity* m_rootEntity;
};

// #########  Scene modifier class cpp #########
#include "SceneModifier.h"
#include "TriangleMeshRenderer.h"
#include "MaterialWireFrame.h"

SceneModifier::SceneModifier(Qt3DCore::QEntity* rootEntity) :
    m_rootEntity(rootEntity),
    QObject(rootEntity)
{
}

void SceneModifier::addTriangleMeshCustomMaterial(QString name, const std::vector<Import3d::Triangle>& meshVector)
{
    if (!m_rootEntity)
    {
        return;
    }

    // Mesh entity
    Qt3DCore::QEntity *triangleMeshEntity = new Qt3DCore::QEntity(m_rootEntity);
    triangleMeshEntity->setObjectName(QStringLiteral("customMeshEntity"));

    TriangleMeshRenderer *triangleMeshRenderer = new TriangleMeshRenderer(meshVector);
    MaterialWireFrame* materialWireFrame = new MaterialWireFrame();
    Qt3DCore::QTransform *transform = new Qt3DCore::QTransform;
    transform->setScale(1.f);

    triangleMeshEntity->addComponent(triangleMeshRenderer);
    triangleMeshEntity->addComponent(transform);
    triangleMeshEntity->addComponent(materialWireFrame);

    //emit meshAdded(name, triangleMeshEntity);
}

// ######### Point and Triangle structs #########
struct Point
{
    QVector3D p; //point x, y, z
    QVector3D c; //color red, green, blue

    Point() {}

    Point(float xp, float yp, float zp)
    {
        p = QVector3D(xp, yp, zp);
        c = QVector3D(0, 0, 0);
    }
    Point(QVector3D pos, unsigned char r, unsigned char g, unsigned char b)
    {
        p = pos;
        c = QVector3D(static_cast<float>(r) / 255.f,
                      static_cast<float>(g) / 255.f,
                      static_cast<float>(b) / 255.f);
    }
};

struct Triangle 
{
    Point vertices[3];

    Triangle()
    {
    }

    Triangle(Point p1, Point p2, Point p3)
    {
        vertices[0] = p1;
        vertices[1] = p2;
        vertices[2] = p3;
    }

};


// ######### TriangleMeshRenderer class h #########
class TriangleMeshRenderer : public Qt3DRender::QGeometryRenderer
{
    Q_OBJECT
public:
    explicit TriangleMeshRenderer(const std::vector<Import3d::Triangle>& meshVector, Qt3DCore::QNode *parent = 0);
    ~TriangleMeshRenderer();
};


class TriangleMeshGeometry : public Qt3DRender::QGeometry
{
    Q_OBJECT
public:
    TriangleMeshGeometry(const std::vector<Import3d::Triangle>& meshVector, TriangleMeshRenderer *parent);
};


// ######### TriangleMeshRenderer class cpp #########
TriangleMeshRenderer::TriangleMeshRenderer(const std::vector<Import3d::Triangle>& meshVector, QNode *parent)
    : Qt3DRender::QGeometryRenderer(parent)
{
    setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);
    setGeometry(new TriangleMeshGeometry(meshVector, this));
}

TriangleMeshRenderer::~TriangleMeshRenderer()
{
}

TriangleMeshGeometry::TriangleMeshGeometry(const std::vector<Import3d::Triangle>& meshVector, TriangleMeshRenderer *parent)
    : Qt3DRender::QGeometry(parent)
{
    Qt3DRender::QBuffer *vertexDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, this);
    Qt3DRender::QBuffer *indexDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::IndexBuffer, this);

    // Vertexbuffer
    QByteArray vertexBufferData;
    // Buffer size = triangle count * 3 * (3 + 3 + 3), 3 vertices per trinalge, each 3 floats for vertex position x,y,z, 3 floats normal and 3 floats color
    int bytesPerVertex = 9 * sizeof(float);
    int bytesPerTriangle = 3 * bytesPerVertex;
    vertexBufferData.resize(static_cast<int>(meshVector.size()) * bytesPerTriangle);
    char* pByte = vertexBufferData.data();
    int i = 0;
    // Indexbuffer
    QByteArray indexBufferData;
    indexBufferData.resize(static_cast<int>(meshVector.size()) * 3 * sizeof(uint));
    uint* rawIndexArray = reinterpret_cast<uint*>(indexBufferData.data());
    int idx = 0;

    for (int n = 0; n < meshVector.size(); ++n)
    {
        QVector3D nt = QVector3D::normal(meshVector[n].vertices[0].p, meshVector[n].vertices[1].p, meshVector[n].vertices[2].p); 

        for (int v = 0; v < 3; ++v)
        {
            // Vertex
            *reinterpret_cast<float*>(pByte) = meshVector[n].vertices[v].p.x(); pByte += 4;
            *reinterpret_cast<float*>(pByte) = meshVector[n].vertices[v].p.y(); pByte += 4;
            *reinterpret_cast<float*>(pByte) = meshVector[n].vertices[v].p.z(); pByte += 4;
            // Normal
            *reinterpret_cast<float*>(pByte) = nt.x(); pByte += 4;
            *reinterpret_cast<float*>(pByte) = nt.y(); pByte += 4;
            *reinterpret_cast<float*>(pByte) = nt.z(); pByte += 4;
            // Color
            *reinterpret_cast<float*>(pByte) = meshVector[n].vertices[v].c.x(); pByte += 4;
            *reinterpret_cast<float*>(pByte) = meshVector[n].vertices[v].c.y(); pByte += 4;
            *reinterpret_cast<float*>(pByte) = meshVector[n].vertices[v].c.z(); pByte += 4;

            // Index
            rawIndexArray[idx] = static_cast<uint>(idx++);
        }
    }

    vertexDataBuffer->setData(vertexBufferData);
    indexDataBuffer->setData(indexBufferData);

    // Attributes
    Qt3DRender::QAttribute *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(bytesPerVertex);
    positionAttribute->setCount(3 * static_cast<int>(meshVector.size()));
    positionAttribute->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());

    Qt3DRender::QAttribute *normalAttribute = new Qt3DRender::QAttribute();
    normalAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
    normalAttribute->setBuffer(vertexDataBuffer);
    normalAttribute->setDataType(Qt3DRender::QAttribute::Float);
    normalAttribute->setDataSize(3);
    normalAttribute->setByteOffset(3 * sizeof(float));
    normalAttribute->setByteStride(bytesPerVertex);
    normalAttribute->setCount(3 * static_cast<int>(meshVector.size()));
    normalAttribute->setName(Qt3DRender::QAttribute::defaultNormalAttributeName());

    Qt3DRender::QAttribute *colorAttribute = new Qt3DRender::QAttribute();
    colorAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
    colorAttribute->setBuffer(vertexDataBuffer);
    colorAttribute->setDataType(Qt3DRender::QAttribute::Float);
    colorAttribute->setDataSize(3);
    colorAttribute->setByteOffset(6 * sizeof(float));
    colorAttribute->setByteStride(bytesPerVertex);
    colorAttribute->setCount(3 * static_cast<int>(meshVector.size()));
    colorAttribute->setName(Qt3DRender::QAttribute::defaultColorAttributeName());

    Qt3DRender::QAttribute *indexAttribute = new Qt3DRender::QAttribute();
    indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute);
    indexAttribute->setBuffer(indexDataBuffer);
    indexAttribute->setDataType(Qt3DRender::QAttribute::UnsignedInt);
    indexAttribute->setDataSize(1);
    indexAttribute->setByteOffset(0);
    indexAttribute->setByteStride(0);
    indexAttribute->setCount(3 * static_cast<int>(meshVector.size()));

    addAttribute(positionAttribute);
    addAttribute(normalAttribute);
    addAttribute(colorAttribute);
    addAttribute(indexAttribute);

    parent->setGeometry(this);
}

1 个答案:

答案 0 :(得分:1)

OP有兴趣编写着色器程序,因此在Qt中编写OpenGL是必要的,对吗?就像在https://doc.qt.io/qt-5/qtgui-openglwindow-example.htmlhttps://doc.qt.io/qt-5/qtopengl-hellogl2-example.htm中一样。

https://doc.qt.io/qt-5/qopenglshaderprogram.html上有一个简单的着色器示例

program.addShaderFromSourceCode(QOpenGLShader::Vertex,
    "attribute highp vec4 vertex;\n"
    "uniform highp mat4 matrix;\n"
    "void main(void)\n"
    "{\n"
    "   gl_Position = matrix * vertex;\n"
    "}");
program.addShaderFromSourceCode(QOpenGLShader::Fragment,
    "uniform mediump vec4 color;\n"
    "void main(void)\n"
    "{\n"
    "   gl_FragColor = color;\n"
    "}");

来自https://learnopengl.com/Getting-started/Hello-Triangle

要在线框模式下绘制三角形,可以配置OpenGL 通过glPolygonMode(GL_FRONT_AND_BACK,GL_LINE)绘制其图元。 第一个论点说我们想将其应用于 所有三角形,第二行告诉我们将它们绘制为线。任何 随后的绘图调用将以线框模式渲染三角形 直到我们使用将其设置回默认值为止 glPolygonMode(GL_FRONT_AND_BACK,GL_FILL)。

并且来自https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL

gl_FrontFacing变量告诉我们当前片段是否是一部分 正面或背面。例如,我们可以决定 为所有背面输出不同的颜色。