扒几个 3D 模型备用
本文轉自:扒幾個 3D 模型備用 - 京山游俠 - 博客園 (cnblogs.com)
前言
在上一篇中,我展示了 OpenGL 開發的基本過程,算是向 3D 世界邁出的一小步吧。對于簡單的 3D 物體,比如立方體、球體、圓環等等,我們只需要簡單的計算就可以得到他們的頂點的坐標。但是僅僅這樣,還不是太過癮,我們需要找一些復雜一點的 3D 模型,以便于我們體會 3D 世界的魅力。
在我學習 OpenGL 的過程中,我收集了不少的 3D 模型,主要是從?Free3D?下載的,都是 Obj 格式的文件,有的帶紋理貼圖,有的不帶紋理貼圖。比如,有一個小木屋的模型,帶紋理貼圖和法線貼圖,是我學習貼圖和光照的好素材。還有一個地球的模型,還有幾輛汽車的模型。還有我從著名的 OpenGL 網絡教程?LearnOpenGL?中下載得有一套 nanosuit 的模型。對于這些有著規范格式的 3D 模型,我覺得使用 Assimp 庫加載是比較好的選擇,至于 Assimp 庫,以后再介紹。
另外,茶壺也是一個經典的模型,不過是以貝塞爾曲面的方式定義的。貝塞爾曲面其實不難,使用 16 個控制點可以描述一個曲面,并且可以根據我們需要的光滑程度選擇不同的細分級別,關于貝塞爾曲面的內容留待以后再專講,而且我覺得和曲面細分著色器一起學習效果更佳。那么這個茶壺模型的數據在哪里可以找到呢?FreeGlut 中有,可以在 github 中找到。除此之外,紅寶書的源代碼中也有一個茶壺的數據。這里不贅述。
我這里要扒的幾個模型來自紅寶書的源代碼,它們分別是 armadillo.vbm、 bunny.vbm 和 ninja.vbm。這里,作者使用了他自創的 vbm 模型格式。作者還寫了從 obj 格式到 vbm 格式轉換的工具以及從 Maya 導出 vbm 格式的工具。但畢竟 vbm 格式不是標準的通用格式,我并不是很喜歡。但是為了把這三個模型顯示出來看看,我還是認真研究了作者的源代碼。
VBM 模型文件的具體細節
我是通過閱讀紅寶書源代碼中的 vbm.h 和 vbm.cpp 文件來了解 vbm 模型文件的細節的。這是一個二進制的模型文件,一開始是個 VBM_HEADER 結構,在作者的設計中,該文件分為新版和舊版,舊版的頭部結構為 VBM_HEADER_OLD,但是從我扒出的數據來看,根本就不需要考慮舊版。
在 VBM_HEADER 之后,是若干個 VBM_ATTRIB_HEADER 結構,該結構用來說明每個頂點包含哪些屬性,每個屬性又包含哪些分量。從我扒出的數據來看,以上三個模型,都是包含三個屬性的,分別是頂點坐標,包含 4 個 GLfloat 分量,頂點法向量,包含 3 個 GLfloat 分量,紋理貼圖坐標,包含兩個 GLfloat 分量。這和我上一篇中對頂點格式的設計簡直一模一樣。
在 VBM_ATTRIB_HEADER 之后,是若干個 VBM_FRAME_HEADER,看來該作者設計該格式是可以支持動畫的。不過以我扒出的數據來看,以上三個模型文件都只包含一幀。
在 VBM_FRAME_HEADER 之后就是頂點數據。從頭文件中可以得到頂點的個數,以及每個頂點包含哪些屬性,以及每個屬性包含幾個分量,就很容易算出頂點數據的長度。
頂點數據之后,就是索引數據。我讀源代碼,同時還發現頂點數據之后是材質信息。這兩組數據是有點混淆的。好在,以我扒出的數據來看,以上三個模型文件既沒有使用索引,也沒有包含任何材質,那倒是讓我省事了不少。
?
編寫我自己的 VbmObject 類
參考我之前寫的 Mesh 類,就很容易寫一個能在我的 App 框架中非常容易使用的 VbmObject 類。在 VbmObject 類中,寫一個 loadFromVBM() 方法,以從文件中加載頂點數據,同時獲取頂點個數的信息。然后寫一個 setup() 方法,用來創建相應的 VAO 和 VBO,并向緩存中存入數據,并啟用頂點屬性。這里需要特別注意的是,該模型文件中的數據,是每一個屬性集中存放的,所以調用 glVertexAttribPointer() 方法時要特別注意。最后,寫一個 render() 方法進行渲染,render() 方法很簡單,就是調用 glDrawArrays(),當然,調用該方法之前需要綁定 VAO。
vbm.hpp 的完整代碼如下:
#ifndef __VBM_H__ #define __VBM_H__#include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <vector> #include <string> #include <string.h> #include <GL/glew.h> #include <iostream>typedef struct VBM_HEADER_t {unsigned int magic;unsigned int size;char name[64];unsigned int num_attribs;unsigned int num_frames;unsigned int num_vertices;unsigned int num_indices;unsigned int index_type;unsigned int num_materials;unsigned int flags; } VBM_HEADER;typedef struct VBM_ATTRIB_HEADER_t {char name[64];unsigned int type;unsigned int components;unsigned int flags; } VBM_ATTRIB_HEADER;typedef struct VBM_FRAME_HEADER_t {unsigned int first;unsigned int count;unsigned int flags; } VBM_FRAME_HEADER;class VbmObject{protected:unsigned char* file_data;unsigned char* vertex_data;unsigned int vertex_num;GLuint VAO, VBO;public:bool loadFromVBM(const char * filename){std::cout << "File name: " << filename << std::endl;FILE * f = NULL;f = fopen(filename, "rb");if(f == NULL)return false;fseek(f, 0, SEEK_END);size_t filesize = ftell(f);fseek(f, 0, SEEK_SET);file_data = new unsigned char [filesize];fread(file_data, filesize, 1, f);fclose(f);VBM_HEADER * header = (VBM_HEADER *)file_data;vertex_data = file_data + header->size + header->num_attribs * sizeof(VBM_ATTRIB_HEADER) + header->num_frames * sizeof(VBM_FRAME_HEADER);vertex_num = header->num_vertices;std::cout << "Num of Vertices: " << vertex_num << std::endl;return true;}void setup(){glCreateVertexArrays(1, &VAO);glBindVertexArray(VAO);glCreateBuffers(1, &VBO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glNamedBufferStorage(VBO, 9*sizeof(GLfloat)*vertex_num, vertex_data, 0);glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, (void*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat)*vertex_num*4));glEnableVertexAttribArray(1);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat)*vertex_num*3));glEnableVertexAttribArray(2);}void render(){glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, vertex_num);}~VbmObject(){if(file_data != NULL){delete file_data;}} };#endif主程序文件是 DumpVbm.cpp,其框架結構還是和前面的差不多,先是繼承 App 類,在 init() 方法中初始化數據,比如調用 VbmObject 對象的 loadFromVBM() 方法,調用 setup() 方法,同時創建 shader。然后在 display() 中準備模型、視圖、投影矩陣,向 shader 中傳遞這些矩陣數據,然后調用 VbmObject 對象的 render() 方法。
DumpVbm.cpp 的完整內容如下:
#include "../include/app.hpp" #include "../include/shader.hpp" #include "../include/vbm.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};VbmObject armadillo;VbmObject bunny;VbmObject ninja;Shader* shaderDumpVbm;public:void init(){ShaderInfo shaders[] = {{GL_VERTEX_SHADER, "dumpvbm.vert"},{GL_FRAGMENT_SHADER, "dumpvbm.frag"},{GL_NONE, ""}};shaderDumpVbm = new Shader(shaders);armadillo.loadFromVBM("armadillo.vbm");armadillo.setup();bunny.loadFromVBM("bunny.vbm");bunny.setup();ninja.loadFromVBM("ninja.vbm");ninja.setup();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, -5.0f))* glm::rotate(I, t, Y);glm::mat4 projection_matrix = glm::perspective(glm::radians(45.0f), aspect, 1.0f, 100.0f);glm::mat4 armadillo_model_matrix = glm::translate(I, glm::vec3(-2.0f, 0.0f, 0.0f)) * glm::scale(I, glm::vec3(0.015f, 0.015f, 0.015f)) * glm::rotate(I, glm::radians(180.0f), Y);shaderDumpVbm->setModelMatrix(armadillo_model_matrix);shaderDumpVbm->setViewMatrix(view_matrix);shaderDumpVbm->setProjectionMatrix(projection_matrix);shaderDumpVbm->setCurrent();armadillo.render();glm::mat4 bunny_model_matrix = glm::scale(I, glm::vec3(10.0f, 10.0f, 10.0f));shaderDumpVbm->setModelMatrix(bunny_model_matrix);bunny.render();glm::mat4 ninja_model_matrix = glm::translate(I, glm::vec3(2.0f, -1.0f, 0.0f)) * glm::scale(I, glm::vec3(0.015f, 0.015f, 0.015f));shaderDumpVbm->setModelMatrix(ninja_model_matrix);ninja.render();}~MyApp(){if(shaderDumpVbm != NULL){delete shaderDumpVbm;}}};DECLARE_MAIN(MyApp)shader 文件和之前沒有區別。編譯運行,命令如下:
g++ DumpVbm.cpp -o DumpVbm -lGL -lglfw -lGLEW ./DumpVbm?就可以看到效果了。如下:
?
總結
以上是生活随笔為你收集整理的扒几个 3D 模型备用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OpenGL 网络教程 LearnOpe
- 下一篇: 使现有的VSCode成为便携版(绿色版)