欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 维修 > 使用Assimp加载glb/gltf文件,然后使用Qt3D来渲染

使用Assimp加载glb/gltf文件,然后使用Qt3D来渲染

2025/6/22 17:00:38 来源:https://blog.csdn.net/joyopirate/article/details/142326083  浏览:    关键词:使用Assimp加载glb/gltf文件,然后使用Qt3D来渲染

文章目录

  • 1.代码
  • 2.说明
    • 2.1.调用
    • 2.2.关于贴图

1.代码

ModelLoader.h

#ifndef MODELLOADER_H
#define MODELLOADER_H#include <QObject>
#include <Qt3DRender>
#include <QVector3D>
#include <QGeometry>#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>struct Triangle
{int vertexIndex1 { 0 };int vertexIndex2 { 0 };int vertexIndex3 { 0 };bool operator==(const Triangle &rhs) const {return vertexIndex1 == rhs.vertexIndex1 &&vertexIndex2 == rhs.vertexIndex2 &&vertexIndex3 == rhs.vertexIndex3;}
};namespace RenderAttributes
{
Qt3DRender::QAttribute *create(const QVector<Triangle> &triangles, Qt3DRender::QGeometry *parent);
Qt3DRender::QAttribute *create(const QVector<QVector3D> &vertices, const QString &name, Qt3DRender::QGeometry *parent);
Qt3DRender::QAttribute *clone(Qt3DRender::QAttribute *from, Qt3DRender::QGeometry *parent);
}class ModelLoader
{
public:ModelLoader();// 加载模型static int loadModelFromFile(Qt3DCore::QEntity *rootEntity, QString filePath);private:static int processNode(aiNode *node, const aiScene *scene, Qt3DCore::QEntity *entity);static int processMesh(aiMesh *mesh, const aiScene *scene, Qt3DCore::QEntity *entity);
};class MyQPaintedTextureImage : public Qt3DRender::QPaintedTextureImage
{
public:void setImage(QImage &i){image = i;setSize(i.size());}virtual void paint(QPainter *painter) override{painter->drawImage(0, 0, image);}private:QImage image;
};
#endif // MODELLOADER_H

ModelLoader.cpp

