加載模型-02.網(wǎng)格(Mesh)

使用Assimp可以把多種不同格式的模型加載到程序中谷扣,但是一旦載入,它們就都被儲(chǔ)存為Assimp自己的數(shù)據(jù)結(jié)構(gòu)值桩。我們最終的目的是把這些數(shù)據(jù)轉(zhuǎn)變?yōu)镺penGL可讀的數(shù)據(jù)丽旅,才能用OpenGL來渲染物體。我們從前面的教程了解到舟误,一個(gè)網(wǎng)格(Mesh)代表一個(gè)可繪制實(shí)體葡秒,現(xiàn)在我們就定義一個(gè)自己的網(wǎng)格類。

先來復(fù)習(xí)一點(diǎn)目前學(xué)到知識(shí),考慮一個(gè)網(wǎng)格最少需要哪些數(shù)據(jù)眯牧。一個(gè)網(wǎng)格應(yīng)該至少需要一組頂點(diǎn)蹋岩,每個(gè)頂點(diǎn)包含一個(gè)位置向量,一個(gè)法線向量学少,一個(gè)紋理坐標(biāo)向量剪个。一個(gè)網(wǎng)格也應(yīng)該包含一個(gè)索引繪制用的索引,以紋理(diffuse/specular map)形式表現(xiàn)的材質(zhì)數(shù)據(jù)版确。

為了在OpenGL中定義一個(gè)頂點(diǎn)扣囊,現(xiàn)在我們?cè)O(shè)置有最少需求的一個(gè)網(wǎng)格類:

struct Vertex
{
    glm::vec3 Position;
    glm::vec3 Normal;
    glm::vec2 TexCoords;
};

我們把每個(gè)需要的向量?jī)?chǔ)存到一個(gè)叫做Vertex的結(jié)構(gòu)體中,它被用來索引每個(gè)頂點(diǎn)屬性绒疗。另外除了Vertex結(jié)構(gòu)體外侵歇,我們也希望組織紋理數(shù)據(jù),所以我們定義一個(gè)Texture結(jié)構(gòu)體:

struct Texture
{
    GLuint id;
    string type;
};

我們儲(chǔ)存紋理的id和它的類型吓蘑,比如diffuse紋理或者specular紋理惕虑。

知道了頂點(diǎn)和紋理的實(shí)際表達(dá),我們可以開始定義網(wǎng)格類的結(jié)構(gòu):

class Mesh {
public:
    /*  Mesh Data  */
    vector<Vertex> vertices;
    vector<GLuint> indices;
    vector<Texture> textures;
    /*  Functions  */
    Mesh (vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures);
    void Draw (Shader shader);
private:
    /*  Render data  */
    GLuint VAO, VBO, EBO;
    /*  Functions    */
    void setupMesh ();
};

如你所見這個(gè)類一點(diǎn)都不復(fù)雜磨镶,構(gòu)造方法里我們初始化網(wǎng)格所有必須數(shù)據(jù)溃蔫。在setupMesh函數(shù)里初始化緩沖。最后通過Draw函數(shù)繪制網(wǎng)格琳猫。注意伟叛,我們把shader傳遞給Draw函數(shù)。通過把shader傳遞給Mesh脐嫂,在繪制之前我們?cè)O(shè)置幾個(gè)uniform(比如鏈接采樣器到紋理單元)统刮。

構(gòu)造函數(shù)的內(nèi)容非常直接。我們簡(jiǎn)單設(shè)置類的公有變量账千,使用的是構(gòu)造函數(shù)相應(yīng)的參數(shù)网沾。我們?cè)跇?gòu)造函數(shù)中也調(diào)用setupMesh函數(shù):

Mesh (vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures)
{
    this->vertices = vertices;
    this->indices = indices;
    this->textures = textures;

    // Now that we have all the required data, set the vertex buffers and its attribute pointers.
    this->setupMesh ();
}

這里沒什么特別的,現(xiàn)在讓我們研究一下setupMesh函數(shù)蕊爵。

