如何使这个 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) 代码高效?的主要内容,如果未能解决你的问题,请参考以下文章