技术笔记:obj文件基本结构及读取

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 技术笔记:obj文件基本结构及读取

读取Obj格式的模型文件(Dx10) - 【Tutorial】


引言:


最近开始用DirectX 10了,感觉和Dx9还是有一些变化的。虽然还不能完全理解这些变化所带来的优势,但是基本还是适应了。


唯一觉得不适应的就是在Dx10的接口里面,D3DX库里面已经没有对于.x模型文件的直接支持了。就是说不能通过D3DX来读取.x模型文件了,Dx10提供了一种新的模型格式sdkmesh。但是为了读取这种模型,或者自己写解析器,可能会很复杂,或者用DXUT的接口,但是如果仅仅为了读取模型文件就用了这个庞大的接口多少显得有些累赘。所以我自己也弄了一个读取Obj格式的模型文件的Demo,对于想读取自己自定义格式的模型文件的朋友,可以看看。希望能有一点帮助。


正文:


Obj是一种应用广泛的模型格式。在很多三维建模软件里面,都可以找到Obj格式的导出插件。本文简单解析了Obj的最基本的格式,可以读取由三角形组成的模型。


为了简单起见,这里分析的Obj是ASCII码格式的,这样用户可以用wordpad查看obj文件里面的内容,配合程序的调试,可以更好的理解文件解析的过程。其实这个模型格式可以很简单的描述一个Mesh。下面我们简单看看一些关键字:


# 这个就相当于C++代码里面的//,如果一行开始时#,那么就可以理解为这一行完全是注释,解析的时候可以无视


g 这个应该是geometry的缩写,代表一个网格,后面的是网格的名字。


v v是Vertex的缩写,很简单,代表一个顶点的局部坐标系中的坐标,可以有三个到四个分量。我之分析了三个分量,因为对于正常的三角形的网格来说,第四个分量是1,可以作为默认情况忽略。如果不是1,那可能这个顶点是自由曲面的参数顶点,这个我们这里就不分析了,因为大部分的程序都是用三角形的。


vn 这个是Vertex Normal,就是代表法线,这些向量都是单位的,我们可以默认为生成这个obj文件的软件帮我们做了单位化。


vt 这个是Vertex Texture Coordinate,就是纹理坐标了,一般是两个,当然也可能是一个或者三个,这里我之分析两个的情况。


mtllib 这个代表后面的名字是一个材质描述文件的名字,可以根据后面的名字去找相应的文件然后解析材质。


usemtl 这里是说应用名字为matName的材质,后面所有描述的面都是用这个材质,直到下一个usemtl。


f 这里就是face了,真正描述面的关键字。后面会跟一些索引。一般索引的数量是三个,也可能是四个(OpenGL里面可以直接渲染四边形,Dx的话只能分成两个三角形来渲染了)。每个索引数据中可能会有顶点索引,法线索引,纹理坐标索引,以/分隔。


上面就是描述一个简单的Obj模型文件的基本内容了,下面贴上一个例子


#


# Wavefront OBJ file


# Converted by the DEEP Exploration 2.1.12.1218


# Right Hemisphere, LTD


#


mtllib cup.mtl


g insideShape


v -0.395825 0.436485 9.94025e-009


v 0.395911 0.436485 0.0264705


v -0.0391825 0.436485 0.394319


v -0.414339 0.479981 1.25328e-008


v -0.0410357 0.479981 0.41274


v -0.325067 0.479981 0.257087


v -0.23434 -0.406736 -4.03196e-008


v -0.225996 -0.406736 0.0620398


v -0.232244 -0.406736 0.0312992


v -0.116927 -0.406736 -4.03196e-008


…………………………


vt 1 0.978723


vt 0.941176 0.978723


vt 1 1


vt 1 0.468085


vt 0.941176 0.468085


vt 1 0.489362


vt 1 0.212766


vt 0.941176 0.212766


vt 1 0.234043


vt 0.470588 0.212766


vt 0.411765 0.212766


…………………………


vn 0.303793 -0.951539 0.0477825


vn 0.1201 -0.992691 0.0118188


vn 0.307451 -0.951539 0.00686204


vn -0.299922 -0.95154 -0.0679646


vn -0.119043 -0.992691 -0.0198105


vn -0.306307 -0.951539 -0.027376


vn 0.077944 -0.951538 -0.297489


vn 0.0237771 -0.992691 -0.118317


vn 0.037599 -0.951538 -0.305224


vn -0.0473055 0.97514 0.21648


vn 0.0390879 0.988496 -0.146111


vn -0.0180342 0.975141 0.220851


vn -0.188498 0.97514 0.116491


…………………………


usemtl inside


f 1/3/3 800/2/2 799/1/1


f 2/6/6 407/5/5 406/4/4


f 3/9/9 204/8/8 203/7/7


f 5/12/12 98/11/11 97/10/10


f 6/15/15 51/14/14 50/13/13


f 8/18/18 11/17/17 9/16/16


………………………………


基本上就是这些部分就可以描述一个静态的模型了,其实有很多数据,这里用省略号略过了。下面看下mtl文件,就是材质文件。这个比较简单清晰,先贴上一个好了:


#


# Wavefront material file


# Converted by the DEEP Exploration 2.1.12.1218


# Right Hemisphere, LTD


#


newmtl inside


Ka 0.4 0.4 0.4


Kd 0.587609 0.587609 0.587609


Ks 0.071744 0.071744 0.071744


Ns 32


newmtl outside


Ka 0 0 0


Kd 1 1 1


Ks 0.384296 0.194061 0.174387


Ns 64


map_Kd cup.jpg


前面也是一些注释,简单介绍下关键字这些东西就很好看了:


newmtl:New Material,没啥好说的。


Ka:Ambient Color


Kd:Diffuser Color


Ks:Specular Color


Ns:Shininess


map_Kd:Diffuse的纹理贴图。


其实不介绍上面的东西也很直接了。那么一个最简单的Obj格式的文件基本就是这么多东西了,虽然内容很简单,很少,但是这种格式足够描述任意多的静态多边形,写一些简单的程序也足够了。


下面的内容就和Obj格式没什么关系了,是这个Demo的另一部分,简单的介绍下怎样把一些自定义的数据弄到Dx10的ID3DX10Mesh里面,以及一些Dx10的注意事项。


在上面的解析中,我们单纯的把所有的v,vt,vn分别push到三个vector里面。然后利用索引分别拷贝相应的数据就好了,这里面我们需要三个缓冲:顶点缓冲,其中每个顶点包括了位置,法线,纹理坐标等属性;索引缓冲,描述每个三角形的索引,这个索引是指向上面的顶点缓冲区中的;Attribute缓冲,这里面应该是描述一些顶点的材质属性的。根据解析出的Obj的数据,生成这三个缓冲应该没什么大问题。但是这里有一点需要注意下,如果我们单纯对于每个Obj里面的索引去拷贝数据的话,可能会有一些顶点在顶点缓冲中重复。例如,两个三角形公用一个顶点,那么简单的加入数据,共享的顶点就会被加入到顶点缓冲两次。一个简单的办法是用hash表来查找要加入的顶点是否存在于现有的顶点缓冲中(这部分工作我的Demo没有做)。


有了上述三个缓冲,可以利用ID3DX10Mesh的三个接口:SetVertexData , SetIndexData , SetAttributeData 把数据Fill到接口中,在数据写入后,要调用一次CommitDevice,否则是无法渲染的。完成了这些工作,对于写入的Mesh就可以正常对待了。模型文件也可以正常渲染了。当然我们还要自己写Shader来渲染,但是实现一个简单的Phong光照模型并没什么难度,所以这里也不介绍了。


最后,有一点需要特殊注意的。用Dx10渲染一些文字的时候,我的Demo里面用的是ID3DX10FONT,但是在它的DrawText接口被调用后,它会改变ZCompareFunction,会导致下一帧的三维图形渲染的错误。所以一定要在DrawText之后回复状态。可以通过device->SetDepthStencilState( NULL , 0 ) 恢复到默认状态。也可以在Shader里面加一些参数,强制在渲染模型的时候切换渲染状态。例如:


//depth state


DepthStencilState DepthState


{


DepthEnable = TRUE;


DepthWriteMask = ALL;


};


//raster stae


RasterizerState RasterState


{


CullMode = NONE;


};


//the technique


technique10 DefaultTec


{


pass DefaultPass


{


SetVertexShader( CompileShader( vs_4_0 , DefaultVertexShader() ) );


SetGeometryShader( NULL );


SetPixelShader( CompileShader( ps_4_0 , DefaultPixelShader() ) );


//enable depth


SetDepthStencilState( DepthState, 0 );


//Disable culling


SetRasterizerState( RasterState );


}


}


上面的代码是我的Demo里面的一部分,在这个Shader进行渲染的时候强制了一些渲染状态,个人比较推荐后者的解决方案,因为无论是编码还是运行起来,后面的效率要更高一些,而且更有针对性。


好了,这次的东西其实就是体力活而已,没什么想法的东西,只要注意一些细节就可以搞出来了,代码在下面:


开发公司:Alias|Wavefront公司


典型应用:


软件:


(1)Advanced Visualizer(Wavefront)


(2)Poser


其他应用:


所有dcc/cad/cam都支持该格式


问题提出:


3D软件模型之间的互导时出现的错误,比如如果Maya自身的模型出错,也可以先转成OBJ格式,修改之后