#include "modelloader.h"
#include <Qt3DExtras>
#include <Qt3DRender>
#include <QPaintedTextureImage>Qt3DRender::QAttribute *RenderAttributes::create(const QVector<Triangle> &triangles, Qt3DRender::QGeometry *parent)
{auto attribute = new Qt3DRender::QAttribute(parent);QVector<uint> indices;indices.reserve(triangles.size() * 3);for (const Triangle &triangle : triangles) {indices << static_cast<uint>(triangle.vertexIndex1)<< static_cast<uint>(triangle.vertexIndex2)<< static_cast<uint>(triangle.vertexIndex3);}Qt3DRender::QBuffer *dataBuffer = new Qt3DRender::QBuffer(attribute);const int rawSize = indices.size() * static_cast<int>(sizeof(uint));auto rawData = QByteArray::fromRawData(reinterpret_cast<const char*>(indices.constData()), rawSize);rawData.detach();dataBuffer->setData(rawData);attribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute);attribute->setBuffer(dataBuffer);attribute->setVertexBaseType(Qt3DRender::QAttribute::UnsignedInt);attribute->setVertexSize(1);attribute->setByteOffset(0);attribute->setByteStride(sizeof(uint));attribute->setCount(static_cast<uint>(indices.size()));return attribute;
}
Qt3DRender::QAttribute *RenderAttributes::create(const QVector<QVector3D> &vertices, const QString &name, Qt3DRender::QGeometry *parent)
{auto attribute = new Qt3DRender::QAttribute(parent);QVector<float> values;values.reserve(vertices.size() * 3);for (const QVector3D &v : vertices) {values << v.x() << v.y() << v.z();}Qt3DRender::QBuffer *dataBuffer = new Qt3DRender::QBuffer(attribute);const int rawSize = values.size() * static_cast<int>(sizeof(float));auto rawData = QByteArray::fromRawData(reinterpret_cast<const char*>(values.constData()), rawSize);rawData.detach();dataBuffer->setData(rawData);attribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);attribute->setBuffer(dataBuffer);attribute->setVertexBaseType(Qt3DRender::QAttribute::Float);attribute->setVertexSize(3);attribute->setByteOffset(0);attribute->setByteStride(3 * sizeof(float));attribute->setName(name);attribute->setCount(static_cast<uint>(vertices.size()));return attribute;
}uint qHash(const QVector3D &key, uint seed = 0)
{if (key.isNull()){return seed;}else{float array[3] = {key.x(), key.y(), key.z()};return qHashBits(array, 3 * sizeof(int), seed);}
}Qt3DRender::QAttribute *RenderAttributes::clone(Qt3DRender::QAttribute *from, Qt3DRender::QGeometry *parent)
{auto attribute = new Qt3DRender::QAttribute(parent);Qt3DRender::QBuffer *dataBuffer = new Qt3DRender::QBuffer(attribute);auto dataCopy = from->buffer()->data();dataCopy.detach();dataBuffer->setData(dataCopy);attribute->setAttributeType(from->attributeType());attribute->setBuffer(dataBuffer);attribute->setVertexBaseType(from->vertexBaseType());attribute->setVertexSize(from->vertexSize());attribute->setByteOffset(from->byteOffset());attribute->setByteStride(from->byteStride());attribute->setName(from->name());attribute->setCount(from->count());return attribute;
}ModelLoader::ModelLoader() {}int ModelLoader::loadModelFromFile(Qt3DCore::QEntity *rootEntity, QString filePath)
{Assimp::Importer importer;std::string modelPath = filePath.toStdString();const aiScene* sceneObjPtr = importer.ReadFile(modelPath,aiProcess_Triangulate | aiProcess_FlipUVs);// const aiScene* sceneObjPtr = importer.ReadFile(filePath,//                                                aiProcess_FlipUVs);if (!sceneObjPtr|| sceneObjPtr->mFlags == AI_SCENE_FLAGS_INCOMPLETE|| !sceneObjPtr->mRootNode){// 加载模型失败qDebug() << "Error:Model::loadModel, description: "<< importer.GetErrorString();return -1;}qDebug() << "load object:" << sceneObjPtr->mNumMeshes << sceneObjPtr->mNumAnimations;// 是否存在动画if(sceneObjPtr->HasAnimations()){aiAnimation* animation = sceneObjPtr->mAnimations[0];qDebug() << animation->mDuration << animation->mTicksPerSecond << animation->mName.C_Str();}// 是否存在材质if(sceneObjPtr->HasMaterials()){qDebug() << sceneObjPtr->mMaterials[0]->GetName().C_Str();}// 是否存在网格模型if(sceneObjPtr->HasMeshes()){qDebug() << sceneObjPtr->mMeshes[0]->mName.C_Str();}Qt3DCore::QEntity *modelEntity = new Qt3DCore::QEntity(rootEntity);// 修正一下坐标系Qt3DCore::QTransform *mTransform = new Qt3DCore::QTransform();mTransform->setRotationX(-90);modelEntity->addComponent(mTransform);return processNode(sceneObjPtr->mRootNode, sceneObjPtr, modelEntity);
}int ModelLoader::processNode(aiNode *node, const aiScene *scene, Qt3DCore::QEntity *entity)
{qDebug() << "node:" << node->mName.C_Str() << node->mNumMeshes << node->mNumChildren;Qt3DCore::QEntity *partModel = new Qt3DCore::QEntity(entity);Qt3DCore::QTransform *partTransform = new Qt3DCore::QTransform;aiMatrix4x4 mat = node->mTransformation;QMatrix4x4 qMat = QMatrix4x4(mat.a1, mat.a2, mat.a3, mat.a4,mat.b1, mat.b2, mat.b3, mat.b4,mat.c1, mat.c2, mat.c3, mat.c4,mat.d1, mat.d2, mat.d3, mat.d4);partTransform->setMatrix(qMat);partModel->addComponent(partTransform);partModel->setObjectName(node->mName.C_Str()); // 这个很有用,可以用来后期对节点进行查找// qDebug() << partTransform->rotationX()//          << partTransform->rotationY()//          << partTransform->rotationZ();// process each mesh located at the current nodefor (unsigned int i = 0; i < node->mNumMeshes; i++){// the node object only contains indices to index the actual objects in the scene.// the scene contains all the data, node is just to keep stuff organized (like relations between nodes).aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];processMesh(mesh, scene, partModel);}// after we've processed all of the meshes (if any) we then recursively process each of the children nodesfor (unsigned int i = 0; i < node->mNumChildren; i++){processNode(node->mChildren[i], scene, partModel);}return 0;
}int ModelLoader::processMesh(aiMesh *mesh, const aiScene *scene, Qt3DCore::QEntity *entity)
{qDebug() << "---> mesh:" << mesh->mName.C_Str();;Qt3DRender::QGeometry *geometry = new Qt3DRender::QGeometry(entity);Qt3DRender::QGeometryRenderer *geoRen = new Qt3DRender::QGeometryRenderer(entity);geoRen->setGeometry(geometry);geoRen->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles);// 如果网格包含法线,则添加法线节点if(mesh->HasNormals()){// qDebug() << "has normal:" << mesh->mNumVertices;// 法线数据QVector<QVector3D> normals;for (unsigned int i = 0; i < mesh->mNumVertices; i++){aiVector3D normal = mesh->mNormals[i];normals << QVector3D(normal.x, normal.y, normal.z);}auto attr = RenderAttributes::create(normals, "vertexNormal", geometry);geometry->addAttribute(attr);}// 如果网格包含纹理坐标,则添加纹理坐标节点if(mesh->HasTextureCoords(0)){// qDebug() << "NumUVComponents :" << mesh->mNumUVComponents;// if(mesh->mNumUVComponents > 0)// {//     qDebug() << "has texture"//              << mesh->mTextureCoordsNames[0]->C_Str()//         ;// }QVector<QVector3D> textureCoordinates;for (unsigned int i = 0; i < mesh->mNumVertices; i++){aiVector3D texCoord = mesh->mTextureCoords[0][i];textureCoordinates << QVector3D(texCoord.x, texCoord.y, texCoord.z);}auto attrTC = RenderAttributes::create(textureCoordinates, Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName(), geometry);geometry->addAttribute(attrTC);}// 将Assimp网格的顶点坐标添加到坐标节点// qDebug() << "vertices:" << mesh->mNumVertices;QVector<QVector3D> vertices;for (unsigned int i = 0; i < mesh->mNumVertices; i++){aiVector3D vertice = mesh->mVertices[i];vertices << QVector3D(vertice.x, vertice.y, vertice.z);}auto attrVeti = RenderAttributes::create(vertices, Qt3DRender::QAttribute::defaultPositionAttributeName(), geometry);geometry->addAttribute(attrVeti);geometry->setBoundingVolumePositionAttribute(attrVeti);// 将Assimp网格的面索引添加到面索引// qDebug() << "faces:" << mesh->mNumFaces;QVector<Triangle> faces;for (unsigned int i = 0; i < mesh->mNumFaces; i++){Triangle triFace;aiFace face = mesh->mFaces[i];triFace.vertexIndex1 = face.mIndices[0];triFace.vertexIndex2 = face.mIndices[1];triFace.vertexIndex3 = face.mIndices[2];faces << triFace;// for (unsigned int j = 0; j < face.mNumIndices; j++) // 填充面的索引// {//     faceSet->coordIndex.set1Value(faceSet->coordIndex.getNum(), face.mIndices[j]);// }}auto attrFace = RenderAttributes::create(faces, geometry);geometry->addAttribute(attrFace);// 设置材质if(mesh->mMaterialIndex >= 0){qDebug() << "set material";aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];// 创建材质节点// // 使用QMetalRoughMaterial时,貌似亮度有点不对// Qt3DExtras::QMetalRoughMaterial *qMaterial = new Qt3DExtras::QMetalRoughMaterial();// qMaterial->setMetalness(0.4);// qMaterial->setRoughness(0.55);Qt3DExtras::QDiffuseMapMaterial *qMaterial = new Qt3DExtras::QDiffuseMapMaterial();// 设置材质漫反射度float shiness;if (AI_SUCCESS == material->Get(AI_MATKEY_SHININESS, shiness)) {qDebug() << "shiness:" << shiness;// 使用 shiness 值qMaterial->setShininess(shiness);}// 设置材质漫反射颜色aiColor4D diffuseColor;if(AI_SUCCESS == material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor)){qDebug() << "rgb:" << diffuseColor.r << diffuseColor.g << diffuseColor.b;QColor tmpColor = QColor(diffuseColor.r * 255.0,diffuseColor.g * 255.0,diffuseColor.b * 255.0);qDebug() << tmpColor << QColor(255, 255, 255);// qMaterial->setBaseColor(tmpColor);QImage img(100, 100, QImage::Format_ARGB32);img.fill(tmpColor);MyQPaintedTextureImage *txtImg = new MyQPaintedTextureImage();txtImg->setImage(img);Qt3DRender::QTexture2D *txt2d = new Qt3DRender::QTexture2D();txt2d->addTextureImage(txtImg);// qMaterial->setBaseColor(QVariant::fromValue(txt2d));qMaterial->setDiffuse(txt2d);}// 设置环境颜色aiColor4D ambientColor;if(AI_SUCCESS == material->Get(AI_MATKEY_COLOR_AMBIENT, ambientColor)){QColor tmpColor = QColor(ambientColor.r, ambientColor.g, ambientColor.b, ambientColor.a);qDebug() << "ambient color:" << tmpColor;qMaterial->setAmbient(tmpColor);}else{qMaterial->setAmbient(QColor(128, 128, 128));}// 如果材质包含纹理,则创建纹理节点aiString texturePath; //Assimp texture file pathif(AI_SUCCESS == material->GetTexture(aiTextureType_DIFFUSE, 0, &texturePath)){qDebug() << "texture file:" << texturePath.C_Str();const aiTexture *embTexture = scene->GetEmbeddedTexture(texturePath.C_Str());if(embTexture) // 假如内嵌的纹理{qDebug() << "has embed img:"<< (char*)embTexture->achFormatHint<< embTexture->mFilename.C_Str()<< embTexture->mWidth << embTexture->mHeight;// 利用QPaintedTextureImage可以实现加载内存中的图片const unsigned char* data = reinterpret_cast<const unsigned char*>(embTexture->pcData);const size_t size = embTexture->mWidth;QByteArray imageData((char*)data, size);QImage img;if(img.loadFromData(imageData)){qDebug() << img;MyQPaintedTextureImage *txtImg = new MyQPaintedTextureImage();txtImg->setImage(img);Qt3DRender::QTexture2D *txt2d = new Qt3DRender::QTexture2D();txt2d->addTextureImage(txtImg);// qMaterial->setBaseColor(QVariant::fromValue(txt2d));qMaterial->setDiffuse(txt2d);}}else{qDebug() << "-----no embed img";}}else{qDebug() << "get texture fail" << material->GetTextureCount(aiTextureType_DIFFUSE);}entity->addComponent(qMaterial);}entity->addComponent(geoRen);return 0;
}

2.说明

2.1.调用

Qt3DExtras::Qt3DWindow *view = new Qt3DExtras::Qt3DWindow();
view->defaultFrameGraph()->setClearColor(QColor(QRgb(0x2b2b2b)));QString filePath = "myModel.glb";
Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity();
ModelLoader::loadModelFromFile(rootEntity, filePath);view->setRootEntity(rootEntity);// 灯光及其他设置

2.2.关于贴图

假如使用QTextureImage的话,只能加载本地图片,但是参考【How can I load a QPaintedTextureImage into a QTextureMaterial?】,可以使用QPaintedTextureImage来加载内存中的图片。而glb/gltf文件本身包含了图片内容,读取glb文件时,图片内容已经加载到内存中了,因此QPaintedTextureImage更加适合。


参考
【Qt3DExample】
【How can I load a QPaintedTextureImage into a QTextureMaterial?】

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词