zoukankan      html  css  js  c++  java
  • [转载]iPhone中OpenGL ES显示3DS MAX模型之二:lib3ds加载模型

    上一节中,我们分析了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里面来,这样效率高内存占用也小。

  • 相关阅读:
    Asp.Net Web API 2第八课——Web API 2中的属性路由
    Asp.Net Web API 2第七课——Web API异常处理
    Asp.Net Web API 2第六课——Web API路由和动作选择
    Asp.Net Web API 2第五课——Web API路由
    开始学习python
    BMI 小程序 购物车
    深浅copy 文件操作
    字典 dict 集合set
    基本数据类型 (str,int,bool,tuple,)
    python 运算符
  • 原文地址:https://www.cnblogs.com/PursuitOnly/p/2908889.html
Copyright © 2011-2022 走看看