【问题标题】:Access framebuffer in Qt3D在 Qt3D 中访问帧缓冲区
【发布时间】:2018-01-11 12:27:33
【问题描述】:

我的任务:计算 3D 网格的像素坐标(例如制作快照),以从特定相机角度找到该网格的 2D 形状。

我目前正在使用带有 QGeometryRenderer 的 Qt3D 将包含网格的场景渲染到 QWidget,这可以正常工作。 我尝试按照这篇文章How to create screenshot of QWidget? 的建议,使用 QWidget::render() 将 QWidget 的内容渲染到 Pixmap 中。将像素图保存为 .jpg 会生成具有默认背景颜色的空白图像,这是有道理的,因为 QWidget 本身没有保存网格对象。

这是在我的 mainwindow.cpp 中设置场景的方式

// sets the scene objects, camera, lights,...
void MainWindow::setScene() {
    scene = custommesh->createScene(mesh->getVertices(), 
            mesh->getVerticesNormals(), 
            mesh->getFaceNormals(), 
            mesh->getVerticesIndex(), 
            mesh->getFacesIndex());              // QEntity*                         
    custommesh->setMaterial(scene);              // CustomMeshRenderer object
    camera = custommesh->setCamera(view);
    custommesh->setLight(scene, camera);
    custommesh->setCamController(scene, camera);

    view->setRootEntity(scene);                  // Qt3DExtras::Qt3DWindow object

    // Setting up a QWiget working as a container for the view
    QWidget *container = QWidget::createWindowContainer(view);
    container->setMinimumSize(QSize(500, 500));
    QSizePolicy policy = QSizePolicy(QSizePolicy::Policy(5), QSizePolicy::Policy(5));
    policy.setHorizontalStretch(1);
    policy.setVerticalStretch(1);
    container->setSizePolicy(policy);
    container->setObjectName("meshWidget");

    this->ui->meshLayout->insertWidget(0, container);
}

至于这里的渲染是custommeshenderer类,其中定义了QGeometryRenderer,初始化网格时返回一个QEntity*。

#include "custommeshrenderer.h"
#include <Qt3DRender/QAttribute>
#include <Qt3DExtras>
#include <Qt3DRender/QGeometryRenderer>


CustommeshRenderer::CustommeshRenderer()
{
    rootEntity = new Qt3DCore::QEntity;
    customMeshEntity = new Qt3DCore::QEntity(rootEntity);
    transform = new Qt3DCore::QTransform;

    customMeshRenderer = new Qt3DRender::QGeometryRenderer;
    customGeometry = new Qt3DRender::QGeometry(customMeshRenderer);

    m_pVertexDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, customGeometry);
    m_pNormalDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, customGeometry);
    m_pColorDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, customGeometry);
    m_pIndexDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::IndexBuffer, customGeometry);

}

