如何使这个 OpenGL (Qt3D) 代码高效?

Posted

技术标签:

【中文标题】如何使这个 OpenGL (Qt3D) 代码高效?【英文标题】:How to make this OpenGL (Qt3D) code efficient? 【发布时间】:2019-07-03 18:43:12 【问题描述】:

我写了一个小程序,可以渲染一个 175x175 的高度图。渲染是使用 Qt3D 完成的,它基本上是一组围绕 OpenGL 的包装器。该程序可以正常加载并在功能强大的桌面上正常运行。但是,当我在较低功率的 GPU 上运行它时,当我开始移动相机时,图像更新非常不稳定。即使是小型 GPU,渲染 3D 地形网格也不应该那么困难,所以我认为我做错了什么。是否有一些明显的方法可以优化此代码,还是我对小型 GPU 的期望过高?

片段着色器 https://github.com/qt/qt3d/blob/5.12/src/extras/shaders/es2/phong.inc.frag

顶点着色器: https://github.com/qt/qt3d/blob/5.12/src/extras/shaders/es2/morphphong.vert

int main(int argc, char* argv[])

    QGuiApplication app(argc, argv);

    Qt3DExtras::Qt3DWindow view;

    // Scene Root
    Qt3DCore::QEntity *sceneRoot = new Qt3DCore::QEntity();

    // Scene Camera
    Qt3DRender::QCamera *basicCamera = view.camera();
    basicCamera->setProjectionType(Qt3DRender::QCameraLens::PerspectiveProjection);
    basicCamera->setUpVector(QVector3D(0.0f, 1.0f, 0.0f));
    basicCamera->setViewCenter(QVector3D(60.0f, 15.0f, -60.0f));
    basicCamera->setPosition(QVector3D(60.0f, 26.0f, 0.0f));
    // For camera controls
    Qt3DExtras::QFirstPersonCameraController *camController = new Qt3DExtras::QFirstPersonCameraController(sceneRoot);
    camController->setCamera(basicCamera);

    // Material
    Qt3DRender::QMaterial *material= new Qt3DExtras::QPhongMaterial(sceneRoot);

    Qt3DCore::QEntity *customMeshEntity = new Qt3DCore::QEntity(sceneRoot);

    // Transform
    Qt3DCore::QTransform *transform = new Qt3DCore::QTransform;

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

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

    QImage heightmap("../assets/heightmap.png");

    QByteArray vertexBufferData;
    vertexBufferData.resize(heightmap.width() * heightmap.height() * (3 + 3 + 3) * sizeof(float));

    QVector<QVector3D> vertexPositions;
    for (int row = 0; row < heightmap.height(); row++) 
        for (int column = 0; column < heightmap.width(); column++) 
            vertexPositions.append(QVector3D(row, heightmap.pixelColor(row, column).red()/8.0, -column));
        
    

    QVector<QVector3D> vertexNormals;
    for (int row = 0; row < heightmap.height(); row++) 
        for (int column = 0; column < heightmap.width(); column++) 

            int center = (row * heightmap.width()) + column;
            int upper = center - heightmap.width();
            int lower = center + heightmap.width();
            int right = center + 1;
            int left = center -1;
            int lowerLeft = center - 1 + heightmap.width();
            int upperRight = center + 1 - heightmap.width();
            int rightEdge = heightmap.width() - 1;
            int bottomEdge = heightmap.height() -1;

            // Calculate normals for each adjacent face and sum
            // Check for edge conditions
            QVector3D vertexNormal(0, 0, 0);
            if (column != 0 && row != 0 ) 
                vertexNormal += QVector3D::normal(vertexPositions[center], vertexPositions[upper], vertexPositions[left]);
            
            if (column != rightEdge && row != 0) 
                vertexNormal += QVector3D::normal(vertexPositions[center], vertexPositions[upperRight], vertexPositions[upper]);
                vertexNormal += QVector3D::normal(vertexPositions[center], vertexPositions[right], vertexPositions[upperRight]);
             
            if (column != rightEdge && row != bottomEdge) 
                vertexNormal += QVector3D::normal(vertexPositions[center], vertexPositions[lower], vertexPositions[right]);
            
            if (column != 0 && row != bottomEdge) 
                vertexNormal += QVector3D::normal(vertexPositions[center], vertexPositions[lowerLeft], vertexPositions[lower]);
                vertexNormal += QVector3D::normal(vertexPositions[center], vertexPositions[left], vertexPositions[lowerLeft]);
            
            vertexNormals.append(vertexNormal.normalized());
        
    

    // Colors
    QVector3D red(1.0f, 0.0f, 0.0f);
    QVector3D yellow(1.0f, 1.0f, 0.0f);
    QVector3D green(0.0f, 1.0f, 0.0f);
    QVector3D blue(0.0f, 0.0f, 1.0f);
    QVector3D white(1.0f, 1.0f, 1.0f);


    QVector<QVector3D> vertices;
    for (int i = 0; i < vertexPositions.count(); i ++) 
        vertices.append(vertexPositions[i]);
        vertices.append(vertexNormals[i]);
        if (vertexPositions[i].y() > 20.0) 
            vertices.append(red);
        
        else if (vertexPositions[i].y() > 18.0) 
            vertices.append(yellow);
        
        else 
            vertices.append(green);
        
    

    float *rawVertexArray = reinterpret_cast<float *>(vertexBufferData.data());
    int idx = 0;

    Q_FOREACH (const QVector3D &v, vertices) 
        rawVertexArray[idx++] = v.x();
        rawVertexArray[idx++] = v.y();
        rawVertexArray[idx++] = v.z();
    

    // Indices
    QByteArray indexBufferData;
    int indicesCount = (heightmap.height() - 1) * (heightmap.width() - 1) * 2 * 3;
    indexBufferData.resize( indicesCount * sizeof(uint));
    uint *rawIndexArray = reinterpret_cast<uint *>(indexBufferData.data());

    int index = 0;
    for (int row = 0; row < heightmap.height()-1; row++) 
        for (int column = 0; column < heightmap.width()-1; column++) 

            //  1 <- 3
            //  |   /
            //  |  /
            //  v /
            //  2
            int vertexBufferIndex = (row * heightmap.width()) + column;
            rawIndexArray[index++] = vertexBufferIndex;
            rawIndexArray[index++] = vertexBufferIndex + heightmap.width(); // down one row
            rawIndexArray[index++] = vertexBufferIndex + 1; // right one column

            //       1
            //     / ^
            //    /  |
            //   /   |
            //  2 -> 3

            rawIndexArray[index++] = vertexBufferIndex + 1; // right one column
            rawIndexArray[index++] = vertexBufferIndex + heightmap.width(); // down one row
            rawIndexArray[index++] = vertexBufferIndex + heightmap.width() + 1; // down one row and right one column
        
    

    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(9 * sizeof(float));
    positionAttribute->setCount(vertexPositions.count());
    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(9 * sizeof(float));
    normalAttribute->setCount(vertexPositions.count());
    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(9 * sizeof(float));
    colorAttribute->setCount(vertexPositions.count());
    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(indicesCount);

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

    customMeshRenderer->setInstanceCount(1);
    customMeshRenderer->setFirstVertex(0);
    customMeshRenderer->setFirstInstance(0);
    customMeshRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);
    customMeshRenderer->setGeometry(customGeometry);

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

    view.setRootEntity(sceneRoot);
    view.show();

    return app.exec();