初始化

現(xiàn)在我們有一大列的網(wǎng)格數(shù)據(jù)可用于渲染辉哥,這要感謝構(gòu)造函數(shù)。我們確實(shí)需要設(shè)置合適的緩沖攒射,通過頂點(diǎn)屬性指針(vertex attribute pointers)定義頂點(diǎn)著色器layout〈椎現(xiàn)在你應(yīng)該對(duì)這些概念很熟悉,但是我們介紹了結(jié)構(gòu)體中頂點(diǎn)數(shù)據(jù)会放,所以稍微有點(diǎn)不一樣:

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

    glBindVertexArray (this->VAO);
    glBindBuffer (GL_ARRAY_BUFFER, this->VBO);

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

    glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, this->EBO);
    glBufferData (GL_ELEMENT_ARRAY_BUFFER, this->indices.size () * sizeof (GLuint),
        &this->indices[0], GL_STATIC_DRAW);

    // Vertex Positions
    glEnableVertexAttribArray (0);
    glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex),
        (GLvoid*) 0);
    // Vertex Normals
    glEnableVertexAttribArray (1);
    glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex),
        (GLvoid*) offsetof (Vertex, Normal));
    // Vertex Texture Coords
    glEnableVertexAttribArray (2);
    glVertexAttribPointer (2, 2, GL_FLOAT, GL_FALSE, sizeof (Vertex),
        (GLvoid*) offsetof (Vertex, TexCoords));

    glBindVertexArray (0);
}

C++的結(jié)構(gòu)體有一個(gè)重要的屬性饲齐,那就是在內(nèi)存中它們是連續(xù)的。如果我們用結(jié)構(gòu)體表示一列數(shù)據(jù)咧最,這個(gè)結(jié)構(gòu)體只包含結(jié)構(gòu)體的連續(xù)的變量捂人,它就會(huì)直接轉(zhuǎn)變?yōu)橐粋€(gè)float(實(shí)際上是byte)數(shù)組御雕,我們就能用于一個(gè)數(shù)組緩沖(array buffer)中了。比如滥搭,如果我們填充一個(gè)Vertex結(jié)構(gòu)體酸纲,它在內(nèi)存中的排布等于:

Vertex vertex;
vertex.Position = glm::vec3 (0.2f, 0.4f, 0.6f);
vertex.Normal = glm::vec3 (0.0f, 1.0f, 0.0f);
vertex.TexCoords = glm::vec2 (1.0f, 0.0f);
// = [0.2f, 0.4f, 0.6f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f];

感謝這個(gè)有用的特性,我們能直接把一個(gè)作為緩沖數(shù)據(jù)的一大列Vertex結(jié)構(gòu)體的指針傳遞過去瑟匆,它們會(huì)翻譯成glBufferData能用的參數(shù):

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

自然地闽坡,sizeof函數(shù)也可以使用于結(jié)構(gòu)體來計(jì)算字節(jié)類型的大小。它應(yīng)該是32字節(jié)(8float * 4)愁溜。

一個(gè)預(yù)處理指令叫做offsetof(s,m)把結(jié)構(gòu)體作為它的第一個(gè)參數(shù)疾嗅,第二個(gè)參數(shù)是這個(gè)結(jié)構(gòu)體內(nèi)的變量。這是結(jié)構(gòu)體另外的一個(gè)重要用途冕象。函數(shù)返回這個(gè)變量從結(jié)構(gòu)體開始的字節(jié)偏移量(offset)代承。這對(duì)于定義glVertexAttribPointer函數(shù)偏移量參數(shù)效果很好:

glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex),
    (GLvoid*) offsetof (Vertex, Normal));

偏移量現(xiàn)在使用offsetof函數(shù)定義了,在這個(gè)例子里渐扮,設(shè)置法線向量的字節(jié)偏移量等于法線向量在結(jié)構(gòu)體的字節(jié)偏移量次泽,它是3float
,也就是12字節(jié)(一個(gè)float占4字節(jié))席爽。注意,我們同樣設(shè)置步長(zhǎng)參數(shù)等于Vertex結(jié)構(gòu)體的大小啊片。

