Study/OpenGL OpenGL bunny코드 분석 코딩하는 야구쟁이 2011. 10. 16. 15:02 #include #include #include #include #include #include #include #define PI 3.1415926 float radius; float theta; float phi; GLfloat light0_ambient[] = {1.0f, 1.0f, 1.0f, 1.0f}; GLfloat light0_diffuse[] = {1.0f, 0.0f, 0.0f, 1.0f}; GLfloat light0_specular[] = {1.0f, 1.0f, 0.0f, 1.0f}; GLfloat light0_position[] = {100.0f, 100.0f, 100.0f, 1.0f}; //GLfloat light0_position[] = {2.0f, 2.0f, 2.0f, 1.0f}; GLfloat light1_ambient[] = {1.0f, 1.0f, 1.0f, 1.0f}; GLfloat light1_diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; GLfloat light1_specular[] = {1.0f, 0.0f, 0.0f, 1.0f}; GLfloat light1_position[] = {10.0f, 10.0f, 100.0f, 0.0f}; //GLfloat light1_position[] = {0.0f, 0.0f, 10.0f, 0.0f}; GLfloat material0_ambient[] = {0.2f, 1.0f, 0.5f, 1.0f}; GLfloat material0_diffuse[] = {0.2f, 1.0f, 0.5f, 1.0f}; GLfloat material0_specular[] = {1.0f, 1.0f, 0.0f, 1.0f}; GLfloat vertices[][3] = { { -1.0, -1.0, 1.0 }, // 0 { -1.0, 1.0, 1.0 }, // 1 { 1.0, 1.0, 1.0 }, // 2 { 1.0, -1.0, 1.0 }, // 3 { -1.0, -1.0, -1.0 }, // 4 { -1.0, 1.0, -1.0 }, // 5 { 1.0, 1.0, -1.0 }, // 6 { 1.0, -1.0, -1.0 }}; // 7 // Color Setting GLfloat colors[][3] = { { 1.0, 0.0, 0.0 }, // red { 1.0, 1.0, 1.0 }, // white { 1.0, 1.0, 0.0 }, // yellow { 0.0, 1.0, 0.0 }, // green { 0.0, 0.0, 1.0 }, // blue { 1.0, 0.0, 1.0 }}; // magenta struct vector2d{ float x; float y; }; struct vector3d{ float x; float y; float z; }; struct point2d{ float x; float y; }; struct point3d{ float x; float y; float z; vector3d normal; }; point3d *ver; struct face3{ int v1; int v2; int v3; vector3d normal; }; face3 *face; int v_no, f_no; // Data Range float bound_max_x, bound_min_x; float bound_max_y, bound_min_y; float bound_max_z, bound_min_z; // Camera Rotation Parameters float last_x, last_y; float rotationX, rotationY; // 파일을 읽어들이는 함수 void file_load(void) { char *filename; FILE *fp1; filename = "bunny1.txt"; // filename = "head_new.txt"; // filename = "hand_new.txt"; // filename = "sculpture_new.txt"; // '읽기 형식'으로 filename이라는 이름을 가진 파일에 접근해 fp1(FILE객체)에 저장한다. if ( (fp1 = fopen(filename, "r")) == NULL ) { cout << " Can not compute \n"; exit(1); } fscanf(fp1,"%d",&v_no); // vertex의 개수를 읽어 v_no에 저장 ver= new point3d[v_no]; // v_no 개수만큼 point3d 구조체 생성 // vertex를 읽어들인다. for (int i = 0; i < v_no; i++) { fscanf(fp1,"%f %f %f", &(ver[i].x),&(ver[i].y),&(ver[i].z)); // vertex(점)의 정보를 읽어서 저장 if ( ver[i].x > bound_max_x ) bound_max_x = ver[i].x; if ( ver[i].x < bound_min_x ) bound_min_x = ver[i].x; if ( ver[i].y > bound_max_y ) bound_max_y = ver[i].y; if ( ver[i].y < bound_min_y ) bound_min_y = ver[i].y; if ( ver[i].z > bound_max_z ) bound_max_z = ver[i].z; if ( ver[i].z < bound_min_z ) bound_min_z = ver[i].z; } fscanf(fp1,"%d",&f_no); // face의 개수를 읽어 f_no에 저장 face = new face3[f_no]; // f_no 개수만큼 face3 구조체 생성 for ( i = 0; i < f_no; i++ ) fscanf(fp1,"%d %d %d", &(face[i].v1), &(face[i].v2), &(face[i].v3)); // face(면)의 정보를 읽어서 저장 fclose(fp1); // 파일 스트림을 닫는다.(메모리 해제) } // 법선벡터를 정규화한다. 조명 효과를 적용하려면 // 모든 벡터는 정규화 과정을 거쳐야 한다. (단위 법선벡터 : 길이가 1인 법선벡터) vector3d Normalization (vector3d p) { float mag; mag = sqrt(p.x*p.x + p.y*p.y + p.z*p.z); p.x /= mag; p.y /= mag; p.z /= mag; return p; } // 벡터 크로스곱. 두 개의 벡터를 가지고 크로스곱을 적용하면 // 외적(법선벡터)가 나온다. 법선벡터는 두 벡터에 대한 수직인 벡터이다. vector3d Cross_Product(vector3d U, vector3d V) { vector3d cross; // 외적(법선벡터)을 구한다. cross.x = U.y * V.z - U.z * V.y ; cross.y = -1.0 * (U.x * V.z - U.z * V.x) ; cross.z = U.x * V.y - U.y * V.x ; return Normalization(cross); // 계산하기 편한 정규화 적용 } // 표면의 벡터 외적(법선벡터) 계산. 벡터의 외적은 주로 곡면 또는 평면의 // 한 점에 대해 서로 다른 두 접선 벡터가 주어질 때, 그 표면에 수직한 // 법선 벡터를 구하는 사용한다. 외적은 크로스곱으로 구함. // 지엘에선 정점에 의해 둘러싸인 면이 광원을 향한 면인지 // 안쪽면인지 알 방법이 없다. 그래서 법선 벡터를 구해야하는데 // 법선 벡터를 구하기 위해서는 면의 외적을 구해야한다. void face_normal_comtutation(void) { vector3d U, V; int first, second, third; // 세 점을 이용해 두 개의 벡터를 구한다. for ( int j = 0; j < f_no; j++ ) { first = face[j].v1; second = face[j].v2; third = face[j].v3; U.x = ver[third].x - ver[second].x; U.y = ver[third].y - ver[second].y; U.z = ver[third].z - ver[second].z; V.x = ver[first].x - ver[second].x; V.y = ver[first].y - ver[second].y; V.z = ver[first].z - ver[second].z; face[j].normal = Cross_Product(U, V); // 두 개의 벡터를 가지고 크로스곱으로 외적(법선벡터)을 구한다. } } // 벡터의 크기를 구한다. float Vector_Magnitude(vector3d U) { return(sqrt(U.x*U.x + U.y*U.y + U.z*U.z)); } // 정점의 법선벡터 계산하고 정규화를 적용한다. // 다각형 면의 법선벡터를 구하는 것과 이를 기준으로 최종적으로 // 다각형 정점에서의 법선 벡터를 구해야 라이팅을 적용할 수 있다. void vertex_normal_comtutation(void) { vector3d temp_normal; int *index; int count; for ( int i = 0 ; i < v_no; i++ ) { // 점의 개수만큼 증가 index = new int[100]; count = 0; for ( int j = 0; j < f_no; j++ ) { // 면의 개수만큼 증가 if ( i == face[j].v1 || i == face[j].v2 || i == face[j].v3 ) { index[count] = j; count++; } } temp_normal.x = 0.0; temp_normal.y = 0.0; temp_normal.z = 0.0; for ( int k = 0; k < count ; k++ ) { temp_normal.x += face[index[k]].normal.x; temp_normal.y += face[index[k]].normal.y; temp_normal.z += face[index[k]].normal.z; } temp_normal.x /= count; temp_normal.y /= count; temp_normal.z /= count; ver[i].normal = Normalization(temp_normal); delete(index); } } // 오브젝트를 그린다. void draw_object(void) { glPointSize(5.0); for ( int i = 0; i < f_no; i++ ) { glBegin(GL_POLYGON); // glBegin(GL_POINTS); glNormal3f(ver[face[i].v1].normal.x,ver[face[i].v1].normal.y, ver[face[i].v1].normal.z ); // 법선벡터 정의 glVertex3f(ver[face[i].v1].x, ver[face[i].v1].y, ver[face[i].v1].z ); // 점의 위치 glNormal3f(ver[face[i].v2].normal.x,ver[face[i].v2].normal.y, ver[face[i].v2].normal.z ); glVertex3f(ver[face[i].v2].x, ver[face[i].v2].y, ver[face[i].v2].z ); glNormal3f(ver[face[i].v3].normal.x,ver[face[i].v3].normal.y, ver[face[i].v3].normal.z ); glVertex3f(ver[face[i].v3].x, ver[face[i].v3].y, ver[face[i].v3].z ); glEnd(); } } // 초기화 void init (void) { glClearColor (0.0, 0.0, 0.0, 0.0) ; // 배경색 초기화 glColor3f(1.0, 1.0, 0.0); radius = 1.5; theta = 0.0; phi = 0.0; glEnable(GL_DEPTH_TEST); // 깊이감 부여. 깊이 정보에 따라 그리게 해준다. bound_max_x = -1000; bound_min_x = 1000; bound_max_y = -1000; bound_min_y = 1000; bound_max_z = -1000; bound_min_z = 1000; file_load(); face_normal_comtutation(); // 표면에 대한 법선 벡터를 계산하고 정규화를 적용한다. vertex_normal_comtutation(); // 정점에 대한 법선 벡터를 계산하고 정규화를 적용한다. last_x = 0; last_y = 0; rotationX = 0.0; rotationY = 0.0; glShadeModel(GL_SMOOTH); // glShadeModel(GL_FLAT); glEnable(GL_LIGHTING); // 조명기능 활성화 glEnable(GL_LIGHT0); // 0번 조명을 사용한다. glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient); // 주변광 적용 glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse); // 분산광 적용 glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular); // 경면광 적용 glLightfv(GL_LIGHT0, GL_POSITION, light0_position); // 광원 위치 설정 /* glEnable(GL_LIGHT1); glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient); glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular); glLightfv(GL_LIGHT1, GL_POSITION, light1_position); glMaterialfv(GL_FRONT, GL_AMBIENT, material0_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, material0_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, material0_specular); */ } void reshape (int w, int h) { glViewport(0, 0, w, h); glMatrixMode (GL_PROJECTION); // GL_PROJECTION 이후에 나오는 행렬 관련 함수들은 모두 // 투영행렬 대상으로 적용된다. 즉, 만들어진 물체들이 // 최종적으로 어떻게 화면에 뿌릴 것인가를 정의한다. // 직교투영과 원근투영이 있다. glLoadIdentity(); // 프로젝트 행렬(해당 모드의 행렬)을 단위행렬로 초기화 // glOrtho(-100.0, 100.0, -100.0, 100.0, -100.0, 1000); gluPerspective(45.0, 1.0, 1.0, 1000); // 원근좌표계(투영) 적용. 관측공간 설정. } void display (void) { // Camera Setting float x, y, z; float dx, dy, dz; float cx, cy, cz; float distance ; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode (GL_MODELVIEW); // 3차원 공간에서 물체의 실제적인 위치를 지정해주는 모드. glLoadIdentity(); // 모델뷰 행렬을 단위행렬로 초기화 // 초점(Focus) 계산 cx = (bound_max_x + bound_min_x)/2.0; cy = (bound_max_y + bound_min_y)/2.0; cz = (bound_max_z + bound_min_z)/2.0; // 카메라의 위치 계산 dx = (bound_max_x - bound_min_x)/2.0; dy = (bound_max_y - bound_min_y)/2.0; dz = (bound_max_z - bound_min_z)/2.0; distance = 2.0 * sqrt(dx*dx + dy*dy + dz*dz); x = distance * cos(phi) * cos(theta); y = distance * cos(phi) * sin(theta); z = distance * sin(phi); gluLookAt(x, y, z, cx, cy, cz, 0.0, 0.0, 1.0); // 카메라의 위치, 초점, 상향벡터 light0_position[0] = x; light0_position[1] = y; light0_position[2] = z; glLightfv(GL_LIGHT0, GL_POSITION, light0_position); /* glBegin(GL_LINES); glColor3f(1.0, 0.0, 0.0); // x축 glVertex3f(0.0, 0.0, 0.0); glVertex3f(1000.0, 0.0, 0.0); glColor3f(0.0, 1.0, 0.0); // y축 glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, 1000.0, 0.0); glColor3f(0.0, 0.0, 1.0); // z축 glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, 0.0, 1000.0); glEnd(); */ // 오브젝트를 약간 회전시켜서 그린다. glRotatef(rotationY, 0.0, 1.0, 0.0); glRotatef(rotationX, 1.0, 0.0, 0.0); draw_object(); glFlush (); // 큐에 담겨져 기다리고 있던 명령을 실행 glutSwapBuffers(); // 더블버퍼 } // 마우스 움직일때 void myGlutMotion(int x, int y ) { rotationX += (float) (y - last_y); rotationY += (float) (x - last_x); last_x = x; last_y = y; glutPostRedisplay(); // 화면 다시 그리기 } // 마우스로 클릭할때 void myGlutMouse(int button, int button_state, int x, int y ) { if ( button == GLUT_LEFT_BUTTON && button_state == GLUT_DOWN ) { last_x = x; last_y = y; } } // 특수키 void SpecialKey( int key, int x, int y) { switch (key) { case GLUT_KEY_LEFT : theta -= 0.1; // 카메라 각도 이동 break; case GLUT_KEY_RIGHT : theta += 0.1; break; case GLUT_KEY_UP : phi += 0.1; break; case GLUT_KEY_DOWN : phi -= 0.1; break; default : break; } if ( theta > 2.0 * PI ) theta -= (2.0 * PI); else if ( theta < 0.0 ) theta += (2.0 * PI); } void main (int argc, char** argv) { glutInit (&argc, argv); glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); // 디스플레이 모드 초기화 glutInitWindowPosition (100, 100); glutInitWindowSize (500, 500); glutCreateWindow ("Bunny"); glutDisplayFunc (display); // 디스플레이 콜백함수 설정 glutReshapeFunc (reshape); // 창의 위치, 크기가 변경 될때의 콜백함수 설정 glutSpecialFunc(SpecialKey); // 특수키 콜백함수 설정 glutMouseFunc(myGlutMouse); // 마우스 콜백함수 설정 glutMotionFunc(myGlutMotion); // 마우스를 눌린 상태에서 포인터가 움직이는 경우 호출할 콜백함수 등록 glutIdleFunc(display); // 이벤트가 없을 경우 호출할 콜백함수 등록 init(); glutMainLoop(); // 이벤트 진입 함수 }