您的位置:

深入了解.obj文件

一、.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; }