[iTyran原創]iPhone中OpenGL ES顯示3DS MAX模型之二:lib3ds加載模型

[iTyran原創]iPhone中OpenGL ES顯示3DS MAX模型之二:lib3ds加載模型

作者:u0u0 - 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裏面來,這樣效率高內存佔用也小。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章