OpenGL Assimp 加载3D模型介绍

news2025/2/27 21:59:01

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1585192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微服务面试题一

1.SOA、分布式、微服务之间有什么关系和区别&#xff1f; 分布式架构是指将单体架构中的各个部分拆分&#xff0c;然后部署不同的机器或进程中去&#xff0c;SOA和微服务基 本上都是分布式架构的SOA是⼀种⾯向服务的架构&#xff0c;系统的所有服务都注册在总线上&#xff0c;…

“植物大战僵尸:机器学习引领游戏AI新革命“

上期回顾&#xff1a; 今天给大家推荐一个Gtihub开源项目&#xff1a;PythonPlantsVsZombies&#xff0c;翻译成中就是植物大战僵尸。 《植物大战僵尸》是一款极富策略性的小游戏。可怕的僵尸即将入侵&#xff0c;每种僵尸都有不同的特点&#xff0c;例如铁桶僵尸拥有极强的抗…

开源推荐榜【Casbin.NET 一个为.NET(C#)项目设计的高效且强大的访问控制库】

Casbin 是一个强大高效的开源访问控制库&#xff0c;支持各种 访问控制模型 , 如 ACL, RBAC, ABAC 等。 我们希望可以让指定的主体 subject&#xff0c;可以访问指定的资源 object&#xff0c;访问可以是读和写。这就是 Casbin 使用最广泛的方式。也称为 { subject, object, ac…

【MATLAB源码-第20期】基于matlab的短波通信多径信道仿真,多径数目为3,用QPSK来测试误码率效果输出误码率对比图。

1、算法描述 短波通信是一种使用短波频段的无线电通信技术&#xff0c;它具有以下特点&#xff1a; 1. 频段范围&#xff1a;短波通信通常使用3MHz到30MHz之间的频段。这个频段之所以称为“短波”&#xff0c;是因为它的波长相对较短&#xff0c;适合进行长距离通信。 2. 长距…

大语言模型的指令调优:综述

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 论文标题&#xff1a;Instruction Tuning for Large Language Models: A Survey 论文地址&#xff1a;https://arxiv.org/abs/2308.10792 指令调优是提升大语言模型&#xff08;LLMs&#xff09;性能…

前端招聘:几招识别注水简历,不懂技术也能挤出水分。

现在的前端开发市场鱼目混珠&#xff0c;尤其是某些培训机构经常鼓动学院美&#xff08;造&#xff09;化&#xff08; 假&#xff09;简历&#xff0c;搞得很多前端招聘经验缺乏&#xff0c;又急需用人单位公司损失不小&#xff0c;满以为找到合适的人员了&#xff0c;其实是频…

倍思和南卡开放式耳机哪个好?深度测评剖析实力王!

​开放式耳机凭借其新颖的佩戴设计和优秀的音质体验&#xff0c;正逐渐赢得越来越多消费者的喜爱。在众多品牌中&#xff0c;南卡、倍思等业界巨头推出的开放式耳机尤为引人注目。这些耳机是否真的名副其实&#xff1f;它们又有哪些特色和优缺点&#xff1f;为了解答这些问题&a…

Canal扩展篇

1.Canal介绍 Canal官网&#xff1a;https://github.com/alibaba/canal Canal 主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据订阅和消费&#xff0c;工作原理如下&#xff1a; Canal 模拟 MySQL slave 的交互协议&#xff0c;伪装自己为 MySQL slave &…

FPGA - 以太网UDP通信(一)

一&#xff0c;简述以太网 以太网简介 ​以太网是一种计算机局域网技术。IEEE组织的IEEE 802.3标准制定了以太网的技术标准&#xff0c;它规定了包括物理层的连线、电子信号和介质访问层协议的内容。 ​ 以太网类型介绍 以太网是现实世界中最普遍的一种计算机网络。以太网有…

流程图高亮跟踪 可自定义渲染图片

【流程跟踪】获取流程定义图 获取代码如下&#xff1a; ProcessDefinition processDefinition repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave").singleResult();String diagramResourceName processDefinition.getDiagramResour…

恒流电源驱动惠斯通电桥输出电压计算

笔记 并联分流/串联分压贯穿始终 先通过式子计算每条路流经的电流大小 &#xff08;1-(支路电阻)/(所有电阻) &#xff09;* 电流源I 每条之路电流大小知道了。就可以知道每条路中间点输出 支路电流*电阻。 输出即&#xff1a;中间点输出电位差。同理可以通过阻值变化推…

力扣HOT100 - 240. 搜索二维矩阵 II

解题思路&#xff1a; 从左下角开始&#xff0c;根据条件删除行和列。 class Solution {public boolean searchMatrix(int[][] matrix, int target) {int row matrix.length - 1;int col matrix[0].length - 1;int l 0;while (row > 0 && l < col) {if (targ…

深入理解java虚拟机-类的生命周期-初始化阶段

针对P2的字节码解释&#xff1a; 操作数栈里面放1&#xff0c;然后对Demo1.value的static变量进行赋值操作&#xff0c;从栈里面拿1赋值给Demo1.value的static变量 P3的情况由于源码是把 public static int value 1; static {value 2; }颠倒为&#xff1a; static {value …

2024电力、电网与智能应用技术国际学术会议(ICPGIAT2024)

2024电力、电网与智能应用技术国际学术会议(ICPGIAT2024) 会议简介 2024年国际电力、电网和智能应用技术大会&#xff08;ICPGIAT 2024&#xff09;将在中国杭州举行&#xff0c;就“电力系统”、“智能控制”和“智能应用技术”等研究主题进行讨论和交流。会议围绕智能系统…

第十二届蓝桥杯大赛软件赛省赛Java 大学 B 组题解

1、ASC public class Main {public static void main(String[] args) {System.out.println(

药店药品进销存管理系统软件可以对有效期管理查询以及对批号库存管理

药店药品进销存管理系统软件可以对有效期管理查询以及对批号库存管理 一、前言 以下软件操作教程以&#xff0c;佳易王药店药品进销存管理软件为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 软件可以对药品有效期进行管理查询&#xff0c;可以…

AI大模型探索之路-应用篇8:Langchain框架LangServe模块-专注于AI模型的部署

目录 前言 一、概述 二、功能特性 三、REST API 开发 四、Postman调用测试 五、Client调用测试 总结 前言 随着AI大语言模型&#xff08;LLM&#xff09;的技术的不断演进&#xff0c;AI应用的开发和部署变得越来越复杂。在这样的背景下&#xff0c;LangServe应运而生—…

java的aop实现方式

java的aop可以通过三种方式来实现 1.使用ajc编译的插件来完成增强 这种方法会直接修改编译完成的class文件&#xff0c;不需要依赖springboot 2.动态代理proxy 2.1cglib 可以代理接口&#xff0c;方法等&#xff0c;这种方式代理类是目标类的子类&#xff0c;要求目标类不…

技术图纸受控下发软件,知名的技术图纸管理软件

技术图纸受控下发软件是一种专门用于管理和控制技术图纸下发的工具。这种软件的主要目的是确保技术图纸的安全、准确和受控的下发&#xff0c;同时提高图纸管理的效率和便利性。 以下是技术图纸受控下发软件的一些主要功能和特点&#xff1a; 权限管理&#xff1a;软件能够设置…

python基础——MySQL

文章目录 一、引入pymysql二、使用三、执行非查询性质的SQL语句四、执行查询性质的SQL语句五、数据插入 一、引入pymysql 除了使用图形化工具以外&#xff0c;我们也可以使用编程语言来执行SQL从而操作数据库。 在Python中&#xff0c;使用第三方库:pymysql来完成对MySQL数据库…