泰然

 找回密码
 注册泰然

[iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之二:lib3ds加载模型

2011-12-18 10:33| 发布者: u0u0| 查看: 13349| 评论: 40|原作者: u0u0

摘要: iPhone中OpenGL ES显示3DS MAX模型,lib3ds加载模型,OpenGL ES 2.0

[iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之二:lib3ds加载模型

作者:u0u0 - iTyran
版权声明:iTyran原创作品,谢绝转载!否则将追究法律责任。

上一节中,我们分析了OBJ格式。
OBJ格式优点是文本形式,可读性好,缺点也很明显,计算机解析文本过程会比解析二进制文件慢很多。OBJ还有个问题是各种3D建模工具导出的布局格式还不太一样,face还有多边形(超过三边形),不利于在OpenGL ES里面加载。

.3ds文件是OBJ的二进制形式,并且多很多信息。有一个C语言写的开源库可以用来加.3ds文件,这就是lib3ds。GPL的license,直接使用在商业程序里面可能不太理想。这不妨碍我们这篇文章的分析。

教程截图:

  


demo继承于我的另一篇教程:Xcode创建的默认iOS OpenGL ES 2.0 project代码分析
的OpenGLStart,去掉了GLKit着色器代码,添加lib3ds并加载显示。

工程下载地址:http://ityran.com/thread-765-1-1.html

原理
我使用Google SketchUp建了一个简单的三角锥和长方体,分别导出为triangle.3ds和square.3ds。要把模型在OpenGL ES里面显示出来,我们需要模型的两个基本信息:顶点坐标和顶点法线。
.3ds文件保存有顶点坐标,和face序列,而顶点法线需求自行计算。
lib3ds提供了接口读取顶点坐标和face序列,并提供接口计算顶点法线。
我们要做的就是把这些数据读取出来,转换成OpenGL ES接受的数据形式。

TT3ds加载类
为了方便加载模型,我lib3ds的接口封装在一个类里面。
这个类负责加载解析.3ds,并转换为OpenGL ES接受的数据。
注意:一个.3ds里面可能包含多个模型,TT3ds只能处理一个文件一个模型。

先来看TT3ds.h文件。
#import 
#import 
#import "lib3ds.h"
@interface TT3ds : NSObject {          char*name;//模型名称                  GLuintnvertices;//顶点数          GLsizeiptrverticesSize;//顶点数组大小          GLfloat*vertices;//顶点数组指针                    GLuintnfaces;//面数          GLsizeiptrfacesSize;//面数据大小          GLubyte*faces;//面数据指针                    //VBO相关数据定义,OpenGL ES 接受数据          GLuintvertexArray;          GLuintvertexArrayBuffer;          GLuintvertexElementBuffer;                    GLenumerror; } - (id)initWithFilename:(NSString *) fileName; - (void)bindVertexArray; - (void)draw;
@end

三个接口,initWithFilename很显然是用来初始化的,用法如下:
testObject = [[TT3ds alloc]initWithFilename:@"triangle"];
参数是triangle而不是triangle.3ds,ofType指定了只能识别3ds文件。
NSString *dsPathname = [[NSBundle mainBundle]pathForResource:fileName ofType:@"3ds"];

后面2个接口是给OpenGL ES框架的
- (void)glkView:(GLKView *)viewdrawInRect:(CGRect)rect
调用的。

lib3ds的文档相当少,只有代码里面的自带demo可以参考一下。
整个流程为参考了3ds2obj这个demo的代码。
lib3ds_file_open返回的Lib3dsFile包含一个node系列。
查找序列里面属性为LIB3DS_NODE_MESH_INSTANCE的node,
一个LIB3DS_NODE_MESH_INSTANCE node是一个模型。
一个.3ds可包含多个模型。

parseMeshNode函数把3ds数据转换为VBO数据。
VBO数据组织如下图所示:



对于的VBO代码如下:
glGenVertexArraysOES(1, &vertexArray);
glBindVertexArrayOES(vertexArray);
glGenBuffers(1, &vertexArrayBuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexArrayBuffer); glBufferData(GL_ARRAY_BUFFER, verticesSize, vertices,GL_STATIC_DRAW); glEnableVertexAttribArray(GLKVertexAttribPosition); glVertexAttribPointer(GLKVertexAttribPosition, 3,GL_FLOAT, GL_FALSE,
sizeof(GLfloat) * 3, BUFFER_OFFSET(0)); glEnableVertexAttribArray(GLKVertexAttribNormal); glVertexAttribPointer(GLKVertexAttribNormal, 3,GL_FLOAT, GL_FALSE,               sizeof(GLfloat)* 3, BUFFER_OFFSET(verticesSize / 2));
glGenBuffers(1, &vertexElementBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,vertexElementBuffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, facesSize,faces, GL_STATIC_DRAW); glBindVertexArrayOES(0);

bindVertexArray
在调用glDrawElements来显示模型前,我们需要告诉OpenGL使用那个数据里显示。
bindVertexArray是TT3ds的一个方法,在TTViewController.m中被调用,封装的目前是避免返回一个vertexArray指针给外部。
- (void)bindVertexArray {
         glBindVertexArrayOES(vertexArray);
}
draw
由于.3ds文件提供的是face序列,这里就需要用到glDrawElements来显示模型,而不是glDrawArrays。
我们已经将element信息通过VBO设置好,TT3ds的draw方法如下:
- (void)draw {
         glDrawElements(GL_TRIANGLES,facesSize, GL_UNSIGNED_BYTE, 0);
}
glDrawElements第4个参数设置0,而不是faces,这里涉及glDrawElements的两种用法:一种是直接在这里设置为faces指针;一种是在VBO里面提前设置好element信息,glDrawElements的时候把第4个参数设置为0。

TTViewController.m中的加载显示模型
第一步在setupGL中初始化模型
glEnable(GL_DEPTH_TEST);
testObject = [[TT3ds alloc]initWithFilename:@"triangle"];
然后修改draw相关代码,红色部分。
- (void)glkView:(GLKView *)viewdrawInRect:(CGRect)rect
{
   glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //glBindVertexArrayOES(_vertexArray);
    [testObject bindVertexArray];
    
    // Renderthe object again with ES2
   glUseProgram(_program);

   glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0,
_modelViewProjectionMatrix.m);    glUniformMatrix3fv(uniforms[UNIFORM_NORMAL_MATRIX], 1, 0,_normalMatrix.m);         //glDrawElements(GL_TRIANGLES,sizeof(gFaces) / sizeof(GLubyte), GL_UNSIGNED_BYTE, 0);     [testObject draw]; }
运行下工程试试吧,你将看到文章开头的截图。

结束语:
TT3ds功能并不完善,没处理多模型的情况,没优化数据,没异常处理,顶点法线的计算的正确性我不敢保证,正方体的显示颜色上有点不对。
我认为直接使用lib3ds在项目中可能不是很理想,.3ds的数据并没有优化过,太多重复点数据。最理想的方法是:居于lib3ds写一个PC端工具,转换.3ds为另一种优化好的数据格式,再把优化后的数据直接放到OpenGL里面来,这样效率高内存占用也小。

13

鲜花

握手

雷人

路过
1

鸡蛋

刚表态过的朋友 (14 人)

发表评论

最新评论

引用 gz_sun 2013-5-9 14:48
好帖子啊,希望版主多发点源码
引用 gz_sun 2013-5-9 14:48
谢谢分享
引用 L.zq 2013-1-5 14:07
学习 学习~谢谢分享
引用 ssff169 2013-1-2 12:55
这个真心不错呀
引用 tinyKing 2012-12-7 11:39
正在求这个,实在是救命啊
引用 riseliang 2012-10-16 12:52
怎么我导出的3ds不行?
引用 chen41345507 2012-7-23 11:43
mark,等有币了下载
引用 qinwenzhou 2012-6-19 10:28
很好,谢谢
引用 scottboy 2012-5-26 16:54
这个真的写得很好!!
引用 suxk 2012-5-3 22:30
好帖子啊,希望版主多发点源码
引用 chenaza 2012-4-1 11:44
得好好学学
引用 yuzhouwww 2012-3-9 10:39
感谢楼主,我想问一下,lib3ds对多边形数量有限制嘛?
引用 stonexing5 2012-3-8 14:05
xia zai bu liao
引用 kehaoran74 2012-3-7 20:21
手脚拉啊
引用 kehaoran74 2012-3-7 20:21
好东西啊
引用 dreamable 2012-3-7 14:39
学习啦!
引用 jpren 2012-3-6 11:49
讲的很不错
引用 ale423 2012-2-22 23:35
mark了
引用 ale423 2012-2-22 23:35
高手
引用 ale423 2012-2-22 23:35
学习

查看全部评论(40)

手机版|小黑屋|Archiver| 泰然论坛 ( 蜀ICP备13018980号-2 )

GMT+8, 2017-8-21 14:36 , Processed in 0.010450 second(s), 13 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

返回顶部