1. 程式人生 > 其它 >使用 Assimp 庫載入 3D 模型

使用 Assimp 庫載入 3D 模型

前言

要想讓自己的 3D 之旅多一點樂趣,肯定得想辦法找一些有意思一點的 3D 模型。3D 模型有各種各樣的格式,obj的,stl的,fbx的等等不一而足。特別是 obj 格式的 3D 模型,完全是純文字格式,網路上很多高手都喜歡自己寫程式對 obj 格式的模型檔案進行解析。我自己收集了一些 3D 模型,有幾個 obj 格式的,也有幾個 stl 格式的,為我以後的學習做準備。當我需要檢視這些模型的時候,我首選是使用 Blender。在我的程式中使用的時候,我首選 Assimp 庫。在我之前的隨筆中,已經對 Assimp 庫做了介紹。見 為什麼說使用 Linux 系統學習 OpenGL 更方便

使用 Assimp 載入 3D 模型

Assimp 的使用是非常簡單的,直接參考 Assimp 的文件,依葫蘆畫瓢即可。這裡我只簡單介紹一下 Assimp 中的資料組織結構。Assimp 讀入模型後,先有一個 aiScene 物件,在這個物件中,有一個根節點,根節點又有子結點,子結點還可能有子結點,形成一個樹狀的結構。節點的型別是 aiNode。每一個節點包含一個或多個 aiMesh,而 aiMesh 又包含頂點資料和索引資料,索引資料是儲存在 aiFace 型別中的,一般來說,我們的每一個 aiFace 都應該是一個三角形。每一個 aiMesh 又有對應的材質資訊,因為我這裡還沒有涉及到光照和貼圖,所以暫不考慮材質資訊。只需要遞迴地進行解析,我們就可以很容易獲得模型中的所有頂點資訊和索引資訊。

在這裡,我建立了一個 Model 類,同時利用了之前的 Mesh 類,一個 Model 類的物件中可以包含多個 Mesh 類的物件。然後,在 Model 類的 loadModel() 方法中,使用 Assimp 讀取模型資料,遍歷各 aiNode, 再遍歷 aiMesh,將 aiMesh 中的資料拷貝到 Mesh 中,然後呼叫所有 Mesh 物件的 setup() 方法和 render() 方法,即可完成模型的渲染。Model 類的內容如下:

#ifndef __MODEL_HPP__
#define __MODEL_HPP__

#include "mesh.hpp"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>

class Model
{
private:
    std::vector<Mesh> meshes;

public:
    void loadModel(std::string filename)
    {
        Assimp::Importer importer;
        const aiScene *scene = importer.ReadFile(filename,
                                                 aiProcess_Triangulate |
                                                     aiProcess_GenNormals |
                                                     aiProcess_FlipUVs);
        processNode(scene->mRootNode, scene);
    }

    void processNode(aiNode *node, const aiScene *scene)
    {
        for (int i = 0; i < node->mNumMeshes; i++)
        {
            aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
            Vertex tempVertex;
            std::vector<Vertex> tempVertices;
            std::vector<GLuint> tempIndices;
            //先讀取頂點資訊
            for (int j = 0; j < mesh->mNumVertices; j++)
            {
                if (mesh->HasPositions())
                {
                    tempVertex.position.x = mesh->mVertices[j].x;
                    tempVertex.position.y = mesh->mVertices[j].y;
                    tempVertex.position.z = mesh->mVertices[j].z;
                    tempVertex.position.w = 1.0f;
                }
                if (mesh->HasNormals())
                {
                    tempVertex.normal.x = mesh->mNormals[j].x;
                    tempVertex.normal.y = mesh->mNormals[j].y;
                    tempVertex.normal.z = mesh->mNormals[j].z;
                }
                if (mesh->HasTextureCoords(0))
                {
                    tempVertex.texCoord.x = mesh->mTextureCoords[0][j].x;
                    tempVertex.texCoord.y = mesh->mTextureCoords[0][j].y;
                }

                tempVertices.push_back(tempVertex);
            }
            //再讀取索引資訊
            for (int i = 0; i < mesh->mNumFaces; i++)
            {
                for (int j = 0; j < mesh->mFaces[i].mNumIndices; j++)
                {
                    tempIndices.push_back(mesh->mFaces[i].mIndices[j]);
                }
            }
            Mesh tempMesh;

            tempMesh.setVertices(std::move(tempVertices));
            tempMesh.setIndices(std::move(tempIndices));
            tempMesh.setup();
            this->meshes.push_back(std::move(tempMesh));
        }

        if (node->mNumChildren != 0)
        {
            for (int k = 0; k < node->mNumChildren; k++)
            {
                processNode(node->mChildren[k], scene);
            }
        }
        return;
    }

    void render()
    {
        for (auto i = meshes.begin(); i != meshes.end(); i++)
        {
            i->render();
        }
    }
};

#endif

然後來測試一下我之前收集的那些模型檔案,其主檔案 LoadModel.cpp 內容如下:

#include "../include/app.hpp"
#include "../include/shader.hpp"
#include "../include/model.hpp"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

class MyApp : public App {
    private:
        const GLfloat clearColor[4] = {0.2f, 0.3f, 0.3f, 1.0f};
        Model allis;
        Model buma;
        Model saber;
        Model cottage;
        Model bugatti;
        Shader* simpleShader;

    public:
        void init(){
            
            ShaderInfo shaders[] = {
                {GL_VERTEX_SHADER, "simpleShader.vert"},
                {GL_FRAGMENT_SHADER, "simpleShader.frag"},
                {GL_NONE, ""}
            };
            simpleShader = new Shader(shaders);
            allis.loadModel( "allis.stl");
            buma.loadModel("buma.stl");
            saber.loadModel( "saber.stl");
            bugatti.loadModel("bugatti/bugatti.obj");
          
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_LEQUAL);

            glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
        }

        void display(){
            glClearBufferfv(GL_COLOR, 0, clearColor);
            glClear(GL_DEPTH_BUFFER_BIT);

            glm::mat4 I(1.0f);
            glm::vec3 X(1.0f, 0.0f, 0.0f);
            glm::vec3 Y(0.0f, 1.0f, 0.0f);
            glm::vec3 Z(0.0f, 0.0f, 1.0f);
            float t = (float)glfwGetTime();

            glm::mat4 view_matrix = glm::translate(I, glm::vec3(0.0f, 0.0f, -10.0f))
                                        * glm::rotate(I, t, Y);

            glm::mat4 projection_matrix = glm::perspective(glm::radians(45.0f), aspect, 1.0f, 100.0f);

            glm::mat4 allis_model_matrix = glm::translate(I, glm::vec3(-3.0f, -1.5f, 0.0f)) 
                                            * glm::scale(I, glm::vec3(1.0f, 1.0f, 1.0f)) * glm::rotate(I, glm::radians(-90.0f), X);
            
            simpleShader->setModelMatrix(allis_model_matrix);
            simpleShader->setViewMatrix(view_matrix);
            simpleShader->setProjectionMatrix(projection_matrix);
            simpleShader->setCurrent();
            allis.render();

            glm::mat4 buma_model_matrix = glm::translate(I, glm::vec3(-1.0f, -1.5f, 0.0f)) 
                                             * glm::scale(I, glm::vec3(0.03f, 0.03f, 0.03f)) * glm::rotate(I, glm::radians(0.0f), X);
            simpleShader->setModelMatrix(buma_model_matrix);
            buma.render();

            glm::mat4 saber_model_matrix = glm::translate(I, glm::vec3(7.5f, 0.6f, 1.0f)) 
                                            * glm::scale(I, glm::vec3(0.03f, 0.03f, 0.03f)) * glm::rotate(I, glm::radians(-90.0f), X);
            simpleShader->setModelMatrix(saber_model_matrix);
            saber.render();

            glm::mat4 bugatti_model_matrix = glm::translate(I, glm::vec3(5.0f, -1.0f, 0.0f)) 
                                                 * glm::scale(I, glm::vec3(0.2f, 0.2f, 0.2f)) * glm::rotate(I, glm::radians(0.0f), X);
            simpleShader->setModelMatrix(bugatti_model_matrix);
            bugatti.render();

        }

        ~MyApp(){
            if(simpleShader != NULL){
                delete simpleShader;
            }
        }

};


DECLARE_MAIN(MyApp)

效果如下:

我這裡只展示了三個美女和一個汽車,從左側的檔案列表中可以看出,我還收集了不少其它的模型,以後再逐漸與大家見面。

目前看起來還只是白茫茫一片,那是因為模型太精細了,所以看不清楚網格。下一步,我將在我的程式框架中新增改變視窗大小、在場景中漫遊、線框模型和麵模型之間切換等功能。今天就到這裡吧。

版權申明

該隨筆由京山遊俠在2021年08月07日釋出於部落格園,引用請註明出處,轉載或出版請聯絡博主。QQ郵箱:[email protected]