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

How to make this OpenGL (Qt3D) code efficient?

我写了一个小程序来渲染 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();
}

Qt 博客在针对 low-end 硬件优化 Qt3D 应用程序方面有很好的 write-up。

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