使用一個(gè)像這樣的結(jié)構(gòu)體只锻,不僅能提供可讀性更高的代碼同時(shí)也是我們可以輕松的擴(kuò)展結(jié)構(gòu)體。如果我們想要增加另一個(gè)頂點(diǎn)屬性紫谷,我們把它可以簡(jiǎn)單的添加到結(jié)構(gòu)體中齐饮,由于它的可擴(kuò)展性,渲染代碼不會(huì)被破壞笤昨。

渲染

我們需要為Mesh類定義的最后一個(gè)函數(shù)祖驱,是它的Draw函數(shù)。在真正渲染前我們希望綁定合適的紋理瞒窒,然后調(diào)用glDrawElements捺僻。可因?yàn)槲覀儚囊婚_始不知道這個(gè)網(wǎng)格有多少紋理以及它們應(yīng)該是什么類型的崇裁,所以這件事變得很困難匕坯。所以我們?cè)撛鯓釉谥髦性O(shè)置紋理單元和采樣器呢?

解決這個(gè)問題拔稳,我們需要假設(shè)一個(gè)特定的名稱慣例:每個(gè)diffuse紋理被命名為texture_diffuseN,每個(gè)specular紋理應(yīng)該被命名為texture_specularN葛峻。N是一個(gè)從1到紋理允許使用的最大值之間的數(shù)“捅龋可以說术奖,在一個(gè)網(wǎng)格中我們有3個(gè)diffuse紋理和2個(gè)specular紋理礁遵,它們的紋理采樣器應(yīng)該這樣被調(diào)用:

uniform sampler2D texture_diffuse1;
uniform sampler2D texture_diffuse2;
uniform sampler2D texture_diffuse3;
uniform sampler2D texture_specular1;
uniform sampler2D texture_specular2;

使用這樣的慣例,我們能定義我們?cè)谥髦行枰募y理采樣器的數(shù)量采记。如果一個(gè)網(wǎng)格真的有(這么多)紋理佣耐,我們就知道它們的名字應(yīng)該是什么。這個(gè)慣例也使我們能夠處理一個(gè)網(wǎng)格上的任何數(shù)量的紋理挺庞,通過定義合適的采樣器開發(fā)者可以自由使用希望使用的數(shù)量(雖然定義少的話就會(huì)有點(diǎn)浪費(fèi)綁定和uniform調(diào)用了)晰赞。

像這樣的問題有很多不同的解決方案,如果你不喜歡這個(gè)方案选侨,你可以自己創(chuàng)造一個(gè)你自己的方案掖鱼。

最后的繪制代碼:

void Draw (Shader shader)
{
    GLuint diffuseNr = 1;
    GLuint specularNr = 1;
    for (GLuint i = 0; i < this->textures.size (); i++)
    {
        glActiveTexture (GL_TEXTURE0 + i); // Activate proper texture unit before binding
                            // Retrieve texture number (the N in diffuse_textureN)
        stringstream ss;
        string number;
        string name = this->textures[i].type;
        if (name == "texture_diffuse")
            ss << diffuseNr++; // Transfer GLuint to stream
        else if (name == "texture_specular")
            ss << specularNr++; // Transfer GLuint to stream
        number = ss.str ();

        glUniform1f (glGetUniformLocation (shader.Program, ("material." + name + number).c_str ()), i);
        glBindTexture (GL_TEXTURE_2D, this->textures[i].id);
    }
    glActiveTexture (GL_TEXTURE0);

    // Draw mesh
    glBindVertexArray (this->VAO);
    glDrawElements (GL_TRIANGLES, this->indices.size (), GL_UNSIGNED_INT, 0);
    glBindVertexArray (0);
}

