# 第七課:模型加載
# 第七課:模型加載
目前為止,我們一直在硬編碼描述立方體。你一定覺得這樣做很笨拙、不方便。
本課將學習從文件中加載3D模型。和加載紋理類似,我們先寫一個小的、功能有限的加載器,接著再為大家介紹幾個比我們寫的更好的、實用的庫。
為了讓課程盡可能簡單,我們將采用簡單、常用的OBJ格式。同樣也是出于簡單原則,我們只處理每個頂點有一個UV坐標和一個法向量的OBJ文件(目前你不需要知道什么是法向量)。
## 加載OBJ模型
加載函數在common/objloader.hpp中聲明,在common/objloader.cpp中實現。函數原型如下:
```
<pre class="calibre16">```
bool <span class="token3">loadOBJ</span><span class="token1">(</span>
const char <span class="token">*</span> path<span class="token1">,</span>
std<span class="token1">:</span><span class="token1">:</span>vector <span class="token">&</span> out_vertices<span class="token1">,</span>
std<span class="token1">:</span><span class="token1">:</span>vector <span class="token">&</span> out_uvs<span class="token1">,</span>
std<span class="token1">:</span><span class="token1">:</span>vector <span class="token">&</span> out_normals
<span class="token1">)</span>
```
```
我們讓loadOBJ讀取文件路徑,把數據寫入out\_vertices/out\_uvs/out\_normals。如果出錯則返回false。std::vector是C++中的數組,可存放glm::vec3類型的數據,數組大小可任意修改,不過std::vector和數學中的向量(vector)是兩碼事。其實它只是個數組。最后提一點,符號&意思是這個函數將會直接修改這些數組。
### OBJ文件示例
OBJ文件看起來大概像這樣:
```
<pre class="calibre16">```
# Blender3D v249 OBJ File<span class="token1">:</span> untitled<span class="token1">.</span>blend
# www<span class="token1">.</span>blender3d<span class="token1">.</span>org
mtllib cube<span class="token1">.</span>mtl
v <span class="token6">1.000000</span> <span class="token">-</span><span class="token6">1.000000</span> <span class="token">-</span><span class="token6">1.000000</span>
v <span class="token6">1.000000</span> <span class="token">-</span><span class="token6">1.000000</span> <span class="token6">1.000000</span>
v <span class="token">-</span><span class="token6">1.000000</span> <span class="token">-</span><span class="token6">1.000000</span> <span class="token6">1.000000</span>
v <span class="token">-</span><span class="token6">1.000000</span> <span class="token">-</span><span class="token6">1.000000</span> <span class="token">-</span><span class="token6">1.000000</span>
v <span class="token6">1.000000</span> <span class="token6">1.000000</span> <span class="token">-</span><span class="token6">1.000000</span>
v <span class="token6">0.999999</span> <span class="token6">1.000000</span> <span class="token6">1.000001</span>
v <span class="token">-</span><span class="token6">1.000000</span> <span class="token6">1.000000</span> <span class="token6">1.000000</span>
v <span class="token">-</span><span class="token6">1.000000</span> <span class="token6">1.000000</span> <span class="token">-</span><span class="token6">1.000000</span>
vt <span class="token6">0.748573</span> <span class="token6">0.750412</span>
vt <span class="token6">0.749279</span> <span class="token6">0.501284</span>
vt <span class="token6">0.999110</span> <span class="token6">0.501077</span>
vt <span class="token6">0.999455</span> <span class="token6">0.750380</span>
vt <span class="token6">0.250471</span> <span class="token6">0.500702</span>
vt <span class="token6">0.249682</span> <span class="token6">0.749677</span>
vt <span class="token6">0.001085</span> <span class="token6">0.750380</span>
vt <span class="token6">0.001517</span> <span class="token6">0.499994</span>
vt <span class="token6">0.499422</span> <span class="token6">0.500239</span>
vt <span class="token6">0.500149</span> <span class="token6">0.750166</span>
vt <span class="token6">0.748355</span> <span class="token6">0.998230</span>
vt <span class="token6">0.500193</span> <span class="token6">0.998728</span>
vt <span class="token6">0.498993</span> <span class="token6">0.250415</span>
vt <span class="token6">0.748953</span> <span class="token6">0.250920</span>
vn <span class="token6">0.000000</span> <span class="token6">0.000000</span> <span class="token">-</span><span class="token6">1.000000</span>
vn <span class="token">-</span><span class="token6">1.000000</span> <span class="token">-</span><span class="token6">0.000000</span> <span class="token">-</span><span class="token6">0.000000</span>
vn <span class="token">-</span><span class="token6">0.000000</span> <span class="token">-</span><span class="token6">0.000000</span> <span class="token6">1.000000</span>
vn <span class="token">-</span><span class="token6">0.000001</span> <span class="token6">0.000000</span> <span class="token6">1.000000</span>
vn <span class="token6">1.000000</span> <span class="token">-</span><span class="token6">0.000000</span> <span class="token6">0.000000</span>
vn <span class="token6">1.000000</span> <span class="token6">0.000000</span> <span class="token6">0.000001</span>
vn <span class="token6">0.000000</span> <span class="token6">1.000000</span> <span class="token">-</span><span class="token6">0.000000</span>
vn <span class="token">-</span><span class="token6">0.000000</span> <span class="token">-</span><span class="token6">1.000000</span> <span class="token6">0.000000</span>
usemtl Material_ray<span class="token1">.</span>png
s off
f <span class="token6">5</span><span class="token">/</span><span class="token6">1</span><span class="token">/</span><span class="token6">1</span> <span class="token6">1</span><span class="token">/</span><span class="token6">2</span><span class="token">/</span><span class="token6">1</span> <span class="token6">4</span><span class="token">/</span><span class="token6">3</span><span class="token">/</span><span class="token6">1</span>
f <span class="token6">5</span><span class="token">/</span><span class="token6">1</span><span class="token">/</span><span class="token6">1</span> <span class="token6">4</span><span class="token">/</span><span class="token6">3</span><span class="token">/</span><span class="token6">1</span> <span class="token6">8</span><span class="token">/</span><span class="token6">4</span><span class="token">/</span><span class="token6">1</span>
f <span class="token6">3</span><span class="token">/</span><span class="token6">5</span><span class="token">/</span><span class="token6">2</span> <span class="token6">7</span><span class="token">/</span><span class="token6">6</span><span class="token">/</span><span class="token6">2</span> <span class="token6">8</span><span class="token">/</span><span class="token6">7</span><span class="token">/</span><span class="token6">2</span>
f <span class="token6">3</span><span class="token">/</span><span class="token6">5</span><span class="token">/</span><span class="token6">2</span> <span class="token6">8</span><span class="token">/</span><span class="token6">7</span><span class="token">/</span><span class="token6">2</span> <span class="token6">4</span><span class="token">/</span><span class="token6">8</span><span class="token">/</span><span class="token6">2</span>
f <span class="token6">2</span><span class="token">/</span><span class="token6">9</span><span class="token">/</span><span class="token6">3</span> <span class="token6">6</span><span class="token">/</span><span class="token6">10</span><span class="token">/</span><span class="token6">3</span> <span class="token6">3</span><span class="token">/</span><span class="token6">5</span><span class="token">/</span><span class="token6">3</span>
f <span class="token6">6</span><span class="token">/</span><span class="token6">10</span><span class="token">/</span><span class="token6">4</span> <span class="token6">7</span><span class="token">/</span><span class="token6">6</span><span class="token">/</span><span class="token6">4</span> <span class="token6">3</span><span class="token">/</span><span class="token6">5</span><span class="token">/</span><span class="token6">4</span>
f <span class="token6">1</span><span class="token">/</span><span class="token6">2</span><span class="token">/</span><span class="token6">5</span> <span class="token6">5</span><span class="token">/</span><span class="token6">1</span><span class="token">/</span><span class="token6">5</span> <span class="token6">2</span><span class="token">/</span><span class="token6">9</span><span class="token">/</span><span class="token6">5</span>
f <span class="token6">5</span><span class="token">/</span><span class="token6">1</span><span class="token">/</span><span class="token6">6</span> <span class="token6">6</span><span class="token">/</span><span class="token6">10</span><span class="token">/</span><span class="token6">6</span> <span class="token6">2</span><span class="token">/</span><span class="token6">9</span><span class="token">/</span><span class="token6">6</span>
f <span class="token6">5</span><span class="token">/</span><span class="token6">1</span><span class="token">/</span><span class="token6">7</span> <span class="token6">8</span><span class="token">/</span><span class="token6">11</span><span class="token">/</span><span class="token6">7</span> <span class="token6">6</span><span class="token">/</span><span class="token6">10</span><span class="token">/</span><span class="token6">7</span>
f <span class="token6">8</span><span class="token">/</span><span class="token6">11</span><span class="token">/</span><span class="token6">7</span> <span class="token6">7</span><span class="token">/</span><span class="token6">12</span><span class="token">/</span><span class="token6">7</span> <span class="token6">6</span><span class="token">/</span><span class="token6">10</span><span class="token">/</span><span class="token6">7</span>
f <span class="token6">1</span><span class="token">/</span><span class="token6">2</span><span class="token">/</span><span class="token6">8</span> <span class="token6">2</span><span class="token">/</span><span class="token6">9</span><span class="token">/</span><span class="token6">8</span> <span class="token6">3</span><span class="token">/</span><span class="token6">13</span><span class="token">/</span><span class="token6">8</span>
f <span class="token6">1</span><span class="token">/</span><span class="token6">2</span><span class="token">/</span><span class="token6">8</span> <span class="token6">3</span><span class="token">/</span><span class="token6">13</span><span class="token">/</span><span class="token6">8</span> <span class="token6">4</span><span class="token">/</span><span class="token6">14</span><span class="token">/</span><span class="token6">8</span>
```
```
因此:
-
# 是注釋標記,就像C++中的//
- usemtl和mtlib描述了模型的外觀。本課用不到。
- v代表頂點
- vt代表頂點的紋理坐標
- vn代表頂點的法向
- f代表面
v vt vn都很好理解。f比較麻煩。例如f 8/11/7 7/12/7 6/10/7:
- 8/11/7描述了三角形的第一個頂點
- 7/12/7描述了三角形的第二個頂點
- 6/10/7描述了三角形的第三個頂點
- 對于第一個頂點,8指向要用的頂點。此例中是-1.000000 1.000000 -1.000000(索引從1開始,和C++中從0開始不同)
- 11指向要用的紋理坐標。此例中是0.748355 0.998230。
- 7指向要用的法向。此例中是0.000000 1.000000 -0.000000。
我們稱這些數字為索引。若幾個頂點共用同一個坐標,索引就顯得很方便,文件中只需保存一個“V”,可以多次引用,節省了存儲空間。
不好的地方在于,我們不能讓OpenGL混用頂點、紋理和法向索引。因此本課采用的方法是創建一個標準的、未加索引的模型。等第九課時再討論索引,屆時將會介紹如何解決OpenGL的索引問題。
### 用Blender創建OBJ文件
我們寫的蹩腳加載器功能實在有限,因此在導出模型時得格外小心。下圖展示了在Blender中導出模型的情形:

### 讀取OBJ文件
OK,真正開始編碼了。需要一些臨時變量存儲.obj文件的內容。
```
<pre class="calibre16">```
std<span class="token1">:</span><span class="token1">:</span>vector vertexIndices<span class="token1">,</span> uvIndices<span class="token1">,</span> normalIndices<span class="token1">;</span>
std<span class="token1">:</span><span class="token1">:</span>vector temp_vertices<span class="token1">;</span>
std<span class="token1">:</span><span class="token1">:</span>vector temp_uvs<span class="token1">;</span>
std<span class="token1">:</span><span class="token1">:</span>vector temp_normals<span class="token1">;</span>
```
```
學第五課紋理立方體時,你已學會如何打開文件了:
```
<pre class="calibre16">```
FILE <span class="token">*</span> file <span class="token">=</span> <span class="token3">fopen</span><span class="token1">(</span>path<span class="token1">,</span> <span class="token5">"r"</span><span class="token1">)</span><span class="token1">;</span>
<span class="token4">if</span><span class="token1">(</span> file <span class="token">==</span> NULL <span class="token1">)</span><span class="token1">{</span>
<span class="token3">printf</span><span class="token1">(</span><span class="token5">"Impossible to open the file !n"</span><span class="token1">)</span><span class="token1">;</span>
<span class="token4">return</span> <span class="token6">false</span><span class="token1">;</span>
<span class="token1">}</span>
```
```
讀文件直到文件末尾:
```
<pre class="calibre16">```
<span class="token4">while</span><span class="token1">(</span> <span class="token6">1</span> <span class="token1">)</span><span class="token1">{</span>
char lineHeader<span class="token1">[</span><span class="token6">128</span><span class="token1">]</span><span class="token1">;</span>
<span class="token2">// read the first word of the line</span>
int res <span class="token">=</span> <span class="token3">fscanf</span><span class="token1">(</span>file<span class="token1">,</span> <span class="token5">"%s"</span><span class="token1">,</span> lineHeader<span class="token1">)</span><span class="token1">;</span>
<span class="token4">if</span> <span class="token1">(</span>res <span class="token">==</span> EOF<span class="token1">)</span>
<span class="token4">break</span><span class="token1">;</span> <span class="token2">// EOF = End Of File. Quit the loop.</span>
<span class="token2">// else : parse lineHeader</span>
```
```
(注意,我們假設第一行的文字長度不超過128,這樣做太愚蠢了。但既然這只是個實驗品,就湊合一下吧)
首先處理頂點:
```
<pre class="calibre16">```
<span class="token4">if</span> <span class="token1">(</span> <span class="token3">strcmp</span><span class="token1">(</span> lineHeader<span class="token1">,</span> <span class="token5">"v"</span> <span class="token1">)</span> <span class="token">==</span> <span class="token6">0</span> <span class="token1">)</span><span class="token1">{</span>
glm<span class="token1">:</span><span class="token1">:</span>vec3 vertex<span class="token1">;</span>
<span class="token3">fscanf</span><span class="token1">(</span>file<span class="token1">,</span> <span class="token5">"%f %f %fn"</span><span class="token1">,</span> <span class="token">&</span>vertex<span class="token1">.</span>x<span class="token1">,</span> <span class="token">&</span>vertex<span class="token1">.</span>y<span class="token1">,</span> <span class="token">&</span>vertex<span class="token1">.</span>z <span class="token1">)</span><span class="token1">;</span>
temp_vertices<span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>vertex<span class="token1">)</span><span class="token1">;</span>
```
```
也就是說,若第一個字是“v”,則后面一定是3個float值,于是以這3個值創建一個glm::vec3變量,將其添加到數組。
```
<pre class="calibre16">```
<span class="token1">}</span><span class="token4">else</span> <span class="token4">if</span> <span class="token1">(</span> <span class="token3">strcmp</span><span class="token1">(</span> lineHeader<span class="token1">,</span> <span class="token5">"vt"</span> <span class="token1">)</span> <span class="token">==</span> <span class="token6">0</span> <span class="token1">)</span><span class="token1">{</span>
glm<span class="token1">:</span><span class="token1">:</span>vec2 uv<span class="token1">;</span>
<span class="token3">fscanf</span><span class="token1">(</span>file<span class="token1">,</span> <span class="token5">"%f %fn"</span><span class="token1">,</span> <span class="token">&</span>uv<span class="token1">.</span>x<span class="token1">,</span> <span class="token">&</span>uv<span class="token1">.</span>y <span class="token1">)</span><span class="token1">;</span>
temp_uvs<span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>uv<span class="token1">)</span><span class="token1">;</span>
```
```
也就是說,如果不是“v”而是“vt”,那后面一定是2個float值,于是以這2個值創建一個glm::vec2變量,添加到數組。
以同樣的方式處理法向:
```
<pre class="calibre16">```
<span class="token1">}</span><span class="token4">else</span> <span class="token4">if</span> <span class="token1">(</span> <span class="token3">strcmp</span><span class="token1">(</span> lineHeader<span class="token1">,</span> <span class="token5">"vn"</span> <span class="token1">)</span> <span class="token">==</span> <span class="token6">0</span> <span class="token1">)</span><span class="token1">{</span>
glm<span class="token1">:</span><span class="token1">:</span>vec3 normal<span class="token1">;</span>
<span class="token3">fscanf</span><span class="token1">(</span>file<span class="token1">,</span> <span class="token5">"%f %f %fn"</span><span class="token1">,</span> <span class="token">&</span>normal<span class="token1">.</span>x<span class="token1">,</span> <span class="token">&</span>normal<span class="token1">.</span>y<span class="token1">,</span> <span class="token">&</span>normal<span class="token1">.</span>z <span class="token1">)</span><span class="token1">;</span>
temp_normals<span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>normal<span class="token1">)</span><span class="token1">;</span>
```
```
接下來是“f”,略難一些:
```
<pre class="calibre16">```
<span class="token1">}</span><span class="token4">else</span> <span class="token4">if</span> <span class="token1">(</span> <span class="token3">strcmp</span><span class="token1">(</span> lineHeader<span class="token1">,</span> <span class="token5">"f"</span> <span class="token1">)</span> <span class="token">==</span> <span class="token6">0</span> <span class="token1">)</span><span class="token1">{</span>
std<span class="token1">:</span><span class="token1">:</span>string vertex1<span class="token1">,</span> vertex2<span class="token1">,</span> vertex3<span class="token1">;</span>
unsigned int vertexIndex<span class="token1">[</span><span class="token6">3</span><span class="token1">]</span><span class="token1">,</span> uvIndex<span class="token1">[</span><span class="token6">3</span><span class="token1">]</span><span class="token1">,</span> normalIndex<span class="token1">[</span><span class="token6">3</span><span class="token1">]</span><span class="token1">;</span>
int matches <span class="token">=</span> <span class="token3">fscanf</span><span class="token1">(</span>file<span class="token1">,</span> <span class="token5">"%d/%d/%d %d/%d/%d %d/%d/%dn"</span><span class="token1">,</span> <span class="token">&</span>vertexIndex<span class="token1">[</span><span class="token6">0</span><span class="token1">]</span><span class="token1">,</span> <span class="token">&</span>uvIndex<span class="token1">[</span><span class="token6">0</span><span class="token1">]</span><span class="token1">,</span> <span class="token">&</span>normalIndex<span class="token1">[</span><span class="token6">0</span><span class="token1">]</span><span class="token1">,</span> <span class="token">&</span>vertexIndex<span class="token1">[</span><span class="token6">1</span><span class="token1">]</span><span class="token1">,</span> <span class="token">&</span>uvIndex<span class="token1">[</span><span class="token6">1</span><span class="token1">]</span><span class="token1">,</span> <span class="token">&</span>normalIndex<span class="token1">[</span><span class="token6">1</span><span class="token1">]</span><span class="token1">,</span> <span class="token">&</span>vertexIndex<span class="token1">[</span><span class="token6">2</span><span class="token1">]</span><span class="token1">,</span> <span class="token">&</span>uvIndex<span class="token1">[</span><span class="token6">2</span><span class="token1">]</span><span class="token1">,</span> <span class="token">&</span>normalIndex<span class="token1">[</span><span class="token6">2</span><span class="token1">]</span> <span class="token1">)</span><span class="token1">;</span>
<span class="token4">if</span> <span class="token1">(</span>matches <span class="token">!=</span> <span class="token6">9</span><span class="token1">)</span><span class="token1">{</span>
<span class="token3">printf</span><span class="token1">(</span><span class="token5">"File can't be read by our simple parser : ( Try exporting with other optionsn"</span><span class="token1">)</span><span class="token1">;</span>
<span class="token4">return</span> <span class="token6">false</span><span class="token1">;</span>
<span class="token1">}</span>
vertexIndices<span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>vertexIndex<span class="token1">[</span><span class="token6">0</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span>
vertexIndices<span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>vertexIndex<span class="token1">[</span><span class="token6">1</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span>
vertexIndices<span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>vertexIndex<span class="token1">[</span><span class="token6">2</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span>
uvIndices <span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>uvIndex<span class="token1">[</span><span class="token6">0</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span>
uvIndices <span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>uvIndex<span class="token1">[</span><span class="token6">1</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span>
uvIndices <span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>uvIndex<span class="token1">[</span><span class="token6">2</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span>
normalIndices<span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>normalIndex<span class="token1">[</span><span class="token6">0</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span>
normalIndices<span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>normalIndex<span class="token1">[</span><span class="token6">1</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span>
normalIndices<span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>normalIndex<span class="token1">[</span><span class="token6">2</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span>
```
```
代碼與前面的類似,只不過讀取的數據多一些。
### 處理數據
我們只需改變一下數據的形式。讀取的是字符串,現在有了一組數組。這還不夠,我們得把數據組織成OpenGL要求的形式。也就是去掉索引,只保留頂點坐標數據。這步操作稱為索引。
遍歷每個三角形(每個“f”行)的每個頂點(每個 v/vt/vn):
```
<pre class="calibre16">```
<span class="token2">// For each vertex of each triangle</span>
<span class="token4">for</span><span class="token1">(</span> unsigned int i<span class="token">=</span><span class="token6">0</span><span class="token1">;</span> i
```
```
頂點坐標的索引存放到vertexIndices\[i\]:
```
<pre class="calibre16">```
unsigned int vertexIndex <span class="token">=</span> vertexIndices<span class="token1">[</span>i<span class="token1">]</span><span class="token1">;</span>
```
```
因此坐標是temp\_vertices\[ vertexIndex-1 \](-1是因為C++的下標從0開始,而OBJ的索引從1開始,還記得嗎?):
```
<pre class="calibre16">```
glm<span class="token1">:</span><span class="token1">:</span>vec3 vertex <span class="token">=</span> temp_vertices<span class="token1">[</span> vertexIndex<span class="token">-</span><span class="token6">1</span> <span class="token1">]</span><span class="token1">;</span>
```
```
這樣就有了一個頂點坐標:
```
<pre class="calibre16">```
out_vertices<span class="token1">.</span><span class="token3">push_back</span><span class="token1">(</span>vertex<span class="token1">)</span><span class="token1">;</span>
```
```
UV和法向同理,任務完成!
## 使用加載的數據
到這一步,幾乎什么變化都沒發生。這次我們不再聲明一個static const GLfloat g\_vertex\_buffer\_data\[\] = {…},而是創建一個頂點數組(UV和法向同理)。用正確的參數調用loadOBJ:
```
<pre class="calibre16">```
<span class="token2">// Read our .obj file</span>
std<span class="token1">:</span><span class="token1">:</span>vector vertices<span class="token1">;</span>
std<span class="token1">:</span><span class="token1">:</span>vector uvs<span class="token1">;</span>
std<span class="token1">:</span><span class="token1">:</span>vector normals<span class="token1">;</span> <span class="token2">// Won't be used at the moment.</span>
bool res <span class="token">=</span> <span class="token3">loadOBJ</span><span class="token1">(</span><span class="token5">"cube.obj"</span><span class="token1">,</span> vertices<span class="token1">,</span> uvs<span class="token1">,</span> normals<span class="token1">)</span><span class="token1">;</span>
```
```
把數組傳給OpenGL:
```
<pre class="calibre16">```
<span class="token3">glBufferData</span><span class="token1">(</span>GL_ARRAY_BUFFER<span class="token1">,</span> vertices<span class="token1">.</span><span class="token3">size</span><span class="token1">(</span><span class="token1">)</span> <span class="token">*</span> <span class="token3">sizeof</span><span class="token1">(</span>glm<span class="token1">:</span><span class="token1">:</span>vec3<span class="token1">)</span><span class="token1">,</span> <span class="token">&</span>vertices<span class="token1">[</span><span class="token6">0</span><span class="token1">]</span><span class="token1">,</span> GL_STATIC_DRAW<span class="token1">)</span><span class="token1">;</span>
```
```
結束了!
## 結果
不好意思,紋理不好看。我不太擅長美工。歡迎您來提供一些好的紋理。
## 其他模型格式及加載器
這個小巧的加載器應該比較適合初學,不過別在實際中使用它。參考一下[實用鏈接和工具](http://www.opengl-tutorial.org/miscellaneous/useful-tools-links/)頁面,看看有什么能用的。不過請注意,等到第九課才會真正用到這些工具。