使用Assimp实现gltf到obj的转换

Assimp,全称为Asset Import Library,是一个能处理并转换多种三维模型格式的库,提供C/C++接口。本文主要讲述如何利用它实现gltf到obj文件的转换。

Assimp的安装

在ubuntu下,Assimp同时提供apt和源代码编译两种安装方式。
apt方式:

sudo apt-get update
sudo apt-get install libassimp-dev

源代码编译方式:

git clone https://github.com/assimp/assimp.git
cd assimp
cmake CMakeLists.txt 
cmake --build .
make install

推荐使用源代码编译的方式,因为github可以拉到最新的代码,而apt里安装的是较旧的版本。

gltf到obj的转换

我们这里主要讨论的是带纹理的gltf文件,其中纹理被嵌在gltf文件中,不以单独文件形式存在。Assimp提供了多种格式的import和多种格式的export。但是在使用过程中发现,直接调用其接口导出的obj文件只有网格信息,是不包含纹理图片的,同时材质mtl文件中纹理图片的路径会被表示为*0, *1等(mtl文件中map_Kd 那行)。总之是不能直接得到可直接使用的obj文件。为了利用Assimp得到完整的obj文件,需要完善两步,第一步是将嵌在gltf文件中的纹理文件(embedded texture)存成图片,第二步就是在mtl文件中做正确的关联。

读取gltf文件

#include <assimp/material.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <omp.h>
#include <algorithm>
#include <assimp/Exporter.hpp>
#include <assimp/Importer.hpp>

    Assimp::Importer importer;
    const aiScene *scene = importer.ReadFile(
        std::string(meshFile),
        aiProcess_CalcTangentSpace | aiProcess_Triangulate |
            aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);
    if (scene == nullptr) {
        std::cout << importer.GetErrorString() << std::endl;
    }

纹理文件的存储

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

    std::vector<std::string> textureFileNames;
    for (int i = 0; i < scene->mNumTextures; i++) {
        const aiTexture *texture = scene->mTextures[i];
        if (texture != nullptr) {
            unsigned char *image_data = nullptr;
            int width, height, channels;
            if (texture->mHeight == 0) {
                // mHeight = 0 means embedded textures inside
                // here we use stb to save texture images
                image_data = stbi_load_from_memory(
                    reinterpret_cast<unsigned char *>(texture->pcData),
                    texture->mWidth, &width, &height, &channels, 0);

                std::string filename = std::to_string(i) + ".jpg";
                if (texture->mFilename.length > 0) {
                    filename = std::string(texture->mFilename.data) + ".jpg";
                }
                stbi_write_jpg(filename.c_str(), width, height, channels,
                               image_data, 100);
                textureFileNames.push_back(filename);
            }
        }
    }

这里需要说明的几个地方

  1. 在Assimp中,mHeight = 0意味着纹理存在形式为embedded,这种方式在于把纹理图片文件直接以整体一段buffer的方式存在gltf文件中,当需要获取纹理文件时,需要借助第三方的库,如stb_image,libjpeg等,将这一段buffer转成图片文件。
  2. 有的纹理文件被嵌入gltf后,文件名的信息会被保留,可以通过texture->mFilename获得,文件名后缀可通过texture->achFormatHint得到,但有时文件名的信息并不存在,对应texture->mFilename为空,此时我们可以按照自己的要求命名,只需要后续的材质文件能对应上即可。

stb_image库

stb_image是一个简单的图像解码库,使用起来极其简单,实现都写在头文件中,不需要编译成库,项目中直接引用头文件目录即可。下载地址:https://github.com/nothings/stb 。在我们的示例中,只需要使用stb_image.h和stb_image_write.h两个文件,头文件引用方式可以参考上方的代码,前面的宏必须带上。

材质信息的对应

    for (int i = 0; i < scene->mNumMaterials; i++) {
        aiMaterial *material = scene->mMaterials[i];
        aiString s;
        if (AI_SUCCESS == material->Get(AI_MATKEY_TEXTURE_DIFFUSE(0), s)) {
            if (s.data[0] == '*') {
                std::string textureName = std::string(s.data);
                std::string subName =
                    textureName.substr(1, textureName.length() - 1);
                int textureId = std::stoi(subName);
                if (textureId < textureFileNames.size()) {
                    s.Set(textureFileNames[textureId].c_str());
                    material->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0));
                }
            }
        }
    }

在Assimp读出来的material信息里,有一项对应的就是纹理图像的路径(或者文件名),对应就是上述代码中的s。当s以号开头时,在号后面的字符代表的是在texture数组里的index,我们可以按照这个index找到之前存储的对应的纹理文件名称,并同时修改材质的属性即可。

obj文件导出

    Assimp::Exporter exporter;
    std::string formatStr = "obj";
    std::string outMeshFile = "export." + formatStr;
    exporter.Export(scene, formatStr.c_str(), outMeshFile.c_str());

其他

本文只讨论了gltf到obj的转换,其实思路可以拓展到gltf/fbx到obj/ply等其他格式的转换,只要理清了texture的存储以及texture与material的对应关系,都可以做到带纹理的三维模型格式转换。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容