OpenGL Assimp 加载3D模型介绍
Assimp对应模型结构体解说
-
所有的模型、场景数据都包含在scene对象中,如所有的材质和Mesh。同样,场景的根节点引用也包含在这个scene对象中
-
场景的Root node(根节点)可能也会包含很多子节点和一个指向保存模型点云数据mMeshes[]的索引集合。根节点上的mMeshes[]里保存了实际了Mesh对象,而每个子节点上的mMesshes[]都只是指向根节点中的mMeshes[]的一个引用
-
一个Mesh对象本身包含了渲染所需要的所有相关数据,像是顶点位置、法向量、纹理坐标、面(Face)和物体的材质。
-
一个Mesh会包含多个面片。一个Face(面片)表示渲染中的一个最基本的形状单位,即图元(基本图元有点、线、三角面片、矩形面片)。一个面片记录了一个图元的顶点索引,通过这个索引,可以在mMeshes[]中寻找到对应的顶点位置数据。顶点数据和索引分开存放,可以便于我们使用缓存(VBO、NBO、TBO、IBO)来高速渲染物体
-
一个Mesh还会包含一个Material(材质)对象用于指定物体的一些材质属性。如颜色、纹理贴图(漫反射贴图、高光贴图等)
所以第一步加载一个模型文件为scene对象,获取根节点,通过根节点获取每个节点对应的Mesh对象(递归搜索每个节点的子节点来获取所有的节点),并处理每个Mesh对象对应的顶点数据、索引以及它的材质属性。得到一个只包含我们需要的数据的Mesh集合。
Assimp调用解析流程介绍
importer.ReadFile
进行加载模型获取aiScene
对象,这个所有数据来源,后面只要解析aiScene
,解析对应节点,所要每个节点材质和aiMesh对应的顶点、纹理、法向量、网格面、骨骼信息
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>int loadModel() {Assimp::Importer importer;const aiScene* scene = importer.ReadFile("model.FBX", aiProcess_Triangulate);// process model data// ...return 0;
}
scene->mRootNode
相当一个aiNode
,获取根节点,然后递归找出所有子节点,AssimpMesh3DS
为了解析aiMesh
void processNode(aiNode *node, const aiScene *scene, std::string& directory) {for(unsigned int i = 0; i < node->mNumMeshes; i++) {aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];LOGE("=====Operator3DS=====mesh->mName====: %s", mesh->mName.C_Str());AssimpMesh3DS *assimpMesh = new AssimpMesh3DS();assimpMesh->setServiceObjRef(this);if ( assimpMesh->init(mesh, scene, m_boneNumber, m_boneOffsets, m_boneMapping, m_paramRef, directory) ) {m_meshes.push_back( assimpMesh );}}for(unsigned int i = 0; i < node->mNumChildren; i++) {LOGE("=====Operator3DS=====mChildren->mName====: %s", node->mChildren[i]->mName.C_Str());processNode(node->mChildren[i], scene, directory);}
}
processMaterial
方法处理各种材质生成相应的纹理D是环境光材质、N是顶点法向量数据材质,S是反色光材质,根据自己路径进行加载相关材质生成纹理,mesh->mNumVertices
进行顶点坐标、纹理坐标等处理,mesh->mNumFaces
是网格面处理,mesh->mNumBones
是骨骼处理
void AssimpMesh3DS::processMaterial(_3DModelParam* param, std::string& directory, Texture*& texture, aiMaterial* material, aiTextureType type) {aiString str;LOGE("====Operator3DS===aiTextureType=%d======",type);if ( material->GetTexture(type, 0, &str) == aiReturn_SUCCESS ) {std::string path = str.C_Str();// LOGE("====Operator3DS===111=path=%s======",path.c_str());size_t pos = path.find_last_of("\\");if (pos == std::string::npos) {pos = path.find_last_of("/");}if (pos != std::string::npos) {path = path.substr(pos + 1, str.length);}path = directory + path;LOGE("====Operator3DS===2222=path=%s======",path.c_str());if ( FileCheck::checkFileExist(path) ) {if (type == aiTextureType_DIFFUSE && param->frameInfo.size() == 5 && param->radomObjcet.empty()) {FrameParam frame;frame.preloadFrameNumber = param->frameInfo[0];frame.cacheNumberMax = param->frameInfo[1];frame.firstFrameExtend = param->frameInfo[2];frame.lastFrameExtend = param->frameInfo[3];frame.frameNumber = param->frameInfo[4];frame.path = path;m_diffuseCache = m_bufferService->createTextureCache(frame, param->framefps, true);} else if (param->radomObjcet == path) {texture = m_bufferService->createTexture(param->radomPath);} else {texture = m_bufferService->createTexture(path);}}}
}bool AssimpMesh3DS::init(aiMesh *mesh, const aiScene *scene, short& boneNumber, std::vector<aiMatrix4x4>& boneOffsets, std::map<std::string, uint16_t>& boneMapping, _3DModelParam* param, std::string& directory) {if (!mesh->HasPositions()) {return false;}m_defines = "";m_materialName = "";m_parame = param;// 材质if (scene->HasMaterials()) {aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];m_materialName = material->GetName().C_Str();LOGE("====Operator3DS====m_materialName=%s======",m_materialName.c_str());processMaterial(param, directory, m_diffuse, material, aiTextureType_DIFFUSE);processMaterial(param, directory, m_normals, material, aiTextureType_NORMALS);processMaterial(param, directory, m_specular, material, aiTextureType_SPECULAR);processMaterial(param, directory, m_reflection, material, aiTextureType_OPACITY);processMaterial(param, directory, m_emissive, material, aiTextureType_AMBIENT);if (m_normals) {LOGE("====Operator3DS==m_normals====");GLUtils::addDefine(m_defines, "DEFINE_NORMALS");}if (m_specular) {LOGE("====Operator3DS==m_specular====");GLUtils::addDefine(m_defines, "DEFINE_SPECULAR");}if (m_reflection) {GLUtils::addDefine(m_defines, "DEFINE_REFLECTION");LOGE("====Operator3DS==m_reflection====");}if (m_emissive) {GLUtils::addDefine(m_defines, "DEFINE_EMISSIVE");LOGE("====Operator3DS==m_emissive====");}}std::vector<GLushort> indices;std::vector<AssimpVertex3DS> vertices;LOGE("====Operator3DS====mNumVertices=%d==%d==%d==",mesh->mNumVertices,mesh->mNumFaces,mesh->mNumBones);for(unsigned int i = 0; i < mesh->mNumVertices; i++) {AssimpVertex3DS vertex;// 顶点坐标vertex.position.x = mesh->mVertices[i].x;vertex.position.y = mesh->mVertices[i].y;vertex.position.z = mesh->mVertices[i].z;// 纹理坐标if (mesh->HasTextureCoords(0)) {vertex.texcoord.x = mesh->mTextureCoords[0][i].x;vertex.texcoord.y = mesh->mTextureCoords[0][i].y;}// 法线坐标if (mesh->HasNormals()) {vertex.normal.x = mesh->mNormals[i].x;vertex.normal.y = mesh->mNormals[i].y;vertex.normal.z = mesh->mNormals[i].z;}// 切线坐标,有法线贴图时,才处理切线坐标if (m_normals && mesh->HasTangentsAndBitangents()) {vertex.tangent.x = mesh->mTangents[i].x;vertex.tangent.y = mesh->mTangents[i].y;vertex.tangent.z = mesh->mTangents[i].z;
// vertex.bitangent.x = mesh->mBitangents[i].x;
// vertex.bitangent.y = mesh->mBitangents[i].y;
// vertex.bitangent.z = mesh->mBitangents[i].z;}if(param->Filter3DType==Filter_3DSticker_Face){vertices_3d.push_back(vertex);}else{vertices.push_back(vertex);}}// 网格的面数,顶点数for(unsigned int i = 0; i < mesh->mNumFaces; i++) {m_indexCount += mesh->mFaces[i].mNumIndices;indices.push_back(mesh->mFaces[i].mIndices[0]);indices.push_back(mesh->mFaces[i].mIndices[1]);indices.push_back(mesh->mFaces[i].mIndices[2]);}if (m_indexCount > 65535) { //使用unsigned short, 精度范围 0 ~ 65536LOGW("AssimpMesh3DS::init: too many trangles, max 65535, current %d", m_indexCount);}// 骨骼for (unsigned int i = 0; i < mesh->mNumBones; i++) {unsigned short boneIndex = 0;auto bone = mesh->mBones[i];std::string boneName(bone->mName.data);if (boneMapping.find(boneName) == boneMapping.end()) {boneIndex = boneNumber++;boneMapping[boneName] = boneIndex;boneOffsets.push_back(bone->mOffsetMatrix);} else {boneIndex = boneMapping[boneName];}for (unsigned int j = 0 ; j < bone->mNumWeights ; j++) {unsigned int vertexID = bone->mWeights[j].mVertexId;float Weight = bone->mWeights[j].mWeight;// 限制每个顶点,受影响的骨骼数(权重),不超过4个vertices[vertexID].addBoneData(boneIndex, Weight);}}//新3d方式,顶点数据作为全局变量保存,后面需要再调用if(param->Filter3DType==Filter_3DSticker_Face){setupMesh(indices, vertices_3d);}else{setupMesh(indices, vertices);}return true;
}