OpenGL导入3D模型的准备工作

来源:互联网 发布:怎样上淘宝买东西 编辑:程序博客网 时间:2024/05/11 15:41

之前的博客里已经讲述了如何用OpenGL绘制一些简单的图形以及如何给这些图形上色,比如三角形。但是实际应用中,我们常常需要一些更复杂的形状,比如我们需要绘制人的模型。这时,如果我们还用之前的方法,挨个为模型中的每个顶点指定坐标值将显得非常麻烦。况且,简单的平面图形还好,如果是人脸要如何处理?现实的商业应用和游戏中,程序中使用模型一般都是由美术人员通过如 Blender, Maya 或 3ds Max 等建模软件来解决这个问题。美术人员将这些模型以不同格式的文件保存,且文件中包含了对这些模型的数学解释。在OpenGL中,我们可以借助Assimp库加载这些模型,并且利用这些模型文件中自带的数学解释来对这些模型文件进行更复杂的操作。

以下将首先简单介绍模型加载库Assimp库。

当导入一个模型文件时,即Assimp加载一整个包含所有模型和场景数据的模型文件到一个scene对象时,Assimp会为这个模型文件中的所有场景节点、模型节点都生成一个具有对应关系的数据结构,且将这些场景中的各种元素与模型数据对应起来。下图展示了一个简化的Assimp生成的模型文件数据结构:


从图中我们可以看出Assimp通过树状结构存储模型对象。树中每个节点存储了子节点的地址和一个Mesh数组。Mesh对象为真正存储了渲染模型所需的相关信息,包括节点,法线向量,纹理坐标,面片以及物体的材质。这其中,面片(face)即相当于图元,面片中记录了Mesh中的顶点索引,通过面片我们可以快速地查找Mesh中的每个顶点的位置信息。

实际上,根节点的mMeshes[]存储了所有的Mesh对象,而子节点中只是存储了该节点对应的Mesh对象的指针。

所以,我们在加载模型前要做的操作就是加载一个模型文件为scene对象,然后获取每个节点对应的Mesh对象(我们需要递归搜索每个节点的子节点来获取所有的节点),并处理每个Mesh对象对应的顶点数据、索引以及它的材质属性。最终我们得到一个只包含我们需要的数据的Mesh集合。

首先,我们要做的就是定义一个Mesh类。我们小组定义了SubMesh类,操作具体模型项,先将模型按照节点坐标划分为一个个具体的网格,称为子网格,再依次对每个子网格进行操作。最后,将每个子网格再集合成Mesh。

// 子网格类class SubMesh {public:SubMesh(const VertexBufferPtr &vertex_buffer,  int vertex_start, int vertex_count);SubMesh(const VertexBufferPtr &vertex_buffer,const IndexBufferPtr &index_buffer,int index_start,int index_count);void SetVertexBuffer(const VertexBufferPtr& buffer);const VertexBufferPtr& GetVertexBuffer() const;void SetIndexBuffer(const IndexBufferPtr& buffer);const IndexBufferPtr& GetIndexBuffer() const;void SetVertexStart(int vertex_start);int GetVertexStart() const;void SetVertexCount(int vertex_count);int GetVertexCount() const;void SetIndexStart(int index_start);int GetIndexStart() const;void SetIndexCount(int index_count);int GetIndexCount() const;private:VertexBufferPtr vertex_buffer_;IndexBufferPtr index_buffer_;int vertex_start_;int vertex_count_;int index_start_;int index_count_;};
每个子网格中至少需要存储顶点信息和面片索引信息。为了方便后续的操作,我们给SubMesh定义了四个私有数字变量,分别用来存储第一个定点的坐标值和定点的数量,以及第一个面片的索引值,以及面片总数。



原创粉丝点击