OBJ文件格式详解
OBJ文件是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种标准3D模型文件格式,很适合用于3D软件模型之间的互导,也可以通过Maya读写。比如你在3dsMax或LightWave中建了一个模型,想把它调到Maya里面渲染或动画,导出OBJ文件就是一种很好的选择。目前几乎所有知名的3D软件都支持OBJ文件的读写,不过其中很多需要通过插件才能实现。
OBJ文件是一种文本文件,可以直接用写字板打开进行查看和编辑修改。另外,有一种与此相关二进制文件格式(*.MOD),其作为专利未公开,因此这里不作讨论。
1、OBJ文件的特点
OBJ3.0文件格式支持直线(Line)、多边形(Polygon)、表面(Surface)和自由形态曲线(Free-form Curve)。直线和多边形通过它们的点来描述,曲线和表面则根据它们的控制点和依附于曲线类型的额外信息来定义,这些信息支持规则和不规则的曲线,包括那些基于贝塞尔曲线(Bezier)、B样条(B-spline)、基数(Cardinal/Catmull-Rom)和泰勒方程(Taylor equations)的曲线。其他特点如下:
(1)OBJ文件是一种3D模型文件。不包含动画、材质特性、贴图路径、动力学、粒子等信息。
(2)OBJ文件主要支持多边形(Polygons)模型。虽然也支持曲线(Curves)、表面(Surfaces)、点组材质(Point Group Materials),但Maya导出的OBJ文件并不包括这些信息。
(3)OBJ文件支持三个点以上的面,这一点很有用。很多其它的模型文件格式只支持三个点的面,所以导入Maya的模型经常被三角化了,这对于我们对模型进行再加工甚为不利。
(4)OBJ文件支持法线和贴图坐标。在其它软件中调整好贴图后,贴图坐标信息可以存入OBJ文件中,这样文件导入Maya后只需指定一下贴图文件路径就行了,不需要再调整贴图坐标。
2、OBJ文件的基本结构
OBJ文件不需要任何种文件头(File Header),尽管经常使用几行文件信息的注释作为文件的开头。OBJ文件由一行行文本组成,注释行以符号“#”为开头,空格和空行可以随意加到文件中以增加文件的可读性。有字的行都由一两个标记字母也就是关键字(Keyword)开头,关键字可以说明这一行是什么样的数据。多行可以逻辑地连接在一起表示一行,方法是在每一行最后添加一个连接符(\)。 注意连接符(\)后面不能出现空格或Tab格,否则将导致文件出错。
下列关键字可以在OBJ文件使用。在这个列表中, 关键字根据数据类型排列,每个关键字有一段简短描述。
顶点数据(Vertex data):
v 几何体顶点(Geometric vertices)
vt 贴图坐标点(Texture vertices)
vn 顶点法线(Vertex normals)
vp 参数空格顶点 (Parameter space vertices)
自由形态曲线(Free-form curve)/表面属性(surface attributes):
deg 度(Degree)
bmat 基础矩阵(Basis matrix)
step 步尺寸(Step size)
cstype 曲线或表面类型 (Curve or surface type)
元素(Elements):
p 点(Point)
l 线(Line)
f 面(Face)
curv 曲线(Curve)
curv2 2D曲线(2D curve)
surf 表面(Surface)
自由形态曲线(Free-form curve)/表面主体陈述(surface body statements):
parm 参数值(Parameter values )
trim 外部修剪循环(Outer trimming loop)
hole 内部整修循环(Inner trimming loop)
scrv 特殊曲线(Special curve)
sp 特殊的点(Special point)
end 结束陈述(End statement)
自由形态表面之间的连接(Connectivity between free-form surfaces):
con 连接 (Connect)
成组(Grouping):
g 组名称(Group name)
s 光滑组(Smoothing group)
mg 合并组(Merging group)
o 对象名称(Object name)
显示(Display)/渲染属性(render attributes):
bevel 导角插值(Bevel interpolation)
c_interp 颜色插值(Color interpolation)
d_interp 溶解插值(Dissolve interpolation)
lod 细节层次(Level of detail)
usemtl 材质名称(Material name)
mtllib 材质库(Material library)
shadow_obj 投射阴影(Shadow casting)
trace_obj 光线跟踪(Ray tracing)
ctech 曲线近似技术(Curve approximation technique)
stech 表面近似技术 (Surface approximation technique)
3、OBJ文件实例
的确挺难理解,下面通过实例来具体讲解。让我们来创建一个OBJ文件,内容为一个四边形,不过这一回我们不用3D软件,而是用写字板来创建。打开写字板,把下面的5行代码写入,可以适当加一点注释。保存文件为文本格式,文件名为"myObj.obj"。
- <span style="font-family:Microsoft YaHei;font-size:12px;"> v -0.58 0.84 0
- v 2.68 1.17 0
- v 2.84 -2.03 0
- v -1.92 -2.89 0
- f 1 2 3 4</span>
我们来分析一下这些代码。
v -0.58 0.84 0 画一个四边形需要四个顶点,这是第一个顶点,"v"表示顶点(vertex),"-0.58"为这个顶点的X轴坐标值,"0.84"为Y轴坐标值,"0"为Z轴坐标值。它的索引号是1。索引号是画面时要用到的。 v 2.68 1.17 0 v 2.84 -2.03 0 v -1.92 -2.89 0 这分别是第二、三、四个顶点,它们的索引号分别是2,3,4。
f 1 2 3 4 现在开始画面,"f"表示面(face),1,2,3,4是前面那四个顶点的索引号。请注意画这个面连接点的顺序,是从第一个点出发,依次连接第二、三、四个点。如果连接的顺序不同所生成的面也会截然不同,例如"f 1 2 4 3"会产生一个交迭的面。
面的连接点是按顺时针排列或逆时针排列,将决定面的法线方向(面的反正)。例如:"f 1 2 3 4"面的法线向外,"f 4 3 2 1"面的法线向里。 面的连接点顺序错误,是导致导入模型产生碎面的一个重要原因。一个面不能出现两个以上相同的顶点,这也是检查OBJ文件出错的一个要点。例如:"f 1 2 3 4 3",有两个相同的顶点,索引号是3。一个面出现两个相同顶点,可能造成程序的内存分配错误。 下面来研究一下Maya导出的OBJ文件。 在Maya中创建一个多边形立方体,选中这个立方体,选择菜单"File -> Export Selection..."导出格式为OBJ,文件名为"cube.obj",如果没有此格式,请在Plug-in Manager中载入"objExport.mll"。 用写字板打开"cube.obj",可以看到如下代码:
- <span style="font-family:Microsoft YaHei;font-size:12px;"># The units used in this file are centimeters.
- g default
- v -0.500000 -0.500000 0.500000
- v 0.500000 -0.500000 0.500000
- v -0.500000 0.500000 0.500000
- v 0.500000 0.500000 0.500000
- v -0.500000 0.500000 -0.500000
- v 0.500000 0.500000 -0.500000
- v -0.500000 -0.500000 -0.500000
- v 0.500000 -0.500000 -0.500000
- vt 0.000000 0.000000
- vt 1.000000 0.000000
- vt 0.000000 1.000000
- vt 1.000000 1.000000
- vt 0.000000 2.000000
- vt 1.000000 2.000000
- vt 0.000000 3.000000
- vt 1.000000 3.000000
- vt 0.000000 4.000000
- vt 1.000000 4.000000
- vt 2.000000 0.000000
- vt 2.000000 1.000000
- vt -1.000000 0.000000
- vt -1.000000 1.000000
- vn 0.000000 0.000000 1.000000
- vn 0.000000 0.000000 1.000000
- vn 0.000000 0.000000 1.000000
- vn 0.000000 0.000000 1.000000
- vn 0.000000 1.000000 0.000000
- vn 0.000000 1.000000 0.000000
- vn 0.000000 1.000000 0.000000
- vn 0.000000 1.000000 0.000000
- vn 0.000000 0.000000 -1.000000
- vn 0.000000 0.000000 -1.000000
- vn 0.000000 0.000000 -1.000000
- vn 0.000000 0.000000 -1.000000
- vn 0.000000 -1.000000 0.000000
- vn 0.000000 -1.000000 0.000000
- vn 0.000000 -1.000000 0.000000
- vn 0.000000 -1.000000 0.000000
- vn 1.000000 0.000000 0.000000
- vn 1.000000 0.000000 0.000000
- vn 1.000000 0.000000 0.000000
- vn 1.000000 0.000000 0.000000
- vn -1.000000 0.000000 0.000000
- vn -1.000000 0.000000 0.000000
- vn -1.000000 0.000000 0.000000
- vn -1.000000 0.000000 0.000000
- s off
- g pCube1
- usemtl initialShadingGroup
- f 1/1/1 2/2/2 4/4/3 3/3/4
- f 3/3/5 4/4/6 6/6/7 5/5/8
- f 5/5/9 6/6/10 8/8/11 7/7/12
- f 7/7/13 8/8/14 2/10/15 1/9/16
- f 2/2/17 8/11/18 6/12/19 4/4/20
- f 7/13/21 1/1/22 3/3/23 5/14/24</span>
"vt 1.000000 0.000000"这句"vt"代表点的贴图坐标。
"vn 0.000000 0.000000 -1.000000"这句"vn"代表点的法线。
"s off"表示关闭光滑组。
"usemtl initialShadingGroup"表示使用的材质。
"f 7/13/21"这时在面的数据中多了贴图坐标uv点和法线的索引号,索引号分别用左斜线(/)隔开。
格式:"f 顶点索引/uv点索引/法线索引"。
"g pCube1"表示组,这里的成组与Maya中的成组不一样,这里的成组是指把"g pCube1"后出现的面都结合到一起,组成一个整的多边形几何体。
把"cube.obj"文件修改一下就知道成组的意思了。把"s off"这句后面的代码替换成以下代码:
- <span style="font-family:Microsoft YaHei;font-size:12px;">usemtl initialShadingGroup
- g pCube_Face1
- f 1/1/1 2/2/2 4/4/3 3/3/4
- g pCube_Face2
- f 3/3/5 4/4/6 6/6/7 5/5/8
- g pCube_Face3
- f 5/5/9 6/6/10 8/8/11 7/7/12
- g pCube_Face4
- f 7/7/13 8/8/14 2/10/15 1/9/16
- g pCube_Face5
- f 2/2/17 8/11/18 6/12/19 4/4/20
- g pCube_Face6
- f 7/13/21 1/1/22 3/3/23 5/14/24</span>
可不可以用中文命名几何体(组)呢?试试就知道了,把前面的代码改成:
- <span style="font-family:Microsoft YaHei;font-size:12px;">usemtl initialShadingGroup
- g 立方体面1
- f 1/1/1 2/2/2 4/4/3 3/3/4
- g 立方体面2
- f 3/3/5 4/4/6 6/6/7 5/5/8
- g 立方体面3
- f 5/5/9 6/6/10 8/8/11 7/7/12
- g 立方体面4
- f 7/7/13 8/8/14 2/10/15 1/9/16
- g 立方体面5
- f 2/2/17 8/11/18 6/12/19 4/4/20
- g 立方体面6
- f 7/13/21 1/1/22 3/3/23 5/14/24</span>
// Error: line 1: Your OBJ file contains a line which is too long to be parsed. Please edit your obj file. //
// Error: line 1: Error reading file. //
由此可见,物体命名的不规范也是导致OBJ文件出错的原因之一。
关于Maya的物体命名,英文名是很保险的,标点符号中只有下划线(_)可用,数字不能用放到名称的开头,尽量不要用中、日、韩等双字节文字。
OBJ文件不支持有孔的多边形面。举个例子说明一下:选择Maya的创建多边形工具(Polygons -> Create Polyon Tool),在视图中画一个四边形,不要按回车,按Ctrl在四边形中间点一下,可以继续在四边形中挖一个洞。把这个有孔的多边形存成OBJ格式,在导入Maya时,会发现多边形少了一块。如果你把这也看成错误,现在至少你已经知道错误的原因了,就是OBJ文件不支持有孔的多边形面。
4、OBJ文件的实际问题:
现在来讨论一点比较实际的问题吧,就是一旦你遇到了一个出错的OBJ文件,倒底该怎么办?
当你打开OBJ文件后,往往会看到有几万行的代码,你恐怕还没本事情一眼看出错误所在行,除非程序的错误信息中已经告诉你错误行。如果你不知道错误在哪里,可以用排除法,弄清楚肯定正确的代码范围,通过缩减错误代码范围定位错误。例如,你先新建一个空的OBJ文件,把有错的OBJ文件代码粘贴一半过来,然后把这个只有一半代码的新OBJ文件导入Maya。如果这时没有错误信息,说明错误行是在另一半代码中,可以从另一半代码中再粘贴一部分代码试试看;如果这时出现错误,说明错误行就在粘贴的代码中,可以把粘贴过来的代码删去一部分再试试看。就这样,逐步缩减范围直到找到错误行为止。
这种方法虽然很麻烦,不过颇为有效。如果你不会编程,又遇到非常紧急的情况,这种方法还是值得一试的。
5、OBJ文件的更多细节:
简单的OBJ格式写法。
- <span style="font-family:Microsoft YaHei;font-size:12px;"># Simple Wavefront file
- v 0.0 0.0 0.0
- v 0.0 1.0 0.0
- v 1.0 0.0 0.0
- f 1 2 3</span>
- <span style="font-family:Microsoft YaHei;font-size:12px;">v -0.500000 0.000000 0.400000
- v -0.500000 0.000000 -0.800000
- v -0.500000 1.000000 -0.800000
- v -0.500000 1.000000 0.400000
- f -4 -3 -2 -1</span>
指定材质的方法:
- <span style="font-family:Microsoft YaHei;font-size:12px;">Cube with Materials:
- # This cube has a different material
- # applied to each of its faces.
- mtllib master.mtl
- v 0.000000 2.000000 2.000000
- v 0.000000 0.000000 2.000000
- v 2.000000 0.000000 2.000000
- v 2.000000 2.000000 2.000000
- v 0.000000 2.000000 0.000000
- v 0.000000 0.000000 0.000000
- v 2.000000 0.000000 0.000000
- v 2.000000 2.000000 0.000000
- # 8 vertices
- g front
- usemtl red
- f 1 2 3 4
- g back
- usemtl blue
- f 8 7 6 5
- g right
- usemtl green
- f 4 3 7 8
- g top
- usemtl gold
- f 5 1 4 8
- g left
- usemtl orange
- f 5 6 2 1
- g bottom
- usemtl purple
- f 2 6 7 3
- # 6 elements</span>
贝塞尔片面(Bezier Patch):
Maya不能导出OBJ格式的贝塞尔片面,却能够导入它。导入的贝塞尔片面自动转换为Nurbs表面。
- <span style="font-family:Microsoft YaHei;font-size:12px;"># 3.0 Bezier patch
- v -5.000000 -5.000000 0.000000
- v -5.000000 -1.666667 0.000000
- v -5.000000 1.666667 0.000000
- v -5.000000 5.000000 0.000000
- v -1.666667 -5.000000 0.000000
- v -1.666667 -1.666667 0.000000
- v -1.666667 1.666667 0.000000
- v -1.666667 5.000000 0.000000
- v 1.666667 -5.000000 0.000000
- v 1.666667 -1.666667 0.000000
- v 1.666667 1.666667 0.000000
- v 1.666667 5.000000 0.000000
- v 5.000000 -5.000000 0.000000
- v 5.000000 -1.666667 0.000000
- v 5.000000 1.666667 0.000000
- v 5.000000 5.000000 0.000000
- # 16 vertices
- cstype bezier
- deg 3 3
- # Example of line continuation
- surf 0.000000 1.000000 0.000000 1.000000 13 14 \
- 15 16 9 10 11 12 5 6 7 8 1 2 3 4
- parm u 0.000000 1.000000
- parm v 0.000000 1.000000
- end
- # 1 element</span>
Maya好像不支持OBJ格式的曲线,导入时不会出现错误信息,却也不会出现曲线。
- <span style="font-family:Microsoft YaHei;font-size:12px;"># 3.0 Cardinal curve
- v 0.940000 1.340000 0.000000
- v -0.670000 0.820000 0.000000
- v -0.770000 -0.940000 0.000000
- v 1.030000 -1.350000 0.000000
- v 3.070000 -1.310000 0.000000
- # 6 vertices
- cstype cardinal
- deg 3
- curv 0.000000 3.000000 1 2 3 4 5 6
- parm u 0.000000 1.000000 2.000000 3.000000 end
- # 1 element</span>
贴图映射(Texture-Mapped):
- <span style="font-family:Microsoft YaHei;font-size:12px;"># A 2 x 2 square mapped with a 1 x 1 square
- # texture stretched to fit the square exactly.
- mtllib master.mtl
- v 0.000000 2.000000 0.000000
- v 0.000000 0.000000 0.000000
- v 2.000000 0.000000 0.000000
- v 2.000000 2.000000 0.000000
- vt 0.000000 1.000000 0.000000
- vt 0.000000 0.000000 0.000000
- vt 1.000000 0.000000 0.000000
- vt 1.000000 1.000000 0.000000
- # 4 vertices
- usemtl wood
- # The first number is the point,
- # then the slash,
- # and the second is the texture point
- f 1/1 2/2 3/3 4/4
- # 1 element</span>