文件后缀名obju为什么打不开_obj文件基本结构及读取 - 计算机图形学

引言:      最近开始用DirectX 10了,感觉和Dx9还是有一些变化的。虽然还不能完全理解这些变化所带来的优势,但是基本还是适应了。      唯一觉得不适应的就是在Dx10的接口里面,D3DX库里面已经没有对于.x模型文件的直接支持了。就是说不能通过D3DX来读取.x模型文件了,Dx10提供了一种新的模型格式sdkmesh。但是为了读取这种模型,或者自己写解析器,可能会很复杂,或者用DXUT的接口,但是如果仅仅为了读取模型文件就用了这个庞大的接口多少显得有些累赘。所以我自己也弄了一个读取Obj格式的模型文件的Demo,对于想读取自己自定义格式的模型文件的朋友,可以看看。希望能有一点帮助。

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

为了简单起见,这里分析的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 32newmtl 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进行渲染的时候强制了一些渲染状态,个人比较推荐后者的解决方案,因为无论是编码还是运行起来,后面的效率要更高一些,而且更有针对性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值