OpenGL Assimp 加载3D模型介绍

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++的学习之路:16、list(3)

上章有一些东西当时没学到&#xff0c;这里学到了将在补充&#xff0c;文章末附上代码&#xff0c;思维导图。 目录 一、赋值重载 二、带模板的创建 三、析构函数 四、代码 五、思维导图 一、赋值重载 这里的赋值重载就是直接利用交换函数进行把传参生成的临时数据和需要…

对称加密学习

对称加密是一种加密技术&#xff0c;它使用相同的密钥进行数据的加密和解密操作。这种加密方法因其高效性和速度优势&#xff0c;在数据加密领域得到了广泛的应用。 下面是两篇文章&#xff1a; AES加密学习-CSDN博客 加密算法学习-CSDN博客 推荐关注加密专栏&#xff1a; …

建模实例评点(6)业务流程-农业大棚

1 00:00:02,650 --> 00:00:06,000 假设这一步不是老司机来做 2 00:00:06,320 --> 00:00:08,430 主管不是老司机&#xff0c;是个小白 3 00:00:08,440 --> 00:00:09,470 比如像我这样 4 00:00:09,990 --> 00:00:11,580 潘加宇去做这个事情 5 00:00:12,460 -->…

C++感受4-HelloWorld中文版——认识编码

及时了解“编码”对编写代码的影响&#xff0c;是中国程序员越早知道越好的知识点。 一分钟了解什么叫“编码”和“解码”&#xff1b;通过实际演示&#xff0c;充分理解中文Windows下&#xff0c;C源代码编码需要注意的地方&#xff1b;通过 -finput-charsetutf8 等 g 编译配置…

如何训练自己的ChatGPT?需要多少训练数据?

近年&#xff0c;聊天机器人已经是很常见的AI技术。小度、siri、以及越来越广泛的机器人客服&#xff0c;都是聊天机器人的重要适用领域。然而今年&#xff0c;ChatGPT的面世让这一切都进行到一个全新的高度&#xff0c;也掀起了大语言模型&#xff08;LLM&#xff09;的热潮。…

麒麟系统ARM安装rabbitmq

简单记录下&#xff0c;信创服务器&#xff1a;麒麟系统&#xff0c;安装rabbitmq的踩坑记录。 本文章参考了很多大佬文章&#xff0c;我整理后提供。 一、安装基础依赖 yum -y install make gcc gcc-c kernel-devel m4 ncurses-devel openssl-devel unixODBC-devel 二、下载…

实现自动打包py及替换pyinstaller --add-data参数的方法

2024年了&#xff0c;PyInstaller已经来到了6.5.0版本&#xff0c;可我还是不会用它那个--add-data的方法&#xff0c;度了几圈试了试&#xff0c;始终不&#xff08;行&#xff09;如&#xff08;不&#xff09;意&#xff08;通&#xff09;&#xff0c;就是没能把附加文件&a…

【nodejs基础学习三-浏览器偏好设置】

系列文章目录 第一章 nodejs基础学习–注释、变量、运算符、字符串、函数&#xff08;一&#xff09; 第二章 nodejs基础学习–循环、对象字符、模块导入出&#xff08;二&#xff09; 第三章 nodejs基础学习三-浏览器设置 系列文章目录一、开发者模式二、web偏好设置 一、开发…

2021年团体程序设计天梯赛-总决赛_L1

标题&#xff1a;L1-1 人与神 题目&#xff1a; 跨界大神 L. Peter Deutsch 有一句名言&#xff1a;“To iterate is human, to recurse divine.”&#xff08;迭代的是人&#xff0c;递归的是神&#xff09;。本题就请你直接在屏幕上输出这句话。 输入格式&#xff1a; 本题没…

Linux之线程互斥与同步

1.线程互斥相关概念 临界资源&#xff1a;多线程执行流共享的资源就叫做临界资源 。 临界区&#xff1a;每个线程内部&#xff0c;访问临界自娱的代码&#xff0c;就叫做临界区。 互斥&#xff1a;任何时刻&#xff0c;互斥保证有且只有一个执行流进入临界区&#xff0c;访问临…

后端nginx使用set_real_ip_from获取用户真实IP

随着nginx的迅速崛起&#xff0c;越来越多公司将apache更换成nginx. 同时也越来越多人使用nginx作为负载均衡, 并且代理前面可能还加上了CDN加速&#xff0c;但是随之也遇到一个问题&#xff1a;nginx如何获取用户的真实IP地址. 前言&#xff1a;Nginx ngx_http_realip_module…

MapMagic 2 Splines (Beta)

请注意&#xff0c;要使用该模块则必须安装带对象模块的 MapMagic 2 才行。此模块目前正处于早期体验阶段。其功能有限。 MapMagic 2 世界生成器的官方模块。把样条线带到 MapMagic 中&#xff0c;可用于创建道路、溪流、河流或其他加长的对象。 下载&#xff1a;​​Unity资…

c 语言 斐波那契搜索(Fibonacci Search)

给定一个大小为 n 的排序数组 arr[] 和要在其中搜索的元素 x。如果 x 存在于数组中&#xff0c;则返回 x 的索引&#xff0c;否则返回 -1。 例子&#xff1a; 输入&#xff1a; arr[] {2, 3, 4, 10, 40}, x 10输出&#xff1a; 3 元素 x 出现在索引 3 处。 输入&#xff1…

数据结构-合并两个有效数组

题目描述 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意&#xff1a;最终&#xff0c;…

意得辑意得辑

你是否也曾遇到过在发表论文时英语写作水平不尽如人意的困境&#xff1f;审稿意见总是指出语言表达不够好&#xff0c;需要找英语母语者修改&#xff1f;不用担心&#xff0c;我和你一样&#xff0c;也曾历经这样的挑战。但是&#xff0c;我找到了一家值得信赖的专业润色机构—…

Apache Incubator Answer 本地开发部署

文章目录 简介Github文档插件部署 Answer开发环境编译项目初始化项目运行项目 简介 一款适合任何团队的问答平台软件。 Apache Incubator Answer是一个开源项目&#xff0c;它是一个用于构建和部署问答系统的框架。该项目是Apache软件基金会的孵化器项目&#xff0c;提供一个…

Lobe UI - 基于 AntDesign 开发的 AIGC Web 应用的开源 UI 组件库

今天推荐一个可以快速开发 ChatGPT UI 界面的组件库&#xff0c;质量很高&#xff0c;拿来就能用。 Lobe UI 是由 lobehub 团队开发的一套 web UI 组件库&#xff0c;和我之前推荐的很多通用型的 UI 组件库不同&#xff0c;Lobe UI 是专门为目前火热的 AIGC 应用开发而打造&am…

一起学习python——基础篇(13)

前言&#xff0c;python编程语言对于我个人来说学习的目的是为了测试。我主要做的是移动端的开发工作&#xff0c;常见的测试主要分为两块&#xff0c;一块为移动端独立的页面功能&#xff0c;另外一块就是和其他人对接工作。 对接内容主要有硬件通信协议、软件接口文档。而涉…

Mybatis-Plus快速入门

MyBatisPlus 通过扫描实体类&#xff0c;并基于反射获取实体类信息作为数据库信息 类名驼峰转下划线作为表名为id的字段作为主键变量名驼峰转下划线作为表的字段名 遵守这些约定MyBatisPlus就会自动生成字段&#xff0c;方便我们快速实现 一、快速入门 起步依赖 MyBatisPlus…