obj模型

2/7/2025 Unity3dmesh

obj格式

OBJ是一种开放的3D模型文件格式。

支持内容

obj 文件,3dmax 支持导出*.obj文件,同时导出 *.mtl,主要是存储贴图、材质等信息

  • OBJ文件:

    • 主要支持多边形(Polygons)模型,包括三角形、四边形等,也可以表示直线(Lines)和自由形态曲线(Free-form Curves)。
    • 支持法线和贴图坐标,但贴图需要手动重新指认。
    • 不包含动画、材质特性、贴图路径、动力学、粒子、蒙皮等信息。
  • FBX文件:

    • 支持所有主要的三维数据元素以及二维、音频和视频媒体元素。
    • 包含动画、材质特性、贴图、骨骼动画、灯光、摄像机等信息。
    • 支持多边形(Polygons)、曲线(Curves)、表面(Surfaces)等。

结构简单,没有复杂的层级结构

obj文件内容

# 3ds Max Wavefront OBJ Exporter v0.99 - (c)2007 guruware
# 创建的文件:07.02.2025 16:46:31

mtllib 无标题.mtl

#
# object Box001
#

v  -4.1020 0.0000 -17.7684
v  -4.1020 0.0000 -37.0538
v  4.4456 0.0000 -37.0538
v  4.4456 0.0000 -17.7684
v  -4.1020 4.0289 -17.7684
v  4.4456 4.0289 -17.7684
v  4.4456 4.0289 -37.0538
v  -4.1020 4.0289 -37.0538
# 8 vertices

vn -0.5774 -0.5774 0.5774
vn -0.5774 -0.5774 -0.5774
vn 0.5774 -0.5774 -0.5774
vn 0.5774 -0.5774 0.5774
vn -0.5774 0.5774 0.5774
vn 0.5774 0.5774 0.5774
vn 0.5774 0.5774 -0.5774
vn -0.5774 0.5774 -0.5774
# 8 vertex normals

vt 1.0000 0.0000 0.0000
vt 1.0000 1.0000 0.0000
vt 0.0000 1.0000 0.0000
vt 0.0000 0.0000 0.0000
# 4 texture coords

o Box001
g Box001
usemtl wire_168183223
s 2
f 1/1/1 2/2/2 3/3/3 4/4/4 
s 4
f 5/4/5 6/1/6 7/2/7 8/3/8 
s 8
f 1/4/1 4/1/4 6/2/6 5/3/5 
s 16
f 4/4/4 3/1/3 7/2/7 6/3/6 
s 32
f 3/4/3 2/1/2 8/2/8 7/3/7 
s 64
f 2/4/2 1/1/1 5/2/5 8/3/8 
# 6 polygons

#
# object Box002
#

v  2.5366 0.0000 3.8365
v  2.5366 0.0000 -4.8824
v  9.3654 0.0000 -4.8824
v  9.3654 0.0000 3.8365
v  2.5366 4.0207 3.8365
v  9.3654 4.0207 3.8365
v  9.3654 4.0207 -4.8824
v  2.5366 4.0207 -4.8824
# 8 vertices

vn -0.5774 -0.5774 0.5774
vn -0.5774 -0.5774 -0.5774
vn 0.5774 -0.5774 -0.5774
vn 0.5774 -0.5774 0.5774
vn -0.5774 0.5774 0.5774
vn 0.5774 0.5774 0.5774
vn 0.5774 0.5774 -0.5774
vn -0.5774 0.5774 -0.5774
# 8 vertex normals

vt 1.0000 0.0000 0.0000
vt 1.0000 1.0000 0.0000
vt 0.0000 1.0000 0.0000
vt 0.0000 0.0000 0.0000
# 4 texture coords

o Box002
g Box001/Box002
usemtl wire_207171210
s 2
f 9/5/9 10/6/10 11/7/11 12/8/12 
s 4
f 13/8/13 14/5/14 15/6/15 16/7/16 
s 8
f 9/8/9 12/5/12 14/6/14 13/7/13 
s 16
f 12/8/12 11/5/11 15/6/15 14/7/14 
s 32
f 11/8/11 10/5/10 16/6/16 15/7/15 
s 64
f 10/8/10 9/5/9 13/6/13 16/7/16 
# 6 polygons


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

帮助:

3D模型obj文件格式

https://zhuanlan.zhihu.com/p/626883173

unity 案例

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
using UnityEngine.Rendering;

public class MeshConvertObj : MonoBehaviour
{
    public Mesh meshToExport;
    public string filePath = "Assets/SceneBake/ExportedMesh.obj";
    void Start()
    {
        var meshFilter = GetComponent<MeshFilter>();

        using (StreamWriter streamWriter = new StreamWriter(filePath))
        {
            streamWriter.Write(MeshToString(meshFilter, new Vector3(-1f, 1f, 1f)));
            streamWriter.Close();
        }

        meshToExport = Parse(filePath);
    }
    