再导回Maya。


################----OBJ文件 -- 概念----#######################


OBJ文件有2种基本格式:


ASCII格式(.obj)。


binary格式(.mod),该专利尚未公开.


文档版本:v2.11\v3.0。


#############----OBJ文件 -- 特点----#######//代码效果参考:http://www.jhylw.com.cn/070337823.html

################

(1)OBJ是一种3D模型文件,支持法线和贴图坐标,但是不包含动画、动力学、粒子等信息。


(2)OBJ3.0格式支持多边形(Polygon),直线(Lines),表面(Surfaces),和5种自由形态曲线(Free-form


Curves)。包括那些基于Bezier\B-spline\Cardinal\Taylor equations曲线。


注意:


各三维软件由于多方面原因,对obj格式的容纳能力不尽相同,比如Maya导出的OBJ文件只支持多边形。


(3)OBJ文件支持三角面。


很多其它的模型文件格式只支持三角面,所以我们导入Maya的模型经常被三角化了,这对于我们对模型


的再加工甚为不利。


################----基本结构 File structure----#####################


Syntax


Specifying


顶点数据(Vertex data)


v(v x y z w)


Geometric vertices


vt(u v w)


Texture vertices


vn(i j k)


Vertex normals


vp(u v w)


curve/surface attributes (Parameter space vertices)


自由形态曲线(Free-form curve)/表面属性(surface attributes)


deg(degu degv)


Degree


bmat(u matrix)


(v matrix)


Basis matrix


step(stepu stepv)


Step size


cstype


Curve or surface type


元素(Elements)


p(v1 v2 v3 ……)


-Point


l //代码效果参考:http://www.jhylw.com.cn/411135209.html

(v1/vt1 v2/vt2 v3/vt3 ……)

-Line


f (v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ……)


Face


curv(u0 u1 v1 v2 ……)


Curve


curv2 (vp1 vp2 vp3……)


2D curve


surf(s0 s1 t0 t1 v1/vt1/vn1 v2/vt2/vn2……)


Surface


自由形态曲线(Free-form curve)/表面主体陈述(surface body statements)


parm (u p1 p2 p3……) (v p1 p2 p3……)


Parameter values


trim(u0 u1 curv2d u0 u1 curv2d ……)


Outer trimming loop


hole(u0 u1 curv2d u0 u1 curv2d …… )


Inner trimming loop


scrv(u0 u1 curv2d u0 u1 curv2d……)


<td valign="top" width="31%" style="margin: 0; padding: 0; font-size: 12px; font-family: Verdana, 宋体, san

相关文章
|
6月前
|
存储 自然语言处理
QT案例词典 -- 存储内容及遍历
QT案例词典 -- 存储内容及遍历
48 1
使用FILE结构操作文本文件
使用FILE结构操作文本文件
108 0
|
JavaScript 前端开发
javascript实现对象数据键值不同的Object.keys循环读取解决方案
javascript实现对象数据键值不同的Object.keys循环读取解决方案
84 0
javascript实现对象数据键值不同的Object.keys循环读取解决方案
|
存储 数据挖掘 数据库
data的含义与作用及使用方法
data的含义与作用及使用方法
6408 0
|
JavaScript 前端开发 API
for 循环不是目的,map 映射更有意义!【FP探究】
在 JavaScript 中,由于 Function 本质也是对象(这与 Haskell 中【函数的本质是值】思路一致),所以我们可以把 Function 作为参数来进行传递!
|
开发者 Python Windows
文件的读取方式 | 学习笔记
快速学习 文件的读取方式
文件的读取方式 | 学习笔记
有关使用Map结构替换掉复杂的if-else结构【项目使用】
有关使用Map结构替换掉复杂的if-else结构【项目使用】
218 0
读取文件结束的判定的概念,使用方法和文件缓冲区的位置
读取文件结束的判定的概念,使用方法和文件缓冲区的位置
135 0
|
Java
编写Java程序,在硬盘中选取一个 txt 文件,读取该文档的内容后,追加一段文字“[ 来自新华社 ]”,保存到一个新的 txt 文件内
编写Java程序,在硬盘中选取一个 txt 文件,读取该文档的内容后,追加一段文字“[ 来自新华社 ]”,保存到一个新的 txt 文件内
286 0
编写Java程序,在硬盘中选取一个 txt 文件,读取该文档的内容后,追加一段文字“[ 来自新华社 ]”,保存到一个新的 txt 文件内
|
Python
如何读取保存一些list信息的txt,生成有一组名字有规律的list
如何读取保存一些list信息的txt,生成有一组名字有规律的list
180 0
如何读取保存一些list信息的txt,生成有一组名字有规律的list