在經過查閱各種資料以及各種bug之后,終於成功的實現了導入基本的obj模型。
首相介紹一下什么是obj模型
一.什么是OBJ模型
obj文件實際上是一個文本文檔,主要有以下數據,一般可以通過blender軟件導出模型的obj文件。
在3d圖形處理中,一個模型(model)通常由一個或者多個Mesh(網格)組成,一個Mesh是可繪制的獨立實體。例如復雜的人物模型,可以分別划分為頭部,四肢等各個部分來建模,這些Mesh組合在一起最終形成人物模型。
obj的文本內容一般包括以下數據
usemtl和mtllib表示的材質相關數據,解析材質數據稍微繁瑣,本節我們只是為了說明加載模型的原理,不做討論。
o 引入一個新的object
v 表示頂點位置
vt 表示頂點紋理坐標
vn 表示頂點法向量
f 表示一個面,面使用1/2/8這樣格式,表示頂點位置/紋理坐標/法向量的索引,這里索引的是前面用v,vt,vn定義的數據 注意這里Obj的索引是從1開始的,而不是0
二 利用opengl導入obj模型
新建一個ObjLoader
類
1 class ObjLoader{ 2 public: 3 ObjLoader(string filename);//構造函數 4 void Draw();//繪制函數 5 private: 6 vector<vector<GLfloat>>vSets;//存放頂點(x,y,z)坐標 7 vector<vector<GLint>>fSets;//存放面的三個頂點索引 8 };
一個構造函數ObjLoader::ObjLoader用於導入obj文本內容
Draw用於在opengl中繪制模型
vector<vector<GLfloat>>vSets;//存放頂點(x,y,z)坐標
vector<vector<GLint>>fSets;//存放面的三個頂點索引
ObjLoader::ObjLoader(string filename) { std::ifstream file(filename); std::string line; while (getline(file, line)) { if (line.substr(0, 2) == "vt") { } else if (line.substr(0, 2) == "vn") { } else if (line.substr(0, 1) == "v") { vector<GLfloat> Point; GLfloat x, y, z; std::istringstream s(line.substr(2)); s >> x; s >> y; s >> z; Point.push_back(x); Point.push_back(y); Point.push_back(z); vSets.push_back(Point); } else if (line.substr(0, 1) == "f") { vector<GLint> vIndexSets; GLint u, v, w; std::istringstream vtns(line.substr(2)); vtns >> u; vtns >> v; vtns>> w; vIndexSets.push_back(u-1); vIndexSets.push_back(v-1); vIndexSets.push_back(w-1); fSets.push_back(vIndexSets); } else if (line.substr(0, 1) == "#") { } else { } } file.close(); } void ObjLoader::Draw(){ glBegin(GL_TRIANGLES);//開始繪制 for (int i = 0; i < fSets.size(); i++) { GLfloat VN[3]; //三個頂點 GLfloat SV1[3]; GLfloat SV2[3]; GLfloat SV3[3]; if ((fSets[i]).size() != 3) { cout << "the fSetsets_Size is not correct" << endl; } else { GLint firstVertexIndex = (fSets[i])[0];//取出頂點索引 GLint secondVertexIndex = (fSets[i])[1]; GLint thirdVertexIndex = (fSets[i])[2]; SV1[0] = (vSets[firstVertexIndex])[0];//第一個頂點 SV1[1] = (vSets[firstVertexIndex])[1]; SV1[2] = (vSets[firstVertexIndex])[2]; SV2[0] = (vSets[secondVertexIndex])[0]; //第二個頂點 SV2[1] = (vSets[secondVertexIndex])[1]; SV2[2] = (vSets[secondVertexIndex])[2]; SV3[0] = (vSets[thirdVertexIndex])[0]; //第三個頂點 SV3[1] = (vSets[thirdVertexIndex])[1]; SV3[2] = (vSets[thirdVertexIndex])[2]; GLfloat vec1[3], vec2[3], vec3[3];//計算法向量 //(x2-x1,y2-y1,z2-z1) vec1[0] = SV1[0] - SV2[0]; vec1[1] = SV1[1] - SV2[1]; vec1[2] = SV1[2] - SV2[2]; //(x3-x2,y3-y2,z3-z2) vec2[0] = SV1[0] - SV3[0]; vec2[1] = SV1[1] - SV3[1]; vec2[2] = SV1[2] - SV3[2]; //(x3-x1,y3-y1,z3-z1) vec3[0] = vec1[1] * vec2[2] - vec1[2] * vec2[1]; vec3[1] = vec2[0] * vec1[2] - vec2[2] * vec1[0]; vec3[2] = vec2[1] * vec1[0] - vec2[0] * vec1[1]; GLfloat D = sqrt(pow(vec3[0], 2) + pow(vec3[1], 2) + pow(vec3[2], 2)); VN[0] = vec3[0] / D; VN[1] = vec3[1] / D; VN[2] = vec3[2] / D; glNormal3f(VN[0], VN[1], VN[2]);//繪制法向量 glVertex3f(SV1[0], SV1[1], SV1[2]);//繪制三角面片 glVertex3f(SV2[0], SV2[1], SV2[2]); glVertex3f(SV3[0], SV3[1], SV3[2]); } } glEnd(); }
然后我們寫一個主函數
//模型路徑 string filePath = "../data/monkey.obj"; ObjLoader objModel = ObjLoader(filePath); //實現移動鼠標觀察模型所需變量 static float c = 3.1415926 / 180.0f; static float r = 1.0f; static int degree = 90; static int oldPosY = -1; static int oldPosX = -1; //安置光源 void setLightRes() { GLfloat lightPosition[] = { 0.0f, 0.0f, 1.0f, 0.0f }; glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); glEnable(GL_LIGHTING); //啟用光源 glEnable(GL_LIGHT0); //使用指定燈光 } //初始化 void init() { glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(500, 500); glutCreateWindow("ObjLoader"); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); setLightRes(); glEnable(GL_DEPTH_TEST); } void display() { glColor3f(1.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0f, 0.0f, -5.0f); setLightRes(); glPushMatrix(); gluLookAt(r*cos(c*degree), 0, r*sin(c*degree), 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); objModel.Draw();//繪制obj模型 glPopMatrix(); glutSwapBuffers(); } void reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0f, (GLdouble)width / (GLdouble)height, 1.0f, 200.0f); glMatrixMode(GL_MODELVIEW); } //移動鼠標360觀察模型 void moseMove(int button, int state, int x, int y) { if (state == GLUT_DOWN) { oldPosX = x; oldPosY = y; } } void changeViewPoint(int x, int y) { int temp = x - oldPosX; degree += temp; oldPosX = x; oldPosY = y; } void myIdle() { glutPostRedisplay(); } int main(int argc, char* argv[]) { glutInit(&argc, argv); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMouseFunc(moseMove); glutMotionFunc(changeViewPoint); glutIdleFunc(myIdle); glutMainLoop(); return 0; }
這份代碼只適合比較簡單的obj文件,針對更為復雜的obj文件讀取,比如說頂點法向量和紋理的載入等,可在這基礎上進行改進。
運行效果: