OpenGL Assimp 加载3D模型介绍

本文介绍了如何使用Assimp库在OpenGL中加载3D模型,涉及scene对象的结构、Mesh对象的顶点数据、索引、材质处理,以及递归解析模型节点的过程。重点讲解了aiMesh、aiNode和aiMaterial的交互以及如何处理纹理和骨骼信息。
摘要由CSDN通过智能技术生成

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 ~ 65536
        LOGW("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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baoyu45585

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值