    private string MeshToString(MeshFilter mf, Vector3 scale)
    {
        Mesh mesh = mf.mesh;
        Material[] sharedMaterials = mf.GetComponent<Renderer>().sharedMaterials;
        Vector2 textureOffset = mf.GetComponent<Renderer>().material.GetTextureOffset("_MainTex");
        Vector2 textureScale = mf.GetComponent<Renderer>().material.GetTextureScale("_MainTex");

        StringBuilder stringBuilder = new StringBuilder().Append("mtllib design.mtl")
            .Append("\n")
            .Append("g ")
            .Append(mf.name)
            .Append("\n");

        Vector3[] vertices = mesh.vertices;
        for (int i = 0; i < vertices.Length; i++)
        {
            Vector3 vector = vertices[i];
            stringBuilder.Append(string.Format("v {0} {1} {2}\n", vector.x * scale.x, vector.y * scale.y,
                vector.z * scale.z));
        }

        stringBuilder.Append("\n");

        Dictionary<int, int> dictionary = new Dictionary<int, int>();

        if (mesh.subMeshCount > 1)
        {
            int[] triangles = mesh.GetTriangles(1);

            for (int j = 0; j < triangles.Length; j += 3)
            {
                if (!dictionary.ContainsKey(triangles[j]))
                {
                    dictionary.Add(triangles[j], 1);
                }

                if (!dictionary.ContainsKey(triangles[j + 1]))
                {
                    dictionary.Add(triangles[j + 1], 1);
                }

                if (!dictionary.ContainsKey(triangles[j + 2]))
                {
                    dictionary.Add(triangles[j + 2], 1);
                }
            }
        }

        for (int num = 0; num != mesh.uv.Length; num++)
        {
            Vector2 vector2 = Vector2.Scale(mesh.uv[num], textureScale) + textureOffset;

            if (dictionary.ContainsKey(num))
            {
                stringBuilder.Append(string.Format("vt {0} {1}\n", mesh.uv[num].x, mesh.uv[num].y));
            }
            else
            {
                stringBuilder.Append(string.Format("vt {0} {1}\n", vector2.x, vector2.y));
            }
        }

        for (int k = 0; k < mesh.subMeshCount; k++)
        {
            stringBuilder.Append("\n");

            if (k == 0)
            {
                stringBuilder.Append("usemtl ").Append("Material_design").Append("\n");
            }

            if (k == 1)
            {
                stringBuilder.Append("usemtl ").Append("Material_logo").Append("\n");
            }

            int[] triangles2 = mesh.GetTriangles(k);

            for (int l = 0; l < triangles2.Length; l += 3)
            {
                stringBuilder.Append(string.Format("f {0}/{0} {1}/{1} {2}/{2}\n", triangles2[l] + 1,
                    triangles2[l + 2] + 1, triangles2[l + 1] + 1));
            }
        }

        return stringBuilder.ToString();
    }


    public Mesh Parse(string url)
    {
        Mesh mesh = new Mesh();

        List<Vector3> vts = new List<Vector3>();
        List<Vector2> uvs = new List<Vector2>();
        List<Vector3> normals = new List<Vector3>();
        List<int> tris = new List<int>();


        StreamReader sr = new StreamReader(url);

        while (!sr.EndOfStream)
        {
            string line = sr.ReadLine();
            string[] split = line.Split(' ');

            switch (split[0])
            {
                case "o":
                    mesh.name = split[1];
                    break;

                case "v":
                {
                    float x = float.Parse(split[1]);
                    float y = float.Parse(split[2]);
                    float z = float.Parse(split[3]);

                    vts.Add(new Vector3(x, y, z));
                }
                    break;
                case "vn":
                {
                    float x = float.Parse(split[1]);
                    float y = float.Parse(split[2]);
                    float z = float.Parse(split[3]);

                    normals.Add(new Vector3(x, y, z));
                }
                    break;

                case "vt":
                {
                    float x = float.Parse(split[1]);
                    float y = float.Parse(split[2]);

                    uvs.Add(new Vector2(x, y));
                }
                    break;

                case "f":
                {
                    for (int i = 1; i < split.Length; ++i)
                    {
                        string[] sp = split[i].Split('/');
                        try
                        {
                            tris.Add(int.Parse(sp[0]) - 1);
                        }
                        catch (Exception e)
                        {
                            Debug.LogError(split[i]);
                            Debug.LogError(sp.Length);
                            Debug.LogError(e);
                        }
                    }
                }
                    break;

                default:

                    break;
            }
        }

        mesh.vertices = vts.ToArray();
        mesh.uv = uvs.ToArray();
        mesh.normals = normals.ToArray();
        mesh.triangles = tris.ToArray();

        return mesh;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
Last Updated: Sat, 08 Feb 2025 06:23:40 GMT