3차원 물체 그리기
3차원 물체 그리기 OpenGL의 모델 좌표계에서 지정된 한 점이 스크린에 나타나기까지의 과정
모델링 변환 (Modeling Transformation, Model-view Matrix) 정점(x,y,z)은 원점을 기준으로 표현된 객체의 좌표점이다. 모델을 만들기 위해 사용한 기본 모델(raw model) 좌표계를 관측점(viewpoint)에서 보았을 때 위치(눈을 원점으로 간주)하는 좌표계로 변환한다. 이때 사용되는 행렬은 모델뷰 행렬 이다. 즉, 첫 번째 변환에서는 원점을 기준으로 하는 객체 좌표계(기본 모델 좌표계)의 좌표점을 관측점(눈/카메라)을 원점으로 하는 실세계 좌표계의 한 점으로 변환한다.
2)투영 변환 (Projection Transformation, Projection Matrix) 관측 볼륨에서(viewing volume)에서 벗어난 영역을 제거한다. 따라서 화면에서 볼 수 있는 물체만을 가지게 된다. 관측 볼륨은 관측자 시야에 들어오는 영역을 의미한다.
3)원근감 있는 영역(Perspective Division) 투영 변환에 의해서 정점이 앞에 있는지 뒤에 있는지 3차원에서의 위치가 정해지고 정점들을 정규화된 장치 좌표(normalized device coordinate)로 변환을 한다.
4) 뷰포트 변환(Viewport transformation) 3차원 물체(3D 좌표계)를 보여줄 윈도의 좌표(2D 좌표계)로 변환(랜더링) 한다. => OpenGL은 3차원 상의 정점을 2차원 평면에 나타내기 위해 4개의 변환과정을 거친다. 이들 변환은 4*4 행렬로 되어 있으며 정점의 좌표에 이들 행렬을 곱함으로써 변환을 하게 된다.
일반 변환 함수들 OpenGL에서는 행렬(matrix)에 의해서 변형(transformation)이 표현되고, OpenGL상의 모든 변환(transformation)은 두 개 또는 그 이상의 행렬(matrix)에 의해 나타내어진다. 기본 변환 행렬에 사용되는 함수 void glMatrixMode(GLenum mode); //C Sub glMatrixMode(mode As glMatrixModeConstants) 'VB glMatrixMode()함수는 행렬(matrix) 모드를 설정하는데, 모델 뷰(mmModelView), 투영(mmProjection), 텍스처(mmTexture) 행렬(mmMatrix)중 하나를 결정한다.
glMatrixMode() 함수의 mode인자 행렬 모드를 특정 모드로 설정한 다음 다음에 지정할 변환(투영 설정 또는 이동/회전/크기조절)이 효과를 나타낼 수 있도록 현재의 행렬을 초기화하는 glLoadIdentity() 함수를 사용해야 하며, 투영 모드로 설정한 다음 다시 모델뷰 모드로 설정해야 화면에 그림을 그릴 수 있다. glMatrixMode mmProjection ‘투영모드로 설정한다. glLoadIdentity ‘단위 행렬을 곱한다. glFrustum -1.0, 1.0, -1.0, 1.0, 1.5, 20.0 ‘원근 투영을 설정한다. glMatrixMode mmModelView ‘다시 모델뷰 모드로 설정한다.
일반 변환 함수들-2 현재의 행렬을 4*4 단위행렬로 설정하는 함수 현재 행렬의 16개 값을 m에 대입하는 함수 void glLoadIdentity(void); //C Sub glLoadIdentity() ‘VB 현재 행렬의 16개 값을 m에 대입하는 함수 void glLoadMatrix{fd}(const유형*m); //C Sub glLoadMatrix{fd}(m As 유형) ‘VB 현재의 행렬에 m을 곱한다. C를 현재의 행렬이라고 하며 Cm 이 된다. m은 당연히 스칼라 상수이다. void glMultMatrix{fd}(유형*m) ; //C Sub glMultMatrix{fd}(m As 유형) ‘VB
다양한 변환 모델링변환(Modeling Transformation)과 투영변환(Projection Transformation) 및 뷰포트 변환(Viewport Transformation)
관측 및 모델링 변환 (Viewing and Modeling Transformations) 모델링 변환 : 모델과 물체 자체를 조정하고 다루는 것. 모델뷰 : 원래의 좌표점 데이터와 모델뷰 행렬(Modelview matrix)을 곱하여 새롭게 만든 시계 좌표점(eye coordinate)을 의미한다. 모델링 변환 유형 : 이동(translation), 회전(rotation), 크기조절(scaling).
이동(translate)시키는 행렬 함수 현재의 행렬에 물체를 x, y, z 만큼 이동시키는 행렬을 곱한다. 주어진 x, y, z 로 평행 이동 행렬이 만들어져서 현재 행렬과 곱해진다. 주어진 값만큼 평행 이동한다. void glTranslate{fd}(유형 x, 유형 y, 유형z): //C Sub glTranslate{fd}(x As 유형, y As 유형, z As 유형) ‘VB
회전(rotation)시키는 행렬 함수 현재의 행렬에 물체를 회전시키는 행렬을 곱하며, 회전 각도는 degree 단위이고, 회전 방향은 반시계 방향이다. 회전축에 멀리 있는 객체가 가까이에 있는 객체보다 커다란 궤도로 회전 변환한다. void glRotate{fd}(유형 angle, 유형 x, 유형y, 유형z); //C Sub glRotate{fd}(angle As 유형, x As 유형, y As 유형, z As 유형) ‘VB glRotatef 45, 1, 1, 1 ‘x, y, z, 축으로 (1, 1, 1)을 갖는 벡터에 45도 반시계 방향으로 회전
크기변환(scaleing)시키는 행렬 함수 현재의 행렬에 물체를 확대, 축소, 반전시키는 행렬을 곱한다. 크기 변환 값이 1.0보다 크면 늘어나고, 1.0보다 작으면 줄어들고, 마이너스 값이면 축에 대하여 반사된 모습을 나타낸다. void glScale{fd}(유형 x, 유형y, 유형y); //C Sub glScale{fd}(x As 유형, y As 유형, z As 유형) ‘VB glScalef 2, 1, 2 ‘x,z축으로 두배 확대한다.
모델링 변환 순서 모델링 변환 행렬들은 그려지는 이미지 객체에 설정된 반대순서로 적용된다. 즉, 가장 최근에 지정된 변환이 가장 먼저 적용된다. (a)의 경우 glRotatef ‘회전 헹렬, 두 번째로 적용되는 변환 glTranslatef ‘이동 변환 행렬, 첫 번째로 적용되는 변환 .... ‘그리기 (b)의 경우 glTranslatef ‘이동 변환 행렬, 두 번째로 적용되는 변환 glRotatef ‘회전 헹렬, 첫 번째로 적용되는 변환 .... ‘그리기
관측 변환 관측자(viewer) 또는 카메라의 위치를 결정 관찰자의 위치가 원점에서 -Z축으로 내려다보는 경우 : 원점에 관측점(point of observation)이 위치하면 +Z축은 관찰자의 뒤쪽에 있게 되어 볼 수 없게 되고 Z축의 -쪽만 보이게 될 것이다. 관측 변환(viewing transformation)에 의해서 관측점(point of observation)의 위치와 어떤 방향으로 바라보는가를 원하는 대로 바꿀 수 있다. 관측 변환(viewing transformation)은 반드시 다른 어떤 변환(transformation)보다 먼저 지정되어야 한다. 왜냐하면 현재 작업중인 좌표계 시스템이 시계 좌표(eye coordinate)시스템에 대하여 이동하기 때문이다.
관측 변환 방법-1 관측 변환을 위해 눈(카메라)의 위치와, 방향을 설정(변환)하는 세가지 방법 : 1) 모델링 변환 명령의 사용 관측 변환은 앞의 모델링 변환 함수들을 사용하여 구현할 수 있다. 단지 물체를 축소시키기만 해도 시점을 뒤로 이동하는 것과 같은 효과를 볼 수 있다.
관측 변환 방법-2 2) gluLookAt() 함수의 사용. void gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, GLdouble centery, GLdouble centerz, GLdouble upx, GLdouble upy, GLdouble upz) ; //C Sub gluLookAt(eyex As GLdouble, eyey As GLdouble, eyez As GLdouble, centerx As GLdouble, centery As GLdouble, centerz As GLdouble, upx As GLdouble, upy As GLdouble, upz As GLdouble) ‘VB gluLookAt() 함수를 사용하지 않은 경우에는 다음과 같은 인자값을 가진 gluLookAt() 함수를 호출한 것과 같다. 즉 이 인자 값이 기본 관측점이 된다. 눈의 위치는(0,0,0)이고, -z 방향을 보고 있으며 y축 1만큼 되는 방향이 관측점의 위쪽 방향으로 설정된다. gluLookAt 0, 0, 0, 0, 0, -100, 0, 1, 0
관측 변환 방법-3 3) 사용자 정의 루틴 사용. 관측점을 변환시키도록 하는 좌표 값 행렬을 곱하는 방법으로 사용자가 임의로 작성하여 적용할 수 있다.
투영 변환(Projection Transformations) 투영은 실제로 시야에 들어오는 범위인 관측 볼륨(viewing volume)과 절단면(clipping plane) 설정으로 정의된다. 관측 볼륨 안에 들어 있는 객체들을 관측면(투영면) z=0인 평면에 그리는 것이다. 투영 종류 : 직교 투영 변환과 원근 투영 직교 투영(orthorgraphic projection) : 간단한 객체를 모델링 => 사람의 위치와 거리에 영향을 받지 않는 정사 투영 원근 투영(perspective projection) : 많은 물체가 여러 공간을 차지하거나 큰 물체가 보는 사람의 관점에 따라 다른 모양을 나타냄을 보여주려고 할 때 사용.
원근 투영(Perspective Projection) 가까운 물체는 크게, 먼 물체는 작게 보이게 하는 변환. void glFrustum(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble near,GLdouble far); //C Sub glFrustum(left As GLdouble, right As GLdouble, bottom As GLdouble, top As GLdouble, zNear As GLdouble, zFar As GLdouble) 'VB 인자 left는 near평면에서 x최소값이고, right는 x최대값이며, bottom은 y최소값이고, top은 y최대값이 된다. zNear와 zFar값 : 각각 near와 far에 해당되며 모두 양수 값으로 설정해야 하는데, z축을 기준으로 관측자 눈으로부터의 거리를 의미한다. 그리고 이 두면은 모두 z=0인 평면에 평행이며 깊이 버퍼(Z버퍼)에서 랜더링 할 z의 최소값과 최대값을 설정하는 것으로 보면 된다. 즉 z최소값보다 작거나 z최대값보다 크면 즉,너무 가깝거나 멀면 그 객체는 표현되지 않는다.
원근 투영(Perspective Projection)-2 void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear,GLdouble zFar); //C Sub gluPerspective(fovy As GLdouble, aspect As GLdouble, zNear As GLdouble, zFar As GLdouble) 'VB gluPerspective는 glFrustum과 비슷하지만 상하좌우의 값을 설정하는 대신에 y방향의 시선각도(화각)인 fovy(FOV는 Field Of View의 약자)와 종횡비인 aspect를 이용한다는 점이 다르다. 이때의 회전 각도는 관측 각도, 시야 각도로 생각하면 된다. fovy값이 커지면 객체의 크기가 줄어드는데, 시야가 넓어져서 한정된 공간에 그 모든 것을 표현하려면 각 객체의 크기를 줄여야 하는 원리이다. 또한 aspect값이 1이아닌 값이라면 객체가 좌우나 상하로 늘어나서 표현된다.
직교 투영(Orthographic Projection) 직교 투영에서는 점(x,y,z)를 점(x,y,0)로 투영한다. 직교 투영 행렬을 생성하여 현재의 행렬에 곱한다. 다른 변환이 없을 경우 투영 방향은 z축에 평행하게 된다. void glOrtho(GLdouble left,GLdouble right,GLdouble bottom,GLdouble up,GLdouble near,GLdouble far) ; //C void gluOrtho2D(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top); //C Sub glOrtho(left As GLdouble, right As GLdouble, bottom As GLdouble, top As GLdouble, zNear As GLdouble, zFar As GLdouble) 'VB Sub gluOrtho2D(left As GLdouble, right As GLdouble, bottom As GLdouble, top As GLdouble) 'VB gluOrtho2D 함수는 glOrtho의 near와 far를 각각 -1, 1로 정한 것이다. glOrtho –1,1,-1,1,-1,1 => 한 변의 길이가 2인 정육면체에 그림을 그리겠다고 선언
glOrtho 함수 표현되는 이미지는 z=0인 평면에 만들어지기 때문에 평면 z=0이 near와 far사이에 위치하면 이차원 평면은 관측 공간을 가로지르게 된다. 인자 left,right,top,bottom은 glFrustum과 동일하다. 만일 glOrtho -1,1,-1,1,-1,1라고 정의하면 한 변의 길이가 2인 정육면체에다가 그림을 그리겠다고 선언을 하게 되는 것이다.
(3) 뷰포트 변환(Viewport Transformation) 최종 이미지를 매핑할 윈도우에 픽셀 사각형을 정의한다. 2차원 화면 상에 우리가 그린 물체가 보일 2차원 사각 영역을 설정 윈도우 창에서 우리가 보고 싶은 영역까지만 출력할 수 있게 한다. void glViewport(GLint x, GLint y, GLint width, GLint height); //C Sub glViewport(x As GLint, y As GLint, width As GLsizei, height As GLsizei) 'VB x,y는 좌측 상단 모서리의 좌표값이며 wididth, height는 뷰포트 사각형의 크기이고 초기 뷰포트 값은 (0,0, 폼의Width, 폼의Height) 이다. VisualBasic에서는 폼의 scaleMode 속성에서 정한 단위로 초기의 폼의 높이와 너비간의 비율을 유지하는 scaleWidth와 scaleHeight 속성 값을 glViewport함수의 인자로 설정하여 폼의 크기가 변하더라도 초기의 뷰포트 비율을 그대로 유지하도록 하는 것이 일반적인 방법이다.
행렬 스택의 관리 스택 : last-in first-out 형태의 자료구조 OpenGL은 행렬 스택을 이용하여 현재의 행렬을 보존하고, 필요한 변환 작업을 수행 후, 스택에 보존되었던 행렬을 꺼내어 원래의 좌표계로 복원. void glPushMatrix(void); //C Void glPopMatrix(void); //C Sub glPushMatrix( ) 'VB Sub glPopMatrix( ) 'VB Push : 스택에 이전 행렬을 집어 넣는다. Pop : 스택 최상위에 있는 행렬을 꺼낸다.
클리핑 평면 클리핑 평면 : 잘라내는 면을 뜻하며, 대상 물체에 클리핑 평면을 지정하면 평면의 밖에 있는 부분이 잘려나간다. void glClipPlane(Glenum plane, const Gldouble *equation); //C Sub glClipPlane(plane As glClipPlaneConstants, equation As GLdouble) 'VB 평면의 방정식이 Ax+By+Cz = 0 이라고 할 때, Ax*+By*+Cz* >= 0인 점들만이 표시된다. plane 인자는 클리핑 평면을 지정하는 인덱스로서 cpClipPlanei의 값을 갖는다. 여기서 i = 0 , 1, 2, …. 로서 몇 번째 평면인 가를 나타낸다. equation 인자는 평면을 정의하는 A, B, C, D 값을 가지, 배열로서 표현된다.