opengl+openmesh重繪rabbit
阿新 • • 發佈:2018-12-15
本次實驗的物件是斯坦福大學著名的bunny模型(兔子模型)。本文主要介紹瞭如何如下兩點:
- 使用openmesh讀取obj檔案
- 使用opengl重繪讀取的檔案
本文所用的3d模型示意圖如下所示:
具體的實驗步驟講解如下所示:
- 利用openMesh讀取obj檔案
void readfile(string file) { //請求頂點法線 mesh.request_vertex_normals(); //如果頂點法線不存在則報錯 if (!mesh.has_vertex_normals()) { cout << "Error:Standard Vertex Property 'Normals' Not Available!" << endl; return; } //讀取obj檔案 OpenMesh::IO::Options ort; if (!OpenMesh::IO::read_mesh(mesh, file, ort)) { cout << "Error:Cannot Read File:" << file << endl; return; } else { cout << "Success Read File:" << file << endl; } //如果不存在頂點法線,則計算頂點法線 if (!ort.check(OpenMesh::IO::Options::VertexNormal)) { //請求面法線 mesh.request_face_normals(); //利用面法線計算頂點法線 mesh.update_normals(); //釋放面法線 mesh.release_face_normals(); } }
- 初始化頂點與面的繪製
//初始化頂點與面的繪製 void initGL() { //設定背景顏色值為黑色 glClearColor(0.0, 0.0, 0.0, 0.0); //設定清除深度快取時使用的深度值為2 glClearDepth(2.0); //設定兩點間顏色變化為過渡模式 glShadeModel(GL_SMOOTH); //開啟深度緩衝區的功能,從而跟蹤Z軸上的畫素 glEnable(GL_DEPTH_TEST); //啟用光源,利用當前的光照引數推導頂點顏色 glEnable(GL_LIGHTING); //啟用1號光源 glEnable(GL_LIGHT0); //申請一個面的顯示列表,從而實現加速顯示 showFaceList = glGenLists(1); //申請一個線的顯示列表,從而實現加速顯示 showWireList = glGenLists(1); //獲取網格中邊的數量 int temp = mesh.n_edges(); /*繪製圖像的線*/ //建立線的顯示列表,並指定模式為編譯模式 glNewList(showWireList, GL_COMPILE); //關閉光源 glDisable(GL_LIGHTING); //設定線的寬度 glLineWidth(1.0f); //設定線的顏色為灰色 glColor3f(0.5f, 0.5f, 0.5f); //將頂點作為線段進行處理,即雙定點線段 glBegin(GL_LINES); for (MyMesh::HalfedgeIter he_it = mesh.halfedges_begin(); he_it != mesh.halfedges_end(); ++he_it) { //連線有向邊的起點與終點 glVertex3fv(mesh.point(mesh.from_vertex_handle(*he_it)).data()); glVertex3fv(mesh.point(mesh.to_vertex_handle(*he_it)).data()); } //與glBegin()配合 glEnd(); //啟用光源 glEnable(GL_LIGHTING); //與glNewList配合 glEndList(); /*繪製圖像的面*/ //建立面的顯示列表,並指定模式為編譯模式 glNewList(showFaceList, GL_COMPILE); for (MyMesh::FaceIter f_it = mesh.faces_begin(); f_it != mesh.faces_end(); ++f_it) { //將頂點作為線段進行處理,即雙定點線段 glBegin(GL_TRIANGLES); for (MyMesh::FaceVertexIter fv_it = mesh.fv_iter(*f_it); fv_it.is_valid(); ++fv_it) { //連線法向量的起點與終點 glNormal3fv(mesh.normal(*fv_it).data()); //連線有向邊的起點與終點 glVertex3fv(mesh.point(*fv_it).data()); } //與glBegin()配合 glEnd(); } //與glNewList配合 glEndList(); }
- 設定滑鼠的互動操作
//滑鼠互動 void myMouse(int button, int state, int x, int y) { //滑鼠左鍵按下 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { mousetate = 1; Oldx = x; Oldy = y; } //滑鼠右鍵按下 if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { mousetate = 0; } //滾輪事件:縮小操作 if (button == 3 && state == GLUT_UP) { scale -= 0.1f; } //滾輪事件:放大操作 if (button == 4 && state == GLUT_UP) { scale += 0.1f; } //標記重繪該介面 glutPostRedisplay(); } // 滑鼠運動時 void onMouseMove(int x, int y) { if (mousetate) { //計算y軸旋轉角度:x對應y是因為其對應的是法向量 yRotate += x - Oldx; //標記當前視窗需要重繪 glutPostRedisplay(); //更新Oldx的數值 Oldx = x; //計算x軸旋轉角度:y對應x是因為其對應的是法向量 xRotate += y - Oldy; //標記當前視窗需要重繪 glutPostRedisplay(); //更新Oldy的數值 Oldy = y; } }
- 設定鍵盤的互動操作
//鍵盤操作顯示模式 void myKeyboard(unsigned char key, int x, int y) { switch (key) { case '1': showFaces = true; showWires = false; showFaceWires = false; break; case '2': showFaces = false; showWires = true; showFaceWires = false; break; case '3': showFaces = false; showWires = false; showFaceWires = true; break; default: break; } glutPostRedisplay(); }
- 編寫影象的繪製函式
//影象顯示函式 void myDisplay() { //要清除之前的深度快取 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //將矩陣單位化 glLoadIdentity(); //讓物體在三維空間中進行縮放 glScalef(scale, scale, scale); //讓物體繞x軸旋轉xRotate度 glRotatef(xRotate, 1.0f, 0.0f, 0.0f); //讓物體繞y軸旋轉yRotate度 glRotatef(yRotate, 0.0f, 1.0f, 0.0f); //讓物體沿著z軸平移 glTranslatef(0.0f, 0.0f, 0.0f); /*傳遞想要顯示的列表*/ //顯示影象的面 if (showFaces) { glCallList(showFaceList); } //顯示影象的線 if (showWires) { glCallList(showWireList); } //顯示影象的面和線 if (showFaceWires) { glCallList(showFaceList); glCallList(showWireList); } //將前後緩衝區域進行交換 glutSwapBuffers(); }
- 完整的程式碼如下所示
#include <OpenMesh/Core/IO/MeshIO.hh> // 讀取檔案 #include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh> // 操作檔案 #include <iostream> #include <math.h> #include <string.h> #include <sstream> #include <fstream> #include <vector> #include <windows.h> #include "GL\freeglut.h" using namespace std; typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh; //檔案讀取有關的定義 MyMesh mesh; const string modelfile = "bunny.obj"; //檔案的旋轉與縮放 float scale = 1; float xRotate = 0.0f; float yRotate = 0.0f; //定義點、線、面的顯示列表 GLuint showFaceList, showWireList; //設定顯示的模式 bool showFaces = false; bool showWires = false; bool showFaceWires = true; //滑鼠互動有關的定義 int mousetate = 0; //滑鼠當前的狀態 GLfloat Oldx = 0.0; //點選之前的x座標 GLfloat Oldy = 0.0; //點選之前的y座標 //利用OpenMesh讀取obj檔案 void readfile(string file) { //請求頂點法線 mesh.request_vertex_normals(); //如果頂點法線不存在則報錯 if (!mesh.has_vertex_normals()) { cout << "Error:Standard Vertex Property 'Normals' Not Available!" << endl; return; } //讀取obj檔案 OpenMesh::IO::Options ort; if (!OpenMesh::IO::read_mesh(mesh, file, ort)) { cout << "Error:Cannot Read File:" << file << endl; return; } else { cout << "Success Read File:" << file << endl; } //如果不存在頂點法線,則計算頂點法線 if (!ort.check(OpenMesh::IO::Options::VertexNormal)) { //請求面法線 mesh.request_face_normals(); //利用面法線計算頂點法線 mesh.update_normals(); //釋放面法線 mesh.release_face_normals(); } } //改變視窗大小時重繪函式 void myReshape(GLint w, GLint h) { //設定顯示視窗的大小 glViewport(0, 0, static_cast<GLsizei>(w), static_cast<GLsizei>(h)); //將當前矩陣指定為投影矩陣 glMatrixMode(GL_PROJECTION); //將矩陣設定為單位矩陣 glLoadIdentity(); /*設定修建空間的範圍,進行正射投影,從而建立一個平行視景圖*/ if (w > h) glOrtho(-static_cast<GLdouble>(w) / h, static_cast<GLdouble>(w) / h, -1.0, 1.0, -100.0, 100.0); else glOrtho(-1.0, 1.0, -static_cast<GLdouble>(h) / w, static_cast<GLdouble>(h) / w, -100.0, 100.0); //將當前矩陣設定為模型檢視矩陣 glMatrixMode(GL_MODELVIEW); //將矩陣設定為單位矩陣 glLoadIdentity(); } //閒置顯示操作函式 void myIdle() { //設定繞x軸的旋轉角度 xRotate += 0.5f; //設定繞y軸的旋轉角度 yRotate += 0.5f; //調整繞x軸超限後的旋轉角度 if (xRotate >= 360.0f) { xRotate -= 360.0f; } //調整繞y軸超限後的旋轉角度 if (yRotate >= 360.0f) { yRotate -= 360.0f; } //標記重繪此介面 glutPostRedisplay(); } //初始化頂點與面的繪製 void initGL() { //設定背景顏色值為黑色 glClearColor(0.0, 0.0, 0.0, 0.0); //設定清除深度快取時使用的深度值為2 glClearDepth(2.0); //設定兩點間顏色變化為過渡模式 glShadeModel(GL_SMOOTH); //開啟深度緩衝區的功能,從而跟蹤Z軸上的畫素 glEnable(GL_DEPTH_TEST); //啟用光源,利用當前的光照引數推導頂點顏色 glEnable(GL_LIGHTING); //啟用1號光源 glEnable(GL_LIGHT0); //申請一個面的顯示列表,從而實現加速顯示 showFaceList = glGenLists(1); //申請一個線的顯示列表,從而實現加速顯示 showWireList = glGenLists(1); //獲取網格中邊的數量 int temp = mesh.n_edges(); /*繪製圖像的線*/ //建立線的顯示列表,並指定模式為編譯模式 glNewList(showWireList, GL_COMPILE); //關閉光源 glDisable(GL_LIGHTING); //設定線的寬度 glLineWidth(1.0f); //設定線的顏色為灰色 glColor3f(0.5f, 0.5f, 0.5f); //將頂點作為線段進行處理,即雙定點線段 glBegin(GL_LINES); for (MyMesh::HalfedgeIter he_it = mesh.halfedges_begin(); he_it != mesh.halfedges_end(); ++he_it) { //連線有向邊的起點與終點 glVertex3fv(mesh.point(mesh.from_vertex_handle(*he_it)).data()); glVertex3fv(mesh.point(mesh.to_vertex_handle(*he_it)).data()); } //與glBegin()配合 glEnd(); //啟用光源 glEnable(GL_LIGHTING); //與glNewList配合 glEndList(); /*繪製圖像的面*/ //建立面的顯示列表,並指定模式為編譯模式 glNewList(showFaceList, GL_COMPILE); for (MyMesh::FaceIter f_it = mesh.faces_begin(); f_it != mesh.faces_end(); ++f_it) { //將頂點作為線段進行處理,即雙定點線段 glBegin(GL_TRIANGLES); for (MyMesh::FaceVertexIter fv_it = mesh.fv_iter(*f_it); fv_it.is_valid(); ++fv_it) { //連線法向量的起點與終點 glNormal3fv(mesh.normal(*fv_it).data()); //連線有向邊的起點與終點 glVertex3fv(mesh.point(*fv_it).data()); } //與glBegin()配合 glEnd(); } //與glNewList配合 glEndList(); } //滑鼠互動 void myMouse(int button, int state, int x, int y) { //滑鼠左鍵按下 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { mousetate = 1; Oldx = x; Oldy = y; } //滑鼠右鍵按下 if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { mousetate = 0; } //滾輪事件:縮小操作 if (button == 3 && state == GLUT_UP) { scale -= 0.1f; } //滾輪事件:放大操作 if (button == 4 && state == GLUT_UP) { scale += 0.1f; } //標記重繪該介面 glutPostRedisplay(); } // 滑鼠運動時 void onMouseMove(int x, int y) { if (mousetate) { //計算y軸旋轉角度:x對應y是因為其對應的是法向量 yRotate += x - Oldx; //標記當前視窗需要重繪 glutPostRedisplay(); //更新Oldx的數值 Oldx = x; //計算x軸旋轉角度:y對應x是因為其對應的是法向量 xRotate += y - Oldy; //標記當前視窗需要重繪 glutPostRedisplay(); //更新Oldy的數值 Oldy = y; } } //鍵盤操作顯示模式 void myKeyboard(unsigned char key, int x, int y) { switch (key) { case '1': showFaces = true; showWires = false; showFaceWires = false; break; case '2': showFaces = false; showWires = true; showFaceWires = false; break; case '3': showFaces = false; showWires = false; showFaceWires = true; break; default: break; } glutPostRedisplay(); } //影象顯示函式 void myDisplay() { //要清除之前的深度快取 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //將矩陣單位化 glLoadIdentity(); //讓物體在三維空間中進行縮放 glScalef(scale, scale, scale); //讓物體繞x軸旋轉xRotate度 glRotatef(xRotate, 1.0f, 0.0f, 0.0f); //讓物體繞y軸旋轉yRotate度 glRotatef(yRotate, 0.0f, 1.0f, 0.0f); //讓物體沿著z軸平移 glTranslatef(0.0f, 0.0f, 0.0f); /*傳遞想要顯示的列表*/ //顯示影象的面 if (showFaces) { glCallList(showFaceList); } //顯示影象的線 if (showWires) { glCallList(showWireList); } //顯示影象的面和線 if (showFaceWires) { glCallList(showFaceList); glCallList(showWireList); } //將前後緩衝區域進行交換 glutSwapBuffers(); } int main(int argc, char** argv) { //初始化glut庫 glutInit(&argc, argv); //設定glut的模式 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); //設定顯示視窗的位置 glutInitWindowPosition(100, 100); //設定顯示視窗的大小 glutInitWindowSize(800, 500); //設定顯示視窗的名稱 glutCreateWindow("Mesh Viewer"); //讀取模型檔案 readfile(modelfile); //初始化頂點與面的繪製 initGL(); //滑鼠互動功能 glutMouseFunc(myMouse); //滑鼠移動功能 glutMotionFunc(onMouseMove); //按鍵操作顯示模式 glutKeyboardFunc(&myKeyboard); //調整介面大小後的重繪操作 glutReshapeFunc(&myReshape); //影象的繪製 glutDisplayFunc(&myDisplay); //空閒時呼叫的影象繪製函式 glutIdleFunc(&myIdle); //glut迴圈更新介面操作 glutMainLoop(); return 0; }