不同 3D 引擎实现千差万别,但模型都是由非常多的三角形拼接组成。解析模型数据,然后得到所有三角形并完成绘制。
OpenGL 模型加载。解析模型文件,得到一堆三角形数据,把这些三角形绘制出来,就是这个样子了。
本文源码 LearnOpenGL-Model
一个非常流行的模型导入库是 Assimp,它是 Open Asset Import Library(开放的资产导入库)的缩写。 Assimp 能够导入很多种不同的模型文件格式(并也能够导出部分的格式),它会将所有的模型数据加载至 Assimp 的通用数据结构中。
一个 Mesh 对象本身包含了渲染所需要的所有相关数据,像是顶点位置、法向量、纹理坐标、面 (Face) 和物体的材质。
一个网格包含了多个面。Face 代表的是物体的渲染图元 (Primitive)(三角形、方形、点)。一个面包含了组成图元的顶点的索引。由于顶点和索引是分开的,使用一个索引缓冲来渲染是非常简单的。
我们定义 Vertex:
struct Vertex {
glm::vec3 Position; // 顶点位置
glm::vec3 Normal; // 法线
glm::vec2 TexCoords; // 纹理坐标
};
定义一个纹理:
struct Texture {
unsigned int id; // 对应的 glGenTextures ID。
string type; // 和 glsl shader 对应,根据这个名字完成参数传递。
string path; // 图片文件路径。
};
#define TEXTURE_TYPE_DIFFUSE "texture_diffuse"
#define TEXTURE_TYPE_SPECULAR "texture_specular"
相应的 glsl 就是:
uniform sampler2D texture_diffuse1;
那么一个网格(Mesh)可以定义为:
class Mesh {
vector<Vertex> vertices;
vector<unsigned int> indices;
vector<Texture> textures;
unsigned int VAO, VBO, EBO;
};
// 绘制就是:
void Draw(Shader& shader)
{
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
}
一个模型由非常多的 Mesh 组成,可以定义为:
class Model {
vector<Mesh> meshes;
};
// 绘制就是:
void Draw(Shader& shader)
{
for (unsigned int i = 0; i < meshes.size(); i++)
meshes[i].Draw(shader);
}