/**
    Set vertices and their normals for the scene

    @param vertices List with all vertices of the mesh
    @param vertices_normals List with all vertice normals
    @param face_normals List with all face normals
    @param vertice_idx List with the indices for the vertices
    @param face_idx List with all indices for the faces
    @return Entity where some components were added
*/
Qt3DCore::QEntity *CustommeshRenderer::createScene(QList<QVector3D> vertices, QList<QVector3D> vertices_normals, QList<QVector3D> face_normals, QList<int> vertices_idx, QList<QVector3D> faces_idx) {

    // Setting scale to 8.0
    transform->setScale(8.0f);

    // Setting all the colors to (200, 0, 0)
    QList<QVector3D> color_list;
    for(int i = 0; i < vertices.length(); i++) {
        color_list.append(QVector3D(200.0f, 0.0f, 0.0f));
    }

    // Fill vertexBuffer with data which hold the vertices, normals and colors
    // Build structure: Size of Verticles List * 3 (x,y,z) * 4 (since x,y,z are floats, which needs 4 bytes each) 
    vertexBufferData.resize(vertices.length() * 3 * (int)sizeof(float));
    float *rawVertexArray = reinterpret_cast<float *>(vertexBufferData.data());

    normalBufferData.resize(vertices_normals.length() * 3 * (int)sizeof(float));
    float *rawNormalArray = reinterpret_cast<float *>(normalBufferData.data());

    colorBufferData.resize(color_list.length() * 3 * (int)sizeof(float));
    float *rawColorArray = reinterpret_cast<float *>(colorBufferData.data());


    setRawVertexArray(rawVertexArray, vertices);
    setRawNormalArray(rawNormalArray, vertices_normals);
    setRawColorArray(rawColorArray, color_list);

    //Fill indexBufferData with data which holds the triangulation information (patches/tris/lines)
    indexBufferData.resize(faces_idx.length() * 3 * (int)sizeof(uint));
    uint *rawIndexArray = reinterpret_cast<uint *>(indexBufferData.data());

    setRawIndexArray(rawIndexArray, faces_idx);

    //Set data to buffers
    m_pVertexDataBuffer->setData(vertexBufferData);
    m_pNormalDataBuffer->setData(normalBufferData);
    m_pColorDataBuffer->setData(colorBufferData);
    m_pIndexDataBuffer->setData(indexBufferData);

    // Attributes
    Qt3DRender::QAttribute *positionAttribute = new Qt3DRender::QAttribute();
    positionAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
    positionAttribute->setBuffer(m_pVertexDataBuffer);
   // positionAttribute->setBuffer(m_pVertexDataBuffer.data());
    positionAttribute->setDataType(Qt3DRender::QAttribute::Float);
    positionAttribute->setDataSize(3);
    positionAttribute->setByteOffset(0);
    positionAttribute->setByteStride(3 * sizeof(float));
    positionAttribute->setCount(vertices.length());
    positionAttribute->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());

    Qt3DRender::QAttribute *normalAttribute = new Qt3DRender::QAttribute();
    normalAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
    normalAttribute->setBuffer(m_pNormalDataBuffer);
    //normalAttribute->setBuffer(m_pNormalDataBuffer.data());
    normalAttribute->setDataType(Qt3DRender::QAttribute::Float);
    normalAttribute->setDataSize(3);
    normalAttribute->setByteOffset(0);
    normalAttribute->setByteStride(3 * sizeof(float));
    normalAttribute->setCount(vertices.length());
    normalAttribute->setName(Qt3DRender::QAttribute::defaultNormalAttributeName());

    Qt3DRender::QAttribute* colorAttribute = new Qt3DRender::QAttribute();
    colorAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
    colorAttribute->setBuffer(m_pColorDataBuffer);
    //colorAttribute->setBuffer(m_pColorDataBuffer.data());
    colorAttribute->setDataType(Qt3DRender::QAttribute::Float);
    colorAttribute->setDataSize(3);
    colorAttribute->setByteOffset(0);
    colorAttribute->setByteStride(3 * sizeof(float));
    colorAttribute->setCount(vertices.length());
    colorAttribute->setName(Qt3DRender::QAttribute::defaultColorAttributeName());

    Qt3DRender::QAttribute *indexAttribute = new Qt3DRender::QAttribute();
    indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute);
    indexAttribute->setBuffer(m_pIndexDataBuffer);
    //indexAttribute->setBuffer(m_pIndexDataBuffer.data());
    indexAttribute->setDataType(Qt3DRender::QAttribute::UnsignedInt);
    indexAttribute->setDataSize(3);
    indexAttribute->setByteOffset(0);
    indexAttribute->setByteStride(3 * sizeof(uint));
    indexAttribute->setCount(face_normals.length());

    customGeometry->addAttribute(positionAttribute);
    customGeometry->addAttribute(normalAttribute);
    /*customGeometry->addAttribute(colorAttribute);*/
    customGeometry->addAttribute(indexAttribute);

    //Set the final geometry and primitive type
    customMeshRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);
    customMeshRenderer->setVerticesPerPatch(3);
    customMeshRenderer->setGeometry(customGeometry);


    customMeshRenderer->setVertexCount(faces_idx.length()*3);

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

    setMaterial(customMeshEntity);

    return rootEntity;
}

访问帧缓冲区的最佳方法是什么,或者还有其他方法可以拍摄网格快照吗?

我最后的希望是自己实现渲染管道(至少从投影坐标到像素坐标),但我更喜欢另一种解决方案。不幸的是我不得不依赖 Qt3D 并且不能切换到像 QOpenGLWidget 这样的其他类。至少我还没有找到整合它的可能性。

我对 Qt3D 还很陌生,缺乏详细的文档并不能让它变得更容易。

【问题讨论】:

  • 我对 Qt3D 不熟悉,但我的猜测是创建一个QTexture2D,将其设置为QRenderTargetOutput 上的纹理(连同连接点),然后添加输出到QRenderTarget,在QRenderTargetSelector 上设置目标(连同输出),最后将选择器设置为Qt3DWindow 上的活动框架图。查看阴影贴图示例 (doc.qt.io/qt-5.10/qt3d-shadow-map-qml-example.html) 也可能会有所帮助。

标签: c++ qt framebuffer qt3d


【解决方案1】:

您可以为此使用QRenderCapture。这实际上为您做了一个glReadPixels。这个文档有点稀疏,但在线有一个example

另外,我实现了一个offline renderer,如果您不想要整个 3D 窗口,它可以为您提供帮助。

我不知道你的意思是什么

计算 3D 网格的像素坐标(例如制作快照),以从特定相机角度找到该网格的 2D 形状

但是如果你例如想要只用一种颜色(没有高光)渲染整个网格,你可以试试QPerVertexColorMaterial,它给了我这个结果。

【讨论】:

  • 我发现你昨天在这里提供的相同链接解决了我的问题。只是不够快,无法自己写答案:)。
猜你喜欢
  • 2012-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-07
相关资源
最近更新 更多