opengl學習筆記(三)——玩弄模型(使用者互動)
阿新 • • 發佈:2018-11-24
經過前面兩節的功夫,我們算是搞到了一個還算可以的模型,但我們想要把模型翻來翻去,移動一下還是不行的。並且,根據使用者(老師)需求,我們要讓模型能以線框,點的方式繪製。也就有了這最後一講。
首先,我們通過滑鼠左鍵來旋轉物體。這就需要在滑鼠回撥函式裡下功夫了
if (state == GLUT_DOWN && button == GLUT_LEFT_BUTTON)//記錄旋轉拖動起始點 { flag = 0; r_x1 = r_x2 = x; r_y1 = r_y2 = y; } if (state == GLUT_UP && button == GLUT_LEFT_BUTTON)//記錄旋轉拖動最終點並將旋轉量儲存進d,e { d += (r_x2 - r_x1) / 3; e -= (r_y2 - r_y1) / 3; r_x1 = r_x2 = r_y1 = r_y2 = 0; }
這裡的d,e分別代表了繞著y和x軸旋轉的分量。為什麼沒有Z?因為我們可以用x,y的旋轉來代替繞Z的旋轉,這裡不具體說明了。
我們用同樣的方法設定中鍵繫結平移操作。
if (state == GLUT_DOWN && button == GLUT_MIDDLE_BUTTON)//記錄平移拖動起始點 { flag = 1; mov_x1 = mov_x2 = x; mov_y1 = mov_y2 = y; } if (state == GLUT_UP && button == GLUT_MIDDLE_BUTTON)//記錄平移最終點,並將平移量存入trans_x,trans_y { trans_x += mov_x2 - mov_x1; trans_y += mov_y1 - mov_y2; mov_x1 = mov_y1 = mov_x2 = mov_y2 = 0; }
同時,我們需要在滑鼠移動的回撥函式裡做更改來實現拖動。
void end(int x, int y)
{
if (!flag)
{
r_x2 = x;
r_y2 = y;
}
if (flag == 1)
{
mov_x2 = x;
mov_y2 = y;
}
}
我們增加flag來區別左鍵和中鍵的拖動。
最後寫一個簡單的選單實現繪製的切換。這裡不多說明,可以看原始碼裡的操作。同時原始碼裡增加了支援鍵盤改變視角和模型的函式。
#include<iostream> #include<fstream> #include<GL/glew.h>//使用glew庫使用VBO #include <GL/glut.h> #include<cmath> #include<ctime> #include<vector> #include<string> #define BUFFER_OFFSET(bytes) ((GLubyte*)NULL+bytes) #define T 100 #pragma comment(lib, "glew32.lib")//加個glew使用VBO,嘖 using namespace std; struct Vertex { GLfloat x, y, z, nx, ny, nz; }ver; struct Index { GLuint a, b, c; }; struct Index_Line { GLuint a, b; }; Vertex* v_arr;//點集 Index* index_arr;//面索引 Index_Line* index_line_arr;//線索引 int v_num, index_num; double a, b, c;//相機位置 double d, e, f;//旋轉角度 GLuint VBO;//只有一個的孤獨VBO GLuint EBO[2];//索引緩衝指標 GLfloat l_position[4] = { 0,0,0,1 }; GLfloat r_x1, r_y1, r_x2, r_y2;//滑鼠拖動引數 GLint tag = 2;//初始表示畫多邊形 GLfloat mov_x1, mov_y1, mov_x2, mov_y2;//平移變數 GLfloat trans_x, trans_y; GLfloat T_x, T_y, T_z; GLint flag;//0表示滑鼠左鍵按下,1表示中鍵按下 void init(void) { T_x = T_y = T_z = 0; char ch[50]; ifstream in("lizhenxiout-repaired.ply"); bool www = in.fail(); for (int i = 0; i < 3; i++) in.getline(ch, 50); in >> ch; in >> ch; in >> ch; v_num = atoi(ch); for (int i = 0; i < 7; i++) in.getline(ch, 50); in >> ch; in >> ch; in >> ch; index_num = atoi(ch); for (int i = 0; i < 2; i++) in.getline(ch, 50); in.getline(ch, 50); //愚蠢的頭部讀取完畢 v_arr = new Vertex[v_num]; index_arr = new Index[index_num]; index_line_arr = new Index_Line[3 * index_num]; for (int i = 0; i < v_num; i++) { in >> v_arr[i].x >> v_arr[i].y >> v_arr[i].z >> v_arr[i].nx >> v_arr[i].ny >> v_arr[i].nz; T_x += v_arr[i].x; T_y += v_arr[i].y; T_z += v_arr[i].z; } T_x /= v_num; T_y /= v_num; T_z /= v_num; for (int i = 0; i < index_num; i++) { in >> index_arr[i].a >> index_arr[i].a >> index_arr[i].b >> index_arr[i].c; index_line_arr[3 * i].a = index_line_arr[3 * i + 2].b = index_arr[i].a; index_line_arr[3 * i + 1].a = index_line_arr[3 * i].b = index_arr[i].b; index_line_arr[3 * i + 2].a = index_line_arr[3 * i + 1].b = index_arr[i].c; } //使用VBO載入資料 glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, v_num*sizeof(Vertex), v_arr, GL_STATIC_DRAW); //建立EBO索引 glGenBuffers(2, EBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[0]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_num*sizeof(Index), index_arr, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * index_num*sizeof(Index_Line), index_line_arr, GL_STATIC_DRAW); //設定頂點和法線指標 glVertexPointer(3, GL_FLOAT, 6 * sizeof(GLfloat), BUFFER_OFFSET(0)); glNormalPointer(GL_FLOAT, 6 * sizeof(GLfloat), BUFFER_OFFSET(3 * sizeof(GLfloat))); //開啟對應功能 glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GLUT_MULTISAMPLE); glMatrixMode(GL_PROJECTION);//設定投影矩陣 glLoadIdentity(); gluPerspective(60, 1, 0.1, 100000); glClearColor(0, 0, 0, 1); a = 200; b = 0.0; c = 0.0; d = e = f = 0; trans_x = trans_y = 0; r_x1 = r_y1 = r_x2 = r_y2 = 0; mov_x1 = mov_y1 = mov_x2 = mov_y2 = 0; } void timerFunc(int value) { glutPostRedisplay(); glutTimerFunc(10, timerFunc, 1); } void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glLightfv(GL_LIGHT0, GL_POSITION, l_position); gluLookAt(b, c, a, T_x, T_y, T_z, 0, 1, 0); glTranslated(trans_x + (mov_x2 - mov_x1)+T_x, trans_y + (mov_y1 - mov_y2)+T_y, T_z); glRotatef(d + (r_x2 - r_x1) / 3, 0, 1, 0); glRotatef(e - (r_y2 - r_y1) / 3, 1, 0, 0); glRotatef(f, 0, 0, 1); glTranslated(-trans_x - (mov_x2 - mov_x1)-T_x, -trans_y - (mov_y1 - mov_y2)-T_y, -T_z); glTranslated(trans_x + (mov_x2 - mov_x1), trans_y + (mov_y1 - mov_y2), 0); if (tag == 2) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[0]); glDrawElements(GL_TRIANGLES, 3 * index_num, GL_UNSIGNED_INT, 0); } if (tag == 1) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[1]); glDrawElements(GL_LINES, 2 * 3 * index_num, GL_UNSIGNED_INT, 0); } if (tag == 0) { glDrawArrays(GL_POINTS, 0, v_num); } //測試用三角形 //glBegin(GL_TRIANGLES); //glVertex3i(-100, 0, 0); //glVertex3i(100, 0, 0); //glVertex3i(0, 200, 0); //glEnd(); glutSwapBuffers(); } void s_input(int key, int x, int y)//方向鍵改變相機Z { if (key == GLUT_KEY_DOWN) a += 10; if (key == GLUT_KEY_UP) a -= 10; } void input(unsigned char key, int x, int y) { switch (key)//你還是可以通過鍵盤控制引數 { case'w':c += 10; break;//相機x,y移動 case's':c -= 10; break; case'a':b -= 10; break; case'd':b += 10; break; case'h':d -= 5; break;//三個旋轉度設定 case'k':d += 5; break; case'u':e -= 5; break; case'j':e += 5; break; case'i':f += 5; break; case'y':f -= 5; break; default: break; } } void start(int button, int state, int x, int y) { if (state == GLUT_DOWN && button == GLUT_LEFT_BUTTON)//記錄旋轉拖動起始點 { flag = 0; r_x1 = r_x2 = x; r_y1 = r_y2 = y; } if (state == GLUT_DOWN && button == GLUT_MIDDLE_BUTTON)//記錄旋轉拖動起始點 { flag = 1; mov_x1 = mov_x2 = x; mov_y1 = mov_y2 = y; } if (state == GLUT_UP && button == GLUT_LEFT_BUTTON)//記錄旋轉拖動最終點並將旋轉量儲存進d,e { d += (r_x2 - r_x1) / 3; e -= (r_y2 - r_y1) / 3; r_x1 = r_x2 = r_y1 = r_y2 = 0; } if (state == GLUT_UP && button == GLUT_MIDDLE_BUTTON)//記錄平移最終點,並將平移量存入trans_x,trans_y { trans_x += mov_x2 - mov_x1; trans_y += mov_y1 - mov_y2; mov_x1 = mov_y1 = mov_x2 = mov_y2 = 0; } } void end(int x, int y) { if (!flag) { r_x2 = x; r_y2 = y; } if (flag == 1) { mov_x2 = x; mov_y2 = y; } } void menu(int value) { tag = value; } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); GLenum err = glewInit();//初始化glew if (GLEW_OK != err) { exit(0); } init(); glutDisplayFunc(display); glutTimerFunc(10, timerFunc, 1); glutSpecialFunc(s_input); glutKeyboardFunc(input); glutMouseFunc(start); glutMotionFunc(end); int menu_id = glutCreateMenu(menu); glutAddMenuEntry("dot", 0); glutAddMenuEntry("line", 1); glutAddMenuEntry("polygon", 2); glutAttachMenu(GLUT_RIGHT_BUTTON); glutMainLoop(); return 0; }