一、.obj文件简介
OBJ文件是一种包含三维几何体信息的文件格式,它包括物体的位置、法线、纹理坐标和面信息。该格式最初是由AutoCAD创建的,现在被广泛用于3D图形应用程序中
OBJ文件是一种包含模型和材质数据的文本文件,其结构非常简单:文件头信息、顶点和法线、纹理坐标、面等。在OBJ文件中,每行都表示一个参数,用空格或者制表符分隔。这使得它成为一种简单、通用的格式,因为几乎任何3D图形软件都能读取和写入OBJ格式。因此,OBJ文件成为了3D图形软件间通用的桥梁。
二、.obj文件的结构
OBJ文件的结构由一系列的组成部分组成:
1.文件头
OBJ文件格式的第一行通常是格式说明。在这一行中,#代表注释(注释应该位于该行的开头),后面可以跟着一些数字或字母代表该文件的格式版本、作者等信息。
# This is a Wavefront OBJ file # File created by Blender
2.顶点和法线
OBJ文件保存的每个物体顶点都以"v"开头的一行开始,后面跟着xyz三个浮点数表示三维位置坐标。此外,以"vn"开头的行代表了每个顶点的法向量。
# Vertex coordinates v 0.0 0.0 0.0 v 1.0 0.0 0.0 v 0.0 1.0 0.0 vn 0.0 0.0 1.0 vn 0.0 0.0 1.0 vn 0.0 0.0 1.0
3.纹理坐标
使用"vt"开头的行表示每个顶点的纹理坐标。纹理坐标通常是二维的,以u,v两个浮点数表示。
# Texture coordinates vt 0.0 0.0 vt 1.0 0.0 vt 0.0 1.0
4.面
面的定义以"f"开头的一行开始,后跟一系列整数表示该面顶点的索引号。索引号指向前面定义的顶点列表和纹理坐标列表。面的定义可以使用两种不同的格式表示:单个面和多重面。单个面只包含三个顶点,多重面可以包含任意数量的顶点。
# Face definitions f 1/1/1 2/2/2 3/3/3 f 1/1/1 3/3/3 4/4/4
5.材质和组
OBJ文件格式还支持材质和组。使用"mtllib"定义材质库,使用"usemtl"定义使用材质,使用"g"定义物体组。
# Material definitions mtllib example.mtl usemtl red usemtl green # Object groups g object1 g object2
三、示例代码
下面是一个读取并打印OBJ文件的C++函数示例:
#include#include #include #include #include struct Vec3 { float x, y, z; }; struct Vec2 { float u, v; }; struct TriangleIndex { int vertexIdx[3]; int normalIdx[3]; int texIdx[3]; }; std::vector vertices; std::vector normals; std::vector texCoords; std::vector indices; void loadObj(const std::string& filename) { std::ifstream file(filename); std::string line; while (std::getline(file, line)) { std::istringstream iss(line); char c; if (!(iss >> c)) continue; switch (c) { case 'v': if (iss.peek() == 'n') { Vec3 normal; iss >> c; iss >> normal.x; iss >> normal.y; iss >> normal.z; normals.push_back(normal); } else if (iss.peek() == 't') { Vec2 texCoord; iss >> c; iss >> texCoord.u; iss >> texCoord.v; texCoords.push_back(texCoord); } else { Vec3 vertex; iss >> vertex.x; iss >> vertex.y; iss >> vertex.z; vertices.push_back(vertex); } break; case 'f': { TriangleIndex index; for (int i = 0; i < 3; ++i) { iss >> index.vertexIdx[i]; if (iss.peek() == '/') { iss.ignore(); if (iss.peek() != '/') { iss >> index.texIdx[i]; } if (iss.peek() == '/') { iss.ignore(); iss >> index.normalIdx[i]; } } } indices.push_back(index); break; } } } } int main() { std::string filename = "example.obj"; loadObj(filename); printf("Vertices:\n"); for (const auto& v : vertices) { printf("%f %f %f\n", v.x, v.y, v.z); } printf("Normals:\n"); for (const auto& n : normals) { printf("%f %f %f\n", n.x, n.y, n.z); } printf("Texture coordinates:\n"); for (const auto& t : texCoords) { printf("%f %f\n", t.u, t.v); } printf("Faces:\n"); for (const auto& i : indices) { printf("%d/%d/%d %d/%d/%d %d/%d/%d\n", i.vertexIdx[0], i.texIdx[0], i.normalIdx[0], i.vertexIdx[1], i.texIdx[1], i.normalIdx[1], i.vertexIdx[2], i.texIdx[2], i.normalIdx[2]); } return 0; }