這不是最漂亮的代碼,但是這主要?dú)w咎于C++轉(zhuǎn)換類型時(shí)的丑陋援制,比如int轉(zhuǎn)string時(shí)戏挡。我們首先計(jì)算N-元素每個(gè)紋理類型,把它鏈接到紋理類型字符串來獲取合適的uniform名晨仑。然后查找合適的采樣器位置褐墅,給它位置值對(duì)應(yīng)當(dāng)前激活紋理單元,綁定紋理洪己。這也是我們需要在Draw方法是用shader的原因妥凳。我們添加material.到作為結(jié)果的uniform名,因?yàn)槲覀兺ǔ0鸭y理儲(chǔ)存進(jìn)材質(zhì)結(jié)構(gòu)體(對(duì)于每個(gè)實(shí)現(xiàn)也許會(huì)有不同)

注意答捕,當(dāng)我們把diffuse和specular傳遞到字符串流(stringstream
)的時(shí)候逝钥,計(jì)數(shù)器會(huì)增加,在C++自增叫做:變量++拱镐,它會(huì)先返回自身然后加1艘款,而++變量,先加1再返回自身沃琅,我們的例子里哗咆,我們先傳遞原來的計(jì)數(shù)器值到字符串流,然后再加1益眉,下一輪生效晌柬。

你可以從這里得到Mesh類的源碼

Mesh類是對(duì)我們前面的教程里討論的很多話題的的簡(jiǎn)潔的抽象郭脂。在下面的教程里空繁,我們會(huì)創(chuàng)建一個(gè)模型,它用作乘放多個(gè)網(wǎng)格物體的容器朱庆,真正的實(shí)現(xiàn)Assimp的加載接口盛泡。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市娱颊,隨后出現(xiàn)的幾起案子傲诵,更是在濱河造成了極大的恐慌凯砍,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拴竹,死亡現(xiàn)場(chǎng)離奇詭異悟衩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)栓拜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門座泳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人幕与,你說我怎么就攤上這事挑势。” “怎么了啦鸣?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵潮饱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我诫给,道長(zhǎng)香拉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任中狂,我火速辦了婚禮凫碌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘胃榕。我一直安慰自己盛险,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布勤晚。 她就那樣靜靜地躺著,像睡著了一般泉褐。 火紅的嫁衣襯著肌膚如雪赐写。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天膜赃,我揣著相機(jī)與錄音挺邀,去河邊找鬼。 笑死跳座,一個(gè)胖子當(dāng)著我的面吹牛端铛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疲眷,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼禾蚕,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了狂丝?” 一聲冷哼從身側(cè)響起换淆,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤哗总,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后倍试,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體讯屈,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年县习,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涮母。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡躁愿,死狀恐怖叛本,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情攘已,我是刑警寧澤炮赦,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站样勃,受9級(jí)特大地震影響吠勘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜峡眶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一剧防、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辫樱,春花似錦峭拘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至搬男,卻和暖如春拣展,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缔逛。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工备埃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人褐奴。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓按脚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親敦冬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子辅搬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 現(xiàn)在是時(shí)候著手啟用Assimp,并開始創(chuàng)建實(shí)際的加載和轉(zhuǎn)換代碼了脖旱。本教程的目標(biāo)是創(chuàng)建另一個(gè)類伞辛,這個(gè)類可以表達(dá)模型的...
    IceMJ閱讀 2,781評(píng)論 0 3
  • <轉(zhuǎn)>我也忘了轉(zhuǎn)自哪里,抱歉,感謝原作者 什么是Shader Shader(著色器)是一段能夠針對(duì)3D對(duì)象進(jìn)行操作...
    星易乾川閱讀 5,590評(píng)論 1 16
  • 更新:【面試題含答案】http://bbs.9ria.com/thread-288394-1-1.html 高頻問...
    好怕怕閱讀 4,747評(píng)論 3 52
  • 優(yōu)酷騰訊愛奇藝等左右網(wǎng)站的獨(dú)播網(wǎng)劇 VIP用券才能看的電影 在更新或者已經(jīng)完結(jié)的美劇日劇韓劇tvb 各種院線電影...
    一別兩寬丶閱讀 136評(píng)論 0 0