【问题讨论】:

如果您询问的是 OpenGL ES,那么根据定义,您询问的是桌面 OpenGL。 @NicolBolas Qt 包装器的工作方式是为桌面上的 OpenGL 和嵌入式目标上的 OpenGL ES 进行编译。我会适当修改标题 @NicolBolas 我已经改写了问题以匹配标签 您是否尝试过查看它实际生成的 GL 代码?原则上,工作量看起来微不足道... @solidpixel 这是下一步。我还在原始问题中发布了片段着色器代码的链接。我不完全理解片段着色器代码对性能的影响。 【参考方案1】:

Qt 博客对针对低端硬件优化 Qt3D 应用程序有一篇非常好的文章。

https://blog.qt.io/blog/2019/04/02/optimizing-real-time-3d-entry-level-hardware/

【讨论】:

以上是关于如何使这个 OpenGL (Qt3D) 代码高效?的主要内容,如果未能解决你的问题,请参考以下文章

将 Qt3D 用于巨大的地形模型?

qtcanvas3d和qt3d的区别

使用 qt3d 渲染已经加载的网格

通过 Qt3D 模块导入 STL 文件:如何配置灯光和材质

如何使用 Qt3D 在网格中制作透明线?

如何在标准GUI应用程序中使用Qt3D进行渲染?