OBJ网格模型文件(上) - 学习随笔

很早的时候一直有个执念:

我要从0开始,编程写一个obj模型的加载器。

在学习3dsmax的时候,发现这种格式被很多软件所支持,而且导出的文件只含模型和材质,体积很小很干净,建好模型之后通常保存为自身的.max文件后还要导出一份obj文件以备用。

以前用mod工具把游戏里面的3D模型以obj格式导入导出过,比如GTA,战国无双,甚至后来的王者农药等。比如网上下载一个路飞的obj模型导入到GTA中,然后就可以看到……

OBJ网格模型文件(上) - 学习随笔_第1张图片
震惊!海贼王在洛圣都惨遭爆X

还有一些七七八八的MOD...我大一大二那会儿玩得不亦乐乎(= =#)。

另外,obj文件本身是ASCII格式,是可读的,所以就想研究研究。总之,OBJ模型有两个优点:文件格式被广泛支持;3D打印机只识别STL或OBJ格式。

然而,现在我都还没有完全实现这个加载器,可能是因为后来我知道了,世界上还有一种叫做“库”的东西存在~ 利用如VTK、Three.js等这些库,可以很轻易地实现obj模型的加载显示,因为作者已经写好了,用户直接调用接口就行了~不过理解OBJ模型文件内容还是有意义的。(此段划掉)(更新:加载器已有实现记录在下篇)

1 实体建模 vs. 表面建模

在说OBJ模型之前,先从大的写起,说说关于3D建模的分类。根据情况可以略过不看。

所有的3D cad软件从机制上来说其实可以分三类:实体建模(Solid Modeling),表面建模(Surface Modeling),和实体建模+表面建模。
即实际上建模方式只有两种:实体建模和表面建模。工程师用的软件偏向于实体建模,而艺术生用的软件偏向于表面建模。

实体建模:

实体建模的工具方式基本上有三种:扫掠,立体布尔运算和表面围成。
扫掠通常指的是将一个平面沿着一条指定曲线做扫掠操作,然后这个平面扫过的空间就成为了目标固体(所谓的平面拉伸,旋转,方样等都属于这个种类)。
布尔运算是对两个固体来求和,求差或者求交。这里指的固体运算其实是指空间的运算。通常计算机都用一种被称为边界限定法来描述一个空间,布尔运算的实质是对这个被限定住的空间做逻辑运算。求和,指的是不同立体空间相加后构成包含了两个空间的新空间。类似的,求差指的是用一段空间去剪切另外一段空间等。
表面围成就比较直接了,这种方法直接通过几个特殊的表面来生成固体,这些表面必须能够围住一个封闭的空间。

表面建模:

表面建模可以用一句话概括:添加或者修改点,让这些点能够约束一个表面。
表面建模只关注表面的外形,不关注这些表面是否能够围成一个封闭的空间。表面建模的整个过程都像做一个泥娃娃,你可以给它添加泥巴,也可以拿下一些泥巴,还可以捏这些泥巴。而maya这些软件就是给你提供各种工具来玩这个泥娃娃。同样布尔操作也可以用在表面建模中,但是形成的意义有所不同。实体建模中的布尔运算是真正的立体空间求和,求差,求交;而表面模型的布尔操作就像是做不同的表面缝合操作。(通常3D打印用的stl格式基本上就是表面模型描述文件)

本文介绍的OBJ模型属于表面建模模型。封面图赛车模型就是来自表面建模(3dsmax)。

2 网格模型

表面建模模型中,两个点组成一条边,多条边组成一个面,多个面组成一个体,一个或多个体组成一个模型。可以说一个模型是由若干个面组成的。

在计算机图形学中,考虑到图形求交等计算的问题,这个面通常选择三角形,即模型的表面由若干三角面构成。一个模型文件需要记录的信息就是这些三角面的信息,进一步讲,一个三角面又需要有顶点、法线、连接顺序等这些信息。

法线

顶点位置信息——x,y,z坐标值——描述了三角面的位置,但光有位置信息还不够,我们还需要知道一个三角面的正面与背面,为什么要这样,直观的理解就是,由于模型体只有表面,内部是空腔(如果模型是封闭的话),内部的面是不可见的,然后背面朝着我们(摄像机)的面也是不可见的。那怎么标注这个正面与背面呢?就是靠法线了。

(补充:一个简单的例子就是,比如我们有一个立方体模型,这个立方体表示一个魔方之类的物品,这时它可见的是外表面,但如果它表示一间房间,人在房间里面观察,那个此时它可见的应该是内表面。)

所谓面的法线就是垂直于此面的线,用一个向量表示,向量指向的一侧为正面,另一侧为反面。进一步地说,法线用处还是在光照模型中确定一个面的颜色,我之前的文章画个球啊(上)里的明暗处理模型一节提到了。

下图展示了一个无顶的正方体模型的渲染情况,左图是一个正常的正方体,外表面为正面,右图中的模型与左图相同,只是有一面的法线翻转到内侧,可以看到两个模型虽然形状一样,但渲染效果是不同的。

(P.S. 那能不能让我们看到同时看到正反面呢,答案是可以的,3dsmax和three.js里都可以设置一个”双面“参数,就是适用于这种模型不封闭有缺口,但正反面都想看的这种情况的)

OBJ网格模型文件(上) - 学习随笔_第2张图片
法线示意图:法线代表了物体的”正面“

3 OBJ文件格式

OBJ文件是一种被广泛使用的3D模型文件格式(obj为后缀名)。由Alias|Wavefront公司为3D建模和动画软件"Advanced Visualizer"开发的一种标准,适合用于3D软件模型之间的互导,也可以通过3dsmax、Maya等建模软件读写。

OBJ网格模型文件(上) - 学习随笔_第3张图片
例子:立方体模型

OBJ网格模型文件(上) - 学习随笔_第4张图片
立方体模型-纹理坐标示意图(14个点)

举例说明,画了个示意图。图上这个立方体的.obj文件内容大概就长下面的这个样子,相关解释已包含在注释中。

# "#"号开头是注释行
# v(vertex)数据段: 模型顶点列表
# 顶点位置信息,是xyz三维坐标
# v开头的每一行描述一个顶点,行数等于顶点数。8个顶点所以有8行
v  1.00  -1.00  -1.00
v  1.00  1.00  1.00
......
# vt(vertex texture)数据段:模型顶点的纹理坐标列表
# 顶点的纹理坐标信息,是xy二维坐标
# vt开头的每一行描述一个纹理坐标,行数大于等于顶点数,因为一个模型顶点在贴图的UV坐标系中很可能对应多个顶点/纹理坐标。且坐标值范围是在0~1之间,这个模型中有14行。
# 关于纹理坐标看图,本文不多解释纹理坐标,可参考文献[2]或自行百度
vt  0.74  0.75
vt  0.29  0.55
......
# vn(vertex normal)数据段:顶点法线列表
# 三维法向量,xyz
# vn开头的每一行描述一个法向量,行数大于等于顶点数。 前面介绍了,法线是与面相关的概念,但是现在的面是靠顶点来描述,拿示意图中的点"1"为例,它与参与构成了三个面,所以"顶点1"对应有3条法线
# 可能你已经发现了,在这个立方体模型中,共面顶点的法向量的方向是相同的,也就是说这里面的数据会重复,所以在建模软件导出obj文件时有个优化的选项,勾选后的导出的法线列表数据中就不会有重复项,这里的例子优化后有6条法线*
vn  -1.00 0.00 0.00 
vn  1.00 0.00 0.00
vn  0.00 1.00 0.00
......
# f(face):模型的三角面列表
# f开头的每一行描述一个面 ,关键的来了,三个点组成一个面,怎样拿到这三个点呢?通过从1开始的索引,去前面的v、vt、vn列表中去取。
# 总结一下就是:每一行定义1个面,1个面包含3个点,1个点具有“顶点/纹理坐标/法线”3个索引值,索引的是前面3个列表的信息。
f  1/1/1  2/2/1  3/3/1      # 顶点1、顶点2、顶点3 组成的面
f  2/2/1  3/3/1  4/4/1      # 顶点2、顶点3、顶点4 组成的面
f  1/1/1  5/10/1  8/14/6  # 顶点1、顶点5、顶点8 组成的面
......

以上便是obj 文件的核心部分,此外还有一些数据段,记录如下。

  • o 对象名
  • g 组名
  • s 平滑组
  • usemtl 材质名
  • mtllib 材质库.mtl

4 其他3D模型文件格式

通常,三维软件的输出格式分为三类。
第一类是商业内核级别的数据格式,如ACIS商业内核的sat数据格式(*.sat 扩展名为SAT的文件);ParaSolid内核的X_T数据格式(*.X_T)。
第二类是公共级别的数据格式,如Step的*.stp;IGES的*.igs(爱鸡屎,强烈建议不要使用!)
第三类就是专用级的数据格式,就是每个软件自己特有的数据文件格式,如SW的SLDPRT,PROE的PRT等。

网站[4]中由模型文件格式总结,很全。


写完了,不足之处,欢迎指点。


不错的参考:

[1] 3D立体建模的分类:实体建模和表面建模

[2] uv纹理坐标设定与贴图规则 - CSDN博客

[3] 三维软件的格式sat,stp_伟进_新浪博客

[4] Data Formats: 3D, Audio, Image

[5] 科学网-[转载]网格和单元的基本概念 - 安玖臻的博文

你可能感兴趣的:(学习)