如何將外部的obj模型導入OpenGL
1.關於obj的說明。
obj中存放的是頂點坐標信息(v),面的信息(f),法線(vn),紋理坐標(vt),以及材質(這個放在mtl)中
我使用CINEMA 4D導出用VS查看後的信息:
CINEMA 4D中的正方體:
導出obj後的信息
VS中查看;未標題4.obj
# WaveFront *.obj file (generated by CINEMA 4D) mtllib ./未標題4.mtl v -100 -100 100 v -100 100 100 v 100 -100 100 v 100 100 100 v 100 -100 -100 v 100 100 -100 v -100 -100 -100 v -100 100 -100 # 8 vertices vn 0 0 1 vn 1 0 0 vn 0 0 -1 vn -1 0 0 vn 0 1 0 vn 0 -1 0 # 6 normals vt 0 0 0 vt 0 1 0 vt 1 1 0 vt 1 0 0 # 4 texture coordinates o 立方體 usemtl default f 3/4/1 4/3/1 2/2/1 1/1/1 f 5/4/2 6/3/2 4/2/2 3/1/2 f 7/4/3 8/3/3 6/2/3 5/1/3 f 1/4/4 2/3/4 8/2/4 7/1/4 f 4/4/5 6/3/5 8/2/5 2/1/5 f 5/4/6 3/3/6 1/2/6 7/1/6
未標題4.mtl
# WaveFront *.mtl file (generated by CINEMA 4D) newmtl default Kd 1 1 1
這就是其中的信息。
我們知道OpenGL中建立不規則模型可以通過點和三角面來創建。那麽我們要做的就是從obj中導出點的三維信息和面的三維信息。
2、obj中模型導出的設置。
首先在三維軟件中創建一個模型。這裏我以一個人體模型演示(只以線框顯示)。
模型分析:
·我們可以看到這個模型的點面信息太多了,如果直接導出數據太多,處理時程序讀取花費的時間太長,而且我們演示也不需要這種高模。
·還有就是,我們知道OpenGl中的運行後可視化界面的坐標是重-1到1,這裏可以看到這個模型的尺寸太大,我們必須進行縮放。
模型不是一個整體,分層太多,導出後各個模塊的數據是分開的。
模型處理後:
我感覺這樣就滿足我的要求,後期運行程序讀取數據不會超過4分鐘
模型導出:註意一下以下地方就好,因為這裏只要點(v),面(f)其他會幹擾,也會使程序效率下降。導出後只有以下一個文件。
3.數據處理
將那個文件用VS打開後,可以看到裏面的數據,這裏我們要處理一下:
VS雖然可以直接打開obj但是從這個裏面是無法讀取數據的。
所以我們要新建一個txt文件,將裏面的數據復制進來,然後把這個文件放在你新建的win32或MFC項目中,將先前的obj從工程中移除。
在VS中查找“#”開頭的,有以下兩句話,然後直接刪除這個是我用CINEMA 4D導出後自帶的,會幹擾程序的運行。
4.編程
分析:
¨分析這些數據,不難猜想obj文件由若幹行組成,每行開始有一個字母,用來標註改行數據所表示的意思,v 應該是頂點,g應該是一個名字,觀察到,f後緊跟著三個整數,由opengl中的頂點數組得到,他應該是三角行的三個頂點,後面的三個整數就代表著前端頂點的索引值.
¨好知道了數據結構,就可以提取分析obj文件啦class obj3dmodel{ struct vertex{ double x,y,z; }; struct face{ unsigned int v1,v2,v3; }; vector<vertex>vertexs; vector<face>faces; public : void parse(const char *filename); void draw(); };
這裏,我們定義兩個結構體來表示頂點和三角形,並采用vector容器來保存數據組.
¨之所以把兩個結構體定義在類的內部,一個是不想汙染源代碼,另一個是暫時還沒有想到會在別的什麽地方會用到他們.所以暫且把他們藏起來
¨我們同時為他定義了parse和draw方法,分別用來從文件中解析數據和利用opengl繪制
void obj3dmodel::parse(const char *filename){ string s; ifstream fin(filename); if(!fin)return; while(fin>>s){ switch(*s.c_str()){ case ‘f‘: { face f; fin>>f.v1>>f.v2>>f.v3; cout<<"f "<<f.v1<<" "<<f.v2 <<" "<<f.v3<<endl; faces.push_back(f); } break; case ‘v‘: { vertex v; fin>>v.x>>v.y>>v.z; cout<<"v "<<v.x<<" "<<v.y<<" "<<v.z<<" "<<endl; this->vertexs.push_back(v); } break; case ‘w‘:break; case ‘x‘:break; case ‘y‘:break; case ‘z‘:break; case ‘#‘:break; default: {} break; } } } void obj3dmodel::draw(){ glBegin(GL_TRIANGLES); for(int i=0;i<faces.size();i++){ //下標減一 float move_y=0.5; //控制模型的位置 vertex v1=vertexs[faces[i].v1-1]; vertex v2=vertexs[faces[i].v2-1]; vertex v3=vertexs[faces[i].v3-1]; glColor3f(0.3,0.5,0); glVertex3f(v1.x,v1.y-move_y,v1.z); glColor3f(1,1,0); glVertex3f(v2.x,v2.y-move_y,v2.z); glColor3f(0.5,0.5,0); glVertex3f(v3.x,v3.y-move_y,v3.z); } glEnd(); }註意頂點索引方式,obj裏面是以1為基數的,所以在索引時要將索引值減1.則會報越界. 運行程序: 5.運行結果:
·
低模 高模
源碼提供:
// 1.cpp : 定義控制臺應用程序的入口點。 // #include "stdafx.h" #include <GL/glut.h> #include<vector>//vector(向量): C++中的一種數據結構,確切的說是一個類.它相當於一個動態的數組,當程序員無法知道自己需要的數組的規模多大時,用其來解決問題可以達到最大節約空間的目的. #include <fstream> #include <string> #include <iostream> using namespace std; GLfloat step=0.0,s=0.1,move[]={0,0,0}; class obj3dmodel{ struct vertex{ double x,y,z; }; struct face{ unsigned int v1,v2,v3; }; vector<vertex>vertexs; vector<face>faces; public : void parse(const char *filename); void draw(); }; void obj3dmodel::parse(const char *filename){ string s; ifstream fin(filename); if(!fin)return; while(fin>>s){ switch(*s.c_str()){ case ‘f‘: { face f; fin>>f.v1>>f.v2>>f.v3; cout<<"f "<<f.v1<<" "<<f.v2 <<" "<<f.v3<<endl; faces.push_back(f); } break; case ‘v‘: { vertex v; fin>>v.x>>v.y>>v.z; cout<<"v "<<v.x<<" "<<v.y<<" "<<v.z<<" "<<endl; this->vertexs.push_back(v); } break; case ‘w‘:break; case ‘x‘:break; case ‘y‘:break; case ‘z‘:break; case ‘#‘:break; default: {} break; } } } void obj3dmodel::draw(){ glBegin(GL_TRIANGLES); for(int i=0;i<faces.size();i++){ //下標減一 float move_y=0.5; vertex v1=vertexs[faces[i].v1-1]; vertex v2=vertexs[faces[i].v2-1]; vertex v3=vertexs[faces[i].v3-1]; glColor3f(0.3,0.3,0); glVertex3f(v1.x,v1.y-move_y,v1.z); glColor3f(1,1,0); glVertex3f(v2.x,v2.y-move_y,v2.z); glColor3f(0.5,0.5,0); glVertex3f(v3.x,v3.y-move_y,v3.z); } glEnd(); } obj3dmodel obj; void myinit(){ GLfloat light_ambient[]={0.3,0.2,0.5}; GLfloat light_diffuse[]={1,1,1}; GLfloat light_position[]={2,2,2,1}; GLfloat light1_ambient[]={0.3,0.3,0.2}; GLfloat light1_diffuse[]={1,1,1}; GLfloat light1_position[]={-2,-2,-2,1}; //燈光 glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient); glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse); glLightfv(GL_LIGHT0,GL_POSITION,light_position); glLightfv(GL_LIGHT1,GL_AMBIENT,light1_ambient); glLightfv(GL_LIGHT1,GL_DIFFUSE,light1_diffuse); glLightfv(GL_LIGHT1,GL_POSITION,light1_position); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); //深度 glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); //材質 glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); glEnable(GL_COLOR_MATERIAL); } void DrawColorBox(){ obj.draw(); //glutSolidTeapot(1); } void display(){ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); /*s+=0.005; if(s>1.0)s=0.1;*/ //不進行縮放 //位移 //move[0]+=0.005; //x //move[1]+=0.005; //y //move[2]+=0.005; //z //if(move[0]>2)move[0]=0; //if(move[1]>2)move[1]=0; //if(move[2]>2)move[2]=0; glPushMatrix(); //glScalef(s,s,s); //glTranslatef(move[0],move[1],move[2]); glRotatef(step,0,1,0); //glRotatef(step,0,0,1); //glRotatef(step,1,0,0); DrawColorBox(); glPopMatrix(); glFlush(); glutSwapBuffers(); } void stepDisplay(){ //旋轉 step=step+1; if(step>360)step=0; display(); } void myReshape(GLsizei w,GLsizei h){ glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h){ glOrtho(-1.5,1.5,-1.5*(GLfloat)h/(GLfloat)w,1.5*(GLfloat)h/(GLfloat)w,-10,10); }else{ glOrtho(-1.5*(GLfloat)w/(GLfloat)h,1.5*(GLfloat)w/(GLfloat)h,-1.5,1.5,-10,10); } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void keyboard (unsigned char key,int x,int y){ switch(key){ case 27:exit(0);break; } } int _tmain(int argc, CHAR* argv[]) { obj.parse("66.txt"); //obj.parse("11.txt"); glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB); glutInitWindowSize(500,400); glutCreateWindow("simple"); myinit(); glutReshapeFunc(myReshape); glutDisplayFunc(display); glutKeyboardFunc(keyboard); glutIdleFunc(stepDisplay); glutMainLoop(); return 0; }
如何將外部的obj模型導入OpenGL