# 第七课:模型加载
# 第七课:模型加载
目前为止,我们一直在硬编码描述立方体。你一定觉得这样做很笨拙、不方便。
本课将学习从文件中加载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中导出模型的情形:
![](https://box.kancloud.cn/2015-11-02_5636f3051fbe2.png)
### 读取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/)页面,看看有什么能用的。不过请注意,等到第九课才会真正用到这些工具。