Assimp里的一些知識(1)


OpenGL 學習到模型加載的時候,介紹了一個模型導入庫(Open Asset Import Library,Assimp)的用法。初學的時候覺得稍微有些復雜,故借由這篇blog來簡單地捋一下其中的細節。


 

首先,當我們使用Assimp導入模型的時候,它通常會將整個模型加載到一個場景(Scene)對象,這個對象包含了導入模型的所有數據。具體結構如下圖所示(這個圖結構十分重要,需要充分理解):

 

 

  1.  Scene 場景。Scene場景有3個成員,分別是mRootNode(場景根節點),mMeshes(場景中所有網格Mesh的集合),mMaterials(網格的材質屬性)。
  2. Node節點。場景的根節點(Root Node, mRootNode指向)包含了子節點,同時每個節點中有一系列指向場景對象中具體mMeshes成員的索引(Scene下的mMeshes數組儲存了真正的Mesh對象,節點中的mMeshes數組保存的只是場景中網格數組的索引)。此外,這個子結構讓我們可以使用遞歸的方式來處理節點(稍后講到)。
  3. Mesh網格,一個Mesh 網格是可以看做渲染的一個單位,它包含了頂點數據,法線,紋理坐標(只是坐標數據,不是紋理對象),面等數組,以及材質索引(指向Scene中的mMaterials
  4. Face面,面數組由面組成(廢話),概念上,一個面表示的是物體的渲染圖元(primitive)(三角形,方形,點)。在這里,我們則讓它包含了組成圖元的頂點索引(也就是指向Mesh里面的頂點數據啦),從而我們可以使用EBO來繪制圖形。
  5. Material材質,一個Mesh網格還包括了一個材質索引(非數組),索引指向Scene中的具體材質。

這里基本上就把Assimp的結構講解完畢了,在這里補充一下關於材質,貼圖,紋理等的相關知識(防止混淆):貼圖,紋理,材質的區別是什么?

順便補充一下什么叫Mesh,以及它的圖元如何組成Mesh:What is a mesh in OpenGL?


好了,Assimp導入模型到場景之后的數據結構我們已經清楚了,現在我們需要一個類來專門處理這些導入的數據。其實也很簡單,數據處理過程與之前繪制箱子的過程基本一致,只是由於未知導入模型的數據量,我們采用了vector模板類來存儲相關數據

首先,一個網格需要的最少數據量就是頂點數據,法線,紋理坐標。用於面繪制的索引。以及紋理形式的材質數據,,因此我們在這里可以定義一些結構體(沒定義到的后面Mesh類也會出現的啦):

struct Vertex {
    glm::vec3 Position;
    glm::vec3 Normal;
    glm::vec2 TexCoords;
};
struct Texture {
    unsigned int id;
    string type;  //such as diffuseMap or specularMap
};

定義好結構體之后,就是Mesh類的具體定義了:

class Mesh
{
public:
    vector<Vertex> vertices;   //頂點數據
    vector<unsigned int> indices    //頂點繪制索引,也就是Face里面的indices
    vector<Texture> texture;    //材質(紋理)數據。嚴格的說是物體的材質貼圖。詳見上面關於材質紋理貼圖的知乎鏈接啦。

    Mesh(vector<Vertex> vertices, vector<unsigned int> indices, vector<Texture> texture);
    
    void Draw(Shader shader);   //繪制網格的函數

private:
    unsigned int VAO, VBO, EBO;
    void setupMesh();    //處理網格數據的函數
}

不廢話了,直接給setupMesh的源碼(其實是懶)

void setupMesh()
{
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);  

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), 
                 &indices[0], GL_STATIC_DRAW);

    // 頂點位置
    glEnableVertexAttribArray(0);   
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
    // 頂點法線
    glEnableVertexAttribArray(1);   
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
    // 頂點紋理坐標
    glEnableVertexAttribArray(2);   
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));

    glBindVertexArray(0);
}  

這里有一些需要注意的東西。

第一個是傳入數據至VBO以及EBO的時候,由於我們使用的是vector模板類,不可以直接用sizeof()計算大小(為什么?),所以通過用.size(),以及結構體的大小來計算數據量。還有我們用的&vertices[0],而不是像之前那樣的vertices,是因為之前用數組實現,數組名即為指針,用vector實現,需要手動添加首元素的地址。

實際上因為結構體的存儲方式是連續的(sequential),我們得以直接傳入結構體的指針(&vertices[0])作為緩沖的數據(的起始點)

其二是OffsetOf函數,顧名思義用來計算偏移量。

 

然后就是Draw函數了:

void Draw(Shader shader) 
{
    unsigned int diffuseNr = 1;
    unsigned int specularNr = 1;
    for(unsigned int i = 0; i < textures.size(); i++)
    {
        glActiveTexture(GL_TEXTURE0 + i); // 在綁定之前激活相應的紋理單元
        // 獲取紋理序號(diffuse_textureN 中的 N)
        string number;
        string name = textures[i].type;
        if(name == "texture_diffuse")
            number = std::to_string(diffuseNr++);
        else if(name == "texture_specular")
            number = std::to_string(specularNr++);

        shader.setFloat(("material." + name + number).c_str(), i);
        glBindTexture(GL_TEXTURE_2D, textures[i].id);
    }
    glActiveTexture(GL_TEXTURE0);

    // 繪制網格
    glBindVertexArray(VAO);
    glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}

這里我們稍微回顧一下紋理的相關知識:如何把紋理傳入fragment shader,首先將在着色器里面紋理采樣器(sampler2D)的值設置為對應的紋理單元值(0-15),然后激活一個對應的紋理單元,接着我們將要綁定的紋理綁定(glBindTexture())起來。上面Draw函數干的就是這事兒,並且最后根據索引(EBO),當前激活的着色器等繪制圖元,

Model類下一節講。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM