標籤:

OpenGL使用glm類庫載入OBJ

GLM實現了用C語言對OBJ文件的常用操作,用於簡單的3D遊戲編程應該足夠了。3DS MAX,MAYA都可以把模型以OBJ文件格式導出。OBJ文件中包含模型的頂點,面,三角形,法向,紋理坐標等數據,但是其中不包含紋理和材質。材質可以放在OBJ文件中指定一個材質庫文件中,使用時材質庫文件一般和OBJ文件放在一起。紋理圖形就要自己想辦法了。

//模型數據結構:

struct GLMmodel{

char* pathname; //模型文件和材質庫的路徑

char* mtllibname; //材質庫名

GLuint numvertices; //頂點數

GLfloat* vertices; //儲存頂點的向量

GLuint numnormals; //模型法向數

GLfloat* normals; //儲存法向的向量

GLuint numtexcoords; //紋理坐標數

GLfloat* texcoords; //儲存紋理坐標的向量

GLuint numfacetnorms; //規則平面數

GLfloat* facetnorms; //儲存規則平面的向量

GLuint numtriangles; //三角形數

GLMtriangle* triangles; //儲存三角形的向量

GLuint nummaterials; //材質數

GLMmaterial* materials; //儲存材質的向量

GLuint numgroups;//圖元組數

GLMgroup* groups;//儲存圖元組的向量

GLfloat position[3];//模型的位置

了解這個模型數據結構主要用於自己擴充操作,以下是glm提供的相關操作:

//單位化模型model並返回縮放比例因子

單位化就是把模型通過平移和縮放變換限制到3維坐標系中點為中心的一個單位正方體區域內

GLfloat glmUnitize(GLMmodel* model);

//計算模型的寬,高,深尺寸,結果保存在dimensions所指的3元素數組中

GLvoid glmDimensions(GLMmodel* model, GLfloat* dimensions);

//按比例參數縮放模型,參數大於1放大,大於0小於1縮小,小於0反射,等於0縮小到0

GLvoid glmScale(GLMmodel* model, GLfloat scale);

//反轉模型的多邊形頂點順序,同時反轉法向量,默認多邊形頂點順序是逆時針的。

GLvoid glmReverseWinding(GLMmodel* model);

//計算模型面的法向(假定多邊形頂點順序為逆時針)

GLvoid glmFacetNormals(GLMmodel* model);

//計算模型的平滑頂點法向,angle參數為平滑交叉的最大角度(角度制)

GLvoid glmVertexNormals(GLMmodel* model, GLfloat angle);

//按線性投影產生紋理坐標,它把頂點線性映射到矩形上

GLvoid glmLinearTexture(GLMmodel* model);

//按球形映射產生紋理坐標

GLvoid glmSpheremapTexture(GLMmodel* model);

//從內存中釋放模型

GLvoid glmDelete(GLMmodel* model);

//從Wavefront公司標準的.OBJ文件中讀取模型

GLMmodel* glmReadOBJ(char* filename);

//將模型按Wavefront .OBJ文件格式寫入文件,文件名由filename參數指定

//mode指定寫入方式,此參數為取或(「|」)的位聯合:

//GLM_NONE - 只按頂點處理

//GLM_FLAT - 按面計演算法向

//GLM_SMOOTH - 按頂點計演算法向

//GLM_TEXTURE - 包含紋理座標

//GLM_COLOR - 只包含顏色信息(純色材質)

//GLM_MATERIAL - 包含材質信息

//其中GLM_FLAT和GLM_SMOOTH不能同時指定

//GLM_COLOR和GLM_MATERIAL也不能同時指定

GLvoid glmWriteOBJ(GLMmodel* model, char* filename, GLuint mode);

//按mode指定模式使用當前的OPENGL繪製上下文(context)繪製模型

//mode參數同glmReadOBJ中的mode參數

GLvoid glmDraw(GLMmodel* model, GLuint mode);

//由模型生成OPENGL顯示列表並返回顯示列表索引號

//其中mode參數同glmWriteOBJ和glmDraw中的mode參數

GLuint glmList(GLMmodel* model, GLuint mode);

//合併模型中差別很小的頂點,epsilon參數指定要合併頂點間的最大差距

//推薦epsilon參數0.00001為焊接模型的起點

GLvoid glmWeld(GLMmodel* model, GLfloat epsilon);

//讀取PPM格式的圖形文件

//返回一個24位色的圖象指針(可用於OPENGL紋理映射和圖象繪製函數)

//同時圖象尺寸存儲在width,height指針參數指定的地址中

glmReadPPM(char* filename, int* width, int* height);

2。glm.c的使用

下面我們學習如何使用glm.c。

首先把glm.c加入工程,並在主文件中

#include "glm.h"。

載入文件用如下代碼:

char* g_model_fn = "data\al.obj"; GLMmodel* g_model = NULL;

g_model = glmReadOBJ(g_model_fn); if (!g_model) exit(0); glmUnitize(g_model); glmScale(g_model,1.8); glmFacetNormals(g_model); glmVertexNormals(g_model, 90.0);

g_model_fn是文件名。

g_model是指向GLMmodel結構的指針。

glmReadOBJ載入一個OBJ文件,生成一個GL

Mmodel結構,並返回其指針。以後對這個

model的操作都需要這個指針作為標識。

glmUnitize把模型歸一化到一個(-0.5,-0.5,-0.5)-

(0.5,0.5,0.5)的盒子內。這樣模型的最大尺寸

是1,中心在原點。

glmScale把模型作伸縮變換(Al身高1米8)。

glmFacetNormals為模型的每個面(facet)生成法

向矢量。

glmVertexNormals為模型的每個頂點生成法向

矢量。頂點的法向矢量是對相鄰面的法向矢

量取平均值而得到的,因此需要預先調用

glmFacetNormals。第二個參數表示一個界限,

如果某個相鄰面與第一個相鄰面的夾角超過

此界限,那麼該面不參與平均。這樣可以保

留大的轉折而不致於讓整個模型都沒楞沒角。

顯示模型用如下代碼:

Vector3 his_position(0,0.9,-2);

glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glDisable(GL_TEXTURE_2D);

glTranslatef(his_position.x,his_position.y,his_position.z); glmDraw(g_model,GLM_SMOOTH | GLM_MATERIAL);

his_position 是記錄Al位置的全局變數。Al的

位置用他的重心坐標表示,因此y坐標是0.9

(y坐標豎直向上)。

首先用glTranslatef把局部坐標系設置到Al的位

置。

glmDraw顯示模型。第二個參數表示顯示方式。

GLM_SMOOTH表示光滑著色,GLM_MATERIAL

表示使用模型自帶的材料。此外還有其它一

些方式:GLM_NONE只顯示頂點,GLM_FLAT

使用非光滑的著色),GLM_TEXTURE

使用紋理坐標。

程序結束時還應刪除模型:

if(g_model) glmDelete(g_model);

glm還包括其它一些函數:

GLvoid glmDimensions(GLMmodel* model, GLfloat* dimensions); 得到模型的長寬高 。

GLvoid glmReverseWinding(GLMmodel* model); 顛倒多邊形的頂點順序 。

GLvoid glmLinearTexture(GLMmodel* model); 用投影到平面的方法生成紋理坐標 。

GLvoid glmSpheremapTexture(GLMmodel* model); 用投影到球面的方法生成紋理坐標 。

GLvoid glmWriteOBJ(GLMmodel* model, char* filename, GLuint mode); 把模型存成OBJ文件 。

GLuint glmList(GLMmodel* model, GLuint mode); 生成模型的display list 。

GLvoid glmWeld(GLMmodel* model, GLfloat epsilon); 合併距離小於epsilon的頂點 。

GLubyte* glmReadPPM(const char* filename, int* width, int* height); 載入一個PPM文件 。


推薦閱讀:

如何判定一點是否在給定頂點的不規則封閉區域內?
如何用OpenGL封裝一個2D引擎?
對多重採樣(MSAA)原理的一些疑問?
為什麼 NVIDIA 的示例總是一條龍?
Shader 在現在圖形管線中可以負責多少部分?

TAG:OpenGL |