본문 바로가기

Programming/OpenGL

OpenGL Transformations_Projection

프로젝션이라 함은 보통 같거나 낮은 차원으로 해당 좌표가 어디에 매칭이 되는지에 대한 짝을 만들어 내는 과정이라고 볼 수 있어요. 


음… :) 말이 좀 어려운가요?ㅋㅋ

우리가 현재 작업하는 내용은 3차원 상에서 이루어 지는 내용인데 반해서, 보게 되는 화면은 2차원(모니터) 가 되는 거에요. 모니터 상에는 내가 이루어 놓은 3차원 세계가 어떻게 보이게 되나-

예전 포스팅에서도 몇 번(특히나 코드 내에서) 언급 되긴 했지만 어떻게 동작하는지, 시키는지에 대해서 중점적으로 보려고 합니다 :D

물론, 아름답게 함수로 이루어져 있기 때문에 우리는 불러다 쓰면 되긴 하지만 결과가 어떻게 나오는지 알아야 불러다 쓰잖아요~ :D


프로젝션 변환을 설정 하는 것은 바로 뷰잉 볼륨(Viewing Volume)이라는 것을 만드는 거에요. 과정은 아래의 두 가지를 하면 됩니다.
  1. 클리핑 영역(Clipping planes)을 설정 합니다.
    서적에서는 '스치듯 안녕' 으로 가볍게 넘어갔지만 저는 설명을 좀 더 붙일까 해요 :D


    클리핑 영역이라 함은 실질적으로 그래픽 카드한테 보내 줄 정점의 정보들이 있는 곳을 전달해 주는 거에요.

    실제 모든 시야가 그렇듯 볼 수 있는 영역은 한정 되어 있잖아요? 특별히 컴퓨터의 경우에는 "처리"라는 과정이 들어가는데 보이지 않는 부분에 대해서 "처리"하면서 일을 시킬 필요는 없으니까(처리 한다고 해도 어차피 안보이니까 아예 안 시키는 거죠:D) 그 영역을 지정 해 주는 거에요.

    예시를 위해서 2차원 상에서 작업을 한다고 가정 할게요.


    이 예시는 실제 모니터에 출력될 화면과 동일 하다고 할게요.(윈도우의 크기까지 같다는 전제 에요 :D) 오브젝트는 세 개의 정점으로 이루어진 폴리곤 이구요~ 오브젝트를 움직이면서 클리핑 영역 밖으로 이동 했다고 했을 때는 아래와 같은 처리가 이루어 집니다.


    위에서 보다시피 클리핑 영역 내에 임의의 오브젝트의 정점이 하나라도 포함 되어 있다면 OpenGL은 해당 오브젝트를 그려야 할 모든 정보를 그래픽카드로 넘겨주게 되죠. 포함 되지 않는다면 해당 오브젝트는 그려지지 않아요.(그만큼 "처리" 하게 되는 양이 적어 지는 거죠)
    이게 현재 실무에서 실제 쓰이는 것인지는 모르겠지만, 제가 현재 하고 있는 건 커다란 오브젝트 하나를 표현하기 위해서 자를 수 있는 가장 작은 부위까지 나눠서 관리 하는 방법이에요.

    앞선 포스팅에서 로봇 예제를 보았을 때 저는 Robot 오브젝트 하나만 컨트롤 하지만, 실제로 Robot 오브젝트는 Head, Body, LeftArm, RightArm, LeftLeg, RightLeg 라는 각기 최소한으로 표현되는 오브젝트들을 가지고 있는 거거든요. 클리핑 뷰어안에 왼팔만 살짝 보인다고 한다면, LeftArm 오브젝트 외에 다른 오브젝트는 애초에 그리지 않게 되는 거죠.

    위의 방식은 OpenGL이 알아서 해주는 거라고 알고 있는데(아니면 어떡하지@_@;;;) 저 방법 말고 실제로 커다란 오브젝트에 가려서 뒤쪽의 오브젝트가 하나의 정점도 실제 모니터에 출력될 화면에 포함이 안되는 경우가 있는데, 이 과정을 OpenGL은 해주지 않는 다고 알고 있거든요. 이걸 프로그래머가 미리 처리해서 그리지 않도록 하는 방법이 있어요. 어떠한 방법으로 하는지는 정확히 모르지만, 그렇게 처리를 한다면 실제로 화면에 출력되는 연산자체를 안하게 되겠죠?


  2. 오브젝트를 어떻게 그릴지 정합니다. 정확한 의미로 그린다라고 하기 보다는 뷰볼륨을 만든다고 보는게 맞는 의미 일거에요. 그 의미는 밑에서 설명 할게요 :) OpenGL에서는 두 가지의 방법을 제공하는데, 일단 예시를 본 후에 제공하는 방법에 대해서 보도록 하죠.

    저번 포스팅에서 만들었던 정육면체로 진행 할 거에요. (0, 0, 0)에서 부터 (-1, -1, -1)로 가는 정육면체 이구요. 이런 상자가 세 개가 있고, 각기 다른 색상과 위치를 가지고 있어요.

    • 카메라
      (-3, 3, -6)의 위치에서 z축 양의 방향을 바라보고 있는 상태.

    • 첫 번째 상자
      빨강색. 아무런 변환을 취하지 않은 상태

    • 두 번째 상자
      초록색. 빨강색 상자의 위치에서 (-3, 0, 3) 만큼 이동한 상태

    • 세 번째 상자
      파랑색. 빨강색 상자의 위치에서 (-6, 0, 6) 만큼 이동한 상태

    바로 아래와 같은 상태를 나타내는 거지요.


    일단, 알아 놓아야 하는 게 위의 그림은 우리가 카메라를 바라보는 상태잖아요? 화면은 카메라가 바라보고 있는 장소에 있는 것들이 보이는 거니까 공간 지각 능력을 살포시 발휘해 보았을 때, 어떻게 보이는 지 상상 되겠죠? :D


    상자를 그리는 부분은 이전 코드를 사용 할게요. 이동에 관련된 부분은 이전 포스팅을 참고 하면서 스스로 해 보아요 :D
    void DrawCubeS()
    // 지난 포스팅 참조
    {
    	// TODO : push & pop으로 행렬을 묶어야 해요 :D
    	DrawCube(color1);
    	
    	// TODO : 여기서 두 번째 상자 변환
    	DrawCube(color2);
    	
    	// TODO : 여기서 세 번째 상자 변환
    	DrawCube(color3);
    }
    
    void DisplayScene()
    {
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	glEnable(GL_DEPTH_TEST);
    	
    	// TODO 밑에서 설명 할거에요
    	// 프로젝션 관련 설정!
    
    	// 역시 지지지난 포스팅 참조(카메라편)
    	gluLookAt( /* TODO : 카메라 위치, 카메라 바라보는 곳, 카메라 머리 방향 */ );
    
    	DrawCubes();
    	glFlush();
    }


    제가 실제로 사용하는 코드는 서적에서 제공하는 걸 랩핑해 놔서 가독성은 좋으나 지금 공부하는 데에는 하나도 안좋아서;; 실질적으로 내부에서 구현한걸 부분적으로 발췌 할게요. 오브젝트를 그리는 부분에 대해서는 올려 놓지 않을 거에요.

    chapter2에서의 기본 윈도우 프로그래밍 코드에 삽입 하시면 되요. :D 정 모르시겠으면 그때에 댓글ㅎ


    1. Orthographic
      원근법이 전혀 적용되지 않는 카메라가 보고 있는 화면에 대해서 보이는 오브젝트는 자신의 절대적인 크기로 보여지게 되는 거에요. CAD에서 주로 쓰이는 거죠:)

      사용은 아래의 함수를 사용 하면 되요.
      • 함수 원형
        glOrtho(
        	GLdouble left, GLdouble right, 
        	GLdouble bottom, GLdouble top
        	GLdouble near, GLdouble far);


      • 인자
        각각 뷰 볼륨(view volume)의 영역을 지정한다.
        left : 왼쪽 까지의 거리
        right : 오른쪽 까지의 거리
        bottom : 아래쪽 까지의 거리
        top : 위쪽 까지의 거리
        near : 카메라로 부터 뷰볼륨의 앞쪽 까지의 거리
        far : 카메라로 부터 뷰볼륨의 뒤쪽 까지의 거리

      • 반환 값
        void

      • 처리
        인자에 의한 뷰 볼륨을 만들어서, 최종적인 투영 행렬을 만든다.


        초록색은 이해를 돕기 위한 상자 에요 :D 검은색 상자가 뷰 볼륨 이자, 저 내부에 있는 오브젝트들이 실제로 화면에 보여질 녀석(?) 들이지요~


      프로젝션 관련 설정에 아래의 코드를 삽입하면 orth 투영이 된 결과를 볼 수 있는 거죠
      glOrtho(-10.0, 10.0, -10.0, 10.0, 0.5, 1000.0);



    2. Perspective
      원근법을 적용 시켜, 멀리 있는 오브젝트 일수록 작게 보여지는 형태에요. 실제로 게임에서는 이러한 방법을 많이 쓸 수 밖에 없겠죠?ㅎ(딱히 스크롤류 게임이 아니면)

      사용은 아래의 함수를 사용 하면 되요.
      • 함수 원형
        void glFrustum(
        	GLdouble left, GLdouble right, GLdouble bottom, GLdouble top,
        	GLdouble near, GLdouble far);


      • 인자
        near, far : 뷰볼륨의 앞과 뒤 거리
        left, right, bottom, top : 뷰볼륨 전면 영역의 거리(카메라의 중심 지점으로 부터)

      • 반환 값
        void

      • 처리
        인자에 의한 뷰 볼륨을 만들어서, 최종적인 투영 행렬을 만든다.


        빨강색 면부터 검정색으로 이루어진 뷰 볼륨을 만드는 거에요. left, right, top, bottom은 near에 생길 면이 카메라의 중심부로 부터 얼마나 떨어져 있냐를 가지고 만든 답니다. 그렇게 만들어진 사각형이 카메라로 부터 뻗어져 나가서 far의 거리에 면을 만들 수 있겠죠? 그렇게 만들어진 육면체가 뷰 볼륨 에요.

      프로젝션 관련 설정에 아래의 코드를 삽입하면 orth 투영이 된 결과를 볼 수 있는 거죠
      glFrustum(-0.5, 0.5, -0.5, 0.5, 0.5, 100.0);



      그런대… 이 방법이 사실 값을 입력으로 어떻게 보이는가 확인하기 어려워서 각도를 가지고 원근투영을 조절 할 수 있도록 만들어진 함수가 있어요.
      • 함수 원형
        void gluPerspective(
        	GLdouble fovy, GLdouble aspect, 
        	GLdouble near, GLdouble far);
        						

      • 인자
        near, far : 카메라로부터 뷰볼륨의 앞과 뒤의 거리
        fovy : 시야각
        aspect : 화면 비율(너비/높이)

      • 반환 값
        void

      • 처리
        인자에 의한 뷰 볼륨을 만들어서, 최종적인 투영 행렬을 만든다.


        (역시 3차원 자료를 만드는건 어려워요 -_-ㅋㅋㅋ)바로 전에 만들었던 뷰볼륨의 near 평면 부터 그 너비를 시야각(fovy)과 화면의 비율(aspect)로 부터 만들게 되요. 그렇게 되면 그에 연장선을 far 거리까지 가게 되면 뷰볼륨 육면체를 만들 수 있겠죠?

      glFrustum을 gluPerspective로 바꿔보기

참조 : OPENGL GAME PROGRAMMING(Foreword by Mark J.Kilgard)

'Programming > OpenGL' 카테고리의 다른 글

OpenGL Transformations_Using Own Matrix  (2) 2012.04.16
OpenGL Transformation_Matrix  (4) 2012.03.22
OpenGL Transformations_Modeling  (0) 2012.03.17
OpenGL Transformations_Viewing(Camera)  (0) 2012.03.15
Understanding Coordiante Transformations  (0) 2012.03.13