본문 바로가기

Programming/OpenGL

OpenGL primitives_2

저번 포스팅에 이은 글이므로 설명 또한 다소 이어질 수 있으므로 참고 하세요
OpenGL primitives_1

  • Drawing Polygons in 3D(3차원상에 폴리곤 그리기)
    앞서 우리는 기본적으로 점과 선을 그리는 방법에 대해서 알아 봤습니다. 하지만 폴리곤으로 만드는 것이 더욱 강력한 3차원 세계를 만들 수 있다는 것에는 의심의 여지가 없습니다.

    폴리곤이 뭔지 짚고 넘어가야 겠죠? 폴리곤은 다각형을 의미합니다. "면"이 존재해야 하는 것이죠. 보통은 삼각형이지만 경우에 따라선 사각형과 오각형 또한 나올 수 있지만 이 역시 삼각형으로 부터 만드는게 가능하죠. 아래 처럼~

    glBegin(..)의 인자 중에서 이러한 폴리곤을 만드는 인자들이 있었죠.
    	GL_TRIANGLES		// Single triangles
    	GL_TRIANGLE_STRIP	// Series of connected triangles
    	GL_TRIANGLE_FAN		// Set of triangles containing a common central vertex
    	GL_QUADS			// Quadrilaterals
    	GL_QUAD_STRIP		// Series of connected quadrilaterals
    	GL_POLYGON			// Polygon with an arbitrary number of vertices
    	


    폴리곤을 단순히 그리기만 하는 것은 선을 그리는 것과 마찮가지 이지만, 일단 가장 간단한 삼각형을 그려보는 것으로 기본적인 내용을 설명을 할게요 :-D
    • Triangle which basic in polygon
      glBegin(..)에 인자로 넣어주면 됩니다. 삼각형 이기 때문에 3개의 좌표가 필요하겠죠? 저는 약간의 확인을 위해 키 입력으로 몇 개까지 보일 것인지를 만들었지만 여기에 적는 코드는 단순 확인용으로 적는 것임을 먼저 알려 드릴게요.

      일단 좌표 평면상에서의 그리고 싶은 삼각형들의 좌표 에요.

       
    점들을 그릴 때도 설명했지만, 연이어 좌표를 작성해도 무관합니다. 어차피 내부적으로는 3개씩 끊어서 인식하니까요.(삼각형이기 때문에 3개씩 끊는 거에요 :-D)
    	glBegin(GL_TRIANGLES);
    		glVertex2f(2.0, 3.0);
    		glVertex2f(1.0, 1.0);
    		glVertex2f(3.0, 1.0);
    		
    		glVertex2f(-2.0, 0.0);
    		glVertex2f(-1.0, -2.0);
    		glVertex2f(-3.0, -2.0);
    	glEnd();
    	

    코드를 보시면 삼각형 두 개의 좌표를 던져주는 방법이 다릅니다. 아래 그림 처럼요.

    그리고 이 코드는 한 줄씩 점점 보이게 할 거에요. 뭔소리 인고 하니, 위에서 6개의 점들이 연이어 있죠?
    처음에는 (2, 3)만,
    두 번째에는 (2, 3)과 (1, 1)만,
    세 번째는 (2, 3), (1, 1), (3, 1) 만,...

    그림으로 스크롤 압박이 있으니까, 결과는 보고 싶으신 분만 :-D

    결과는 세 개의 짝으로 정보를 넘겨주지 않으면, 절대 그리지 않는 다는 거죠. 반드시 세 개씩! 그럼 사각형을 그리는 것 또한 정보를 네 개씩 줘야 하겠죠?

    • Face(면)
      앞서 좌표의 순번을 달리한 것을 알려드렸는데, '점의 정보 6개를 다 주었음에도 좌표 순번이랑은 차이가 없는 결과네요?' 라고 하는 질문에는 이제 부터 설명 할 폴리곤 모드가 그 해답이 될 거에요. 3차원 이기 때문에 표현 할 수 있는 것이 있습니다.

      바로 "앞/뒤".

      OpenGL은 상태기계이기 때문에 설정된 상태에 따라서 행동을 하게 되고 폴리곤에 대한 것 또한 "상태"중에 하나가 되는 것입니다. 이를 설정할 수 있는 것이 glPolygonMode(..)입니다.

      함수는 아래와 같습니다.
      • 함수 원형
        void glPolygonMode(GLenum face, GLenum mode);
        			
           
      • 인자
        face : 어떤 면의 상태를 바꿀 것인지 정합니다. GL_FRONT, GL_BACK, GL_FRONT_AND_BACK 중 하나를 인자로 넘길 수 있습니다. 기본적으로 앞/뒤 면이 전부 적용된 GL_FRONT_AND_BACK으로 설정 되어 있습니다.
        mode : face에 대해서 표현할 방법을 설정합니다. 아래의 값들 중 하나를 인자로 넘길 수 있습니다.
        • GL_POINT : 점의 정보만을 사용해서 점의 정보를 표현합니다. 앞서 삼각형에 대해서 예시를 들자면, 세 개의 점이 없다면 애초에 아무것도 표현하지 않게 됩니다.
        • GL_LINE : 선만을 이용해서 표현하게 됩니다.
        • GL_FILL : 정점에 설정된 색상을 이용해서 면의 색을 칠하게 됩니다. 기본적으로 폴리곤 모드는 색을 칠하는 것으로 설정이 되어 있습니다.(색은 이후에 자세히 설명하게 되며, 현재는 기본값으로 설정된 하얀색이 나오게 됩니다.)
      • 반환값
        void
    • 앞면과 뒷면의 효과를 달리 해 볼게요. 코드는 아래와 같습니다. 앞면은 색을 칠하고, 뒷면은 색칠 대신에 선만을 그려서 표현하라는 내용에요.
      		glPolygonMode(GL_FRONT, GL_FILL);
      		glPolygonMode(GL_BACK, GL_LINE);
      		

      결과는 아래와 같습니다.
    • Which is Front Face?
      반 시계(CCW)방향을 정면(front-face)로 인식하는 군요. :-D 여러 번 강조해도 지나침이 없는 말이기 때문에 한번 더 할게요. OpenGL은 상태기계 에요. 그럼 정면이 무엇인지 판단하는 상태는 없을 까요? 있죠 :)
         
      바로 아래 함수 입니다.
      • 함수 원형
        			void glFrontFace(GLenum mode);
        			

      • 인자
        mode : 방향성을 정하게 됩니다. 아래의 값들 중 하나를 인자로 넘길 수 있습니다.
        • GL_CW : 시계방향을 정면으로 설정 합니다.
        • GL_CCW : 반시계방향을 정면으로 설정합니다. 기본적으로 이 값이 설정 되어 있습니다.
      • 반환 값
        void

      그럼, 정면이 시계방향이 되도록 바꿔 볼까요?
      		glFrontFace(GL_CW);
      		

         
      위에서 정면은 색을 칠한 것으로, 뒷면은 선만을 그리는 설정은 그대로 두었어요. 결과는 아래와 같습니다.


      폴리곤 모드의 옵션에서 점이나 선으로 설정 했을 때는 선과 점에 대해서 설정되는 값들의 영향을 받게 됩니다. (상태 기계...ㅋㅋㅋ) 전 포스팅에 관련된 내용이 있으니까 이 부분은 직접 해보는 것으로 하고 넘어갈게요:-D
    • Polygon Face Culling(폴리곤 표면 제한)
      OpenGL 내부에서는 상당히 많은 일들이 일어납니다. 아직 클리핑 영역에 대해서는 설명을 한 적이 없는데, 간단히 이야기 하자면 그 영역 안에 들어오지 않은 Object는 계산하지 않게 됩니다. 하지만 내부에 있는 Object는 어떤 것이든 계산을 하게 되는데 여기서 짚고 넘어가야 할 것은 '앞면 때문에 가려진 부분의 뒷면도 보아야 하나?' 입니다.

      물론 보여지는 것 자체에는 상관이 없습니다. 카메라가 바라보고 있는 부분에 대해서 무엇이 앞이고 무엇이 뒤인지 계산해서 최종적으로는 가장 앞에 있는 것을 보여주는 거니까요. 하지만 내부적으로 계산을 하는 것과 계산을 하지 않는 것에는 상당한 차이가 있겠죠?

      표면 제한을 거는 것은 아래처럼 활성화 시켜야 합니다.
      		glEnable(GL_CULL_FACE);
      		


      그럼 어떤 면을 제한을 걸어줘야 하는지 알려줘야겠죠? 그건 glCullFace(..)함수로 설정 하게 됩니다.
      • 함수 원형
        			void glCullFace(GLenum mode);
        			


      • 인자
        mode : 어떠한 면을 제한 할 것인지 정합니다. 아래의 값들 중 하나를 넣을 수 있습니다.
        • GL_FRONT : 앞면을 제한 합니다.
        • GL_BACK : 뒷면을 제한 합니다. 기본적으로 설정 되어 있는 값입니다.
        • GL_FRONT_AND_BACK : 앞면과 뒷면 모두 제한 합니다.
      • 반환 값
        void
         
      단순히 표면제한 활성화만 시키고 실행해 보면 뒷면이 나오지 않는 것을 볼 수 있습니다.(앞선 예시 중 glFrontFace()는 코드에 없습니다. 즉, 반시계가 앞면이 되는 것이죠.)


      다른 값은 직접 입력해서 확인 해 보세요 :-D
    • Hiding Polygon Edges(폴리곤 선 감추기)
      실제로 이게 많이 사용될 일이 있는가 싶기는 한데, 사용법이 생각보다 불편 하더라구요.(제가 아직 제대로 된 사용법을 모르기 때문일 수도 있지만 :-D) 일단, 색을 칠하지 않고, 폴리곤만 보이게 하는 것은 많이 사용하게 됩니다. 물체의 외형을 전체적으로 확인 할 수 있기도 하고 색을 칠하지 않게 되는 것만으로도 속도에서 엄청난 향상을 불러오게 됩니다. 면만을 보이게 하는 것은 앞서 설명한 내용에 많이 있어요~

      이 설정은 glEdgeFlag(..)라는 함수로 설정하게 되며, 아래는 함수에 설명입니다.
      • 함수 원형
        void glEdgeFlag(GLboolean isEdge);
        			void glEdgeFlag(const GLboolean *isEdge);
        			


        두 번째 나오는 함수는 왜 있는지 모르겠어요;;(저자 또한 그렇게 설명:-D) 그런고로 첫 번째 함수만 사용 한다는 전재로 설명 할게요.
         
      • 인자
        isEdge : 다음으로 나오는 빗변을 표시 할 것인지, 하지 않을 것인지에 대한 값입니다. 아래의 값들 중 하나를 넣을 수 있습니다.
        • GL_TRUE : 빗변을 표시 할 때. 기본 값 이므로 별다른 설정이 없다면, 폴리곤의 모든 면은 무조건 표현되게 된다.
        • GL_FALSE : 빗변을 표시 하지 않을 때
      • 반환 값
        void
         
      앞서 설명한 바와 같이 이게 많이 사용 될 일이 있는가 싶기는 한 게, 이 서적에 관한 예제 파일이 없기 때문에 구글링으로 예제를 뒤져봤지만, 별다른 예제를 발견 하지 못했고, 있는 것을 보았을 때도 사용이 불편한 예시이기 때문에 그다지 였습니다.
         
      사각형을 그리는 것이 따로 있긴 하지만, 실제로 한가지만 가지고 사용을 해야 중간에 별다른 코드를 삽입하지 않아도 되기 때문에 삼각형만을 가지고 특정한 물체를 표현하게 됩니다. 일단 삼각형 두 개를 이용해서 사각형 하나를 그려보겠습니다. 그리고자 하는 삼각형은 아래와 같아요


      코드는 아래와 같겠죠?
      		glBegin(GL_TRIANGLES);
      			glVertex2f(1.0, 1.0);
      			glVertex2f(-1.0, 1.0);
      			glVertex2f(1.0, -1.0);
      
      			glVertex2f(-1.0, -1.0);
      			glVertex2f(1.0, -1.0);
      			glVertex2f(-1.0, 1.0);
      		glEnd();
      		

         
      결과는 뭐... 생각했던 대로 나오네요 :-D 아, 물론 색상은 패스하고 선만 그렸던 거에요. 지금은 그것에 관련된 거만 하는 거니까ㅎ


      그럼 사각형 하나만 표현하고 싶은데, 가운데 저 선은 어떻게 못하나 T^T 할 때 저 함수를 사용 하는 겁니다. 앞으로 표현하기 싫은 부분에서 glEdgeFlag(GL_FALSE);를 다시 보여야 할 때는 glEdgeFlag(GL_TRUE)로 돌려놔야 제대로 나오게 됩니다.
       
      그럼 일단 우리가 원하는 것을 도식화 해볼까요?


      그럼 사각형만 나오겠죠ㅎ 하지만 우리가 가지고 있는 것은 점의 좌표 뿐이잖아요? 선은 두 개의 점이 필요한 것이구요~ 선분은 그려지기 좌표가 찍힐 때마다 자신의 edge flag를 확인하게 됩니다.
         
      첫 번째 삼각형에 대해서만 나름... 재밌게 표현 해볼게요.


      위의 방식으로 해서 선분을 그리지 않는 답니다. 그런 고로, 밑의 삼각형 중에서도 가운데 면은 그리지 않으면 되겠죠? 어디에 들어가면 되는지 맞춰 보아요 :-D


    • Antialiasing Polygons(폴리곤 엘리어싱 제거)
      플리곤 안티엘리어싱 역시 상태중에 하나 입니다. 그런 고로 확인이 가능 한데, 아래와 같은 방식으로 확인이 가능 하답니다.
      		GLboolean is_polygon_smooth;
      		glGetBooleanv(GL_POLYGON_SMOOTH, &is_polygon_smooth);
      		is_polygon_smooth = glIsEnabled(GL_POLYGON_SMOOTH);
      		


      적용은 glEnable()로 적용 취소는 glDisable()로 하게 되요.
      		glEnable(GL_POLYGON_SMOOTH);
      		glDisable(GL_POLYGON_SMOOTH);
      		


      서적에서는 별다른 언급 없이 넘어갔네요 :-D 전 이거적느라 힘 빠져서..ㅠㅠ 패스요..
       
    • Specifying a Stipple Pattern(특별한 패턴)
      이전에 선의 패턴을 적용 시킨 것처럼, 면에도 패턴을 적용 할 수 있어요. 하지만 면이기 때문에 단순히 변수 하나로는 안되고, 배열로 던져줘야 해요. 패턴 적용과 취소는 아래 처럼 ON/OFF 할 수 있습니다.
      		glEnable(GL_POLYGON_STIPPLE);
      		glDisable(GL_POLYGON_STIPPLE);
      		

         
      위에서 폴리곤 패턴 적용을 시키는 것을 설정 했다면 아래의 함수를 사용해서 적용할 패턴을 넘겨 주게 되요.
      • 함수 원형
        			void GlPolygonStipple(const GLubyte *mask);
        			

      • 인자
        mask : 32x32 비트를 만드는 Byte 배열을 던져야 합니다. 아래 그림을 보시면 이해가 빠를 거 같아요.


        위의 그림을 보시면 아시겠지만, 비트수로 치면 32x32개의 비트가 모여 있어요. 우리가 인자로 넘길 배열이 기본적으로 Byte로 이루어진 건데, Byte는 8개 비트로 이루어 져있잖아요? 그럼 배열은 128개를 던져야 겠죠? (흑흑...ㅠㅠ)

        그래서 예제 샘플 코드 또한 출처 사이트에서 가져왔어요.(해맑! :-D)
        			GLubyte fly[] = {
        				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        				0x03, 0x80, 0x01, 0xC0, 0x06, 0xC0, 0x03, 0x60, 
        				0x04, 0x60, 0x06, 0x20, 0x04, 0x30, 0x0C, 0x20, 
        				0x04, 0x18, 0x18, 0x20, 0x04, 0x0C, 0x30, 0x20,
        				0x04, 0x06, 0x60, 0x20, 0x44, 0x03, 0xC0, 0x22, 
        				0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22, 
        				0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
        				0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22, 
        				0x66, 0x01, 0x80, 0x66, 0x33, 0x01, 0x80, 0xCC, 
        				0x19, 0x81, 0x81, 0x98, 0x0C, 0xC1, 0x83, 0x30,
        				0x07, 0xe1, 0x87, 0xe0, 0x03, 0x3f, 0xfc, 0xc0, 
        				0x03, 0x31, 0x8c, 0xc0, 0x03, 0x33, 0xcc, 0xc0, 
        				0x06, 0x64, 0x26, 0x60, 0x0c, 0xcc, 0x33, 0x30,
        				0x18, 0xcc, 0x33, 0x18, 0x10, 0xc4, 0x23, 0x08, 
        				0x10, 0x63, 0xC6, 0x08, 0x10, 0x30, 0x0c, 0x08, 
        			0x10, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x08};
        			


        하지만, 패턴은 특정한 정면을 바라보는 2D환경에서 그럴싸하게 볼 수 있지, 다른 비틀지면 우리가 생각하는 결과를 볼 수 없어요. 바로 아래 처럼.. 패턴을 적용시킬 폴리곤은 바로 위에서 만들었던 사각형을 사용 했습니다.


        참조 그림 및 샘플 코드 출처 : http://fly.cc.fer.hr/~unreal/theredbook/chapter02.html
      • 반환 값
        void

         
    이제 glBegin(..)에 들어가는 다양한 것들에 대해서 알아볼까요? :-D(이거 생각보다 양이 많네요 헉헉;;)
    • Triangles(삼각형)
      삼각형이 폴리곤에서 가장 "기본"이 되는 이유는 아래와 같습니다.
      • 삼각형의 점들은 언제나 같은 평면상에 존재 한다.
      • 오목한 부분이 없다.(어디를 봐도 뾰족하죠?)
      • 비틀어 지지 않는다.(이것은 첫 이유와도 일맥 상통하죠. 모든 점들은 같은 평면위에 있을 수 밖에 없으니까:-D)
      기본적인 것은 위에서 기본적인 폴리곤 설명을 위해서 많이 사용 했으니까, 다른 두 개의 차이점이 뭔지 알아 보는 걸로 할게요.
         
      삼각형을 그리기 위해서 단순히 glBegin(..)의 인자로 GL_TRIANGLES를 넣는다면 우리는 두개의 삼각형을 그리기 위해 총 6개의 점이 필요 하게 되죠. 뭐.. 위에 보인 예시처럼 두개의 삼각형이 서로 떨어져 있다면 어쩔 수 없지만, 붙어 있는 삼각형이라면 이전에 나온 점을 활용 하는 방법으로 제시 하는 거에요.
       
      • GL_TRIANGLE_STRIP
        먼저 나온 두 개의 점으로 부터 나오는 삼각형을 만들게 됩니다. 뭐 수학적으로 삼각형 면 하나에 몇 개의 정점을 절약 할 수 있나 는 넘어 갈게요. 그럼 일단 좌표 평면 상에서 표현 해 볼까요?


        앞에 나온 두 개의 점의 정보를 이용하게 되는 것이고, 기본적으로 그려지는 게 반시계로 돌아가니까, (-3, -1)부터 돌아가면 되겠죠? 그럼 코드는 아래와 같이 됩니다. 앞면을 확인 하기 위해서 폴리곤 모드에 정면, 선분 긋기로 설정해 두었습니다.
        			glPolygonMode(GL_FRONT, GL_LINE);
        			glBegin(GL_TRIANGLE_STRIP);
        				glVertex2f(-3.0, -1.0);
        				glVertex2f(-1.0, -1.0);
        				glVertex2f(-2.0, 1.0);
        				glVertex2f(0.0, 1.0);
        				glVertex2f(1.0, -1.0);
        			glEnd();
        			

           
        결과는 아래와 같이 나오네요.


        어랏? 세번째 삼각형이 뒷면이 나오네요? 두 번째 삼각형은 앞면이 나왔는데,,,
        처음 나오게 되는 삼각형의 면이 앞면인지 뒷면인지에 따라서 바로 뒤에 나오는 삼각형의 면을 결정하게 됩니다. 뒤집혀 지는 것은 바로 전에 나온 점의 정보들을 이용해서 계산이 되어요 :-D

      • GL_TRIANGLE_FAN
        다른 점들을 이용하는데, 이 방법은 제일 처음에 나온 점과 그 전에 나온 점의 정보를 이용하게 됩니다. 가령 위의 예시와 같은 그림을 만들고 싶으면, (-2, 1)부터 시작해서 반시계방향으로 점의 정보를 쓰면 되겠죠?

        			glBegin(GL_TRIANGLE_FAN);
        				glVertex2f(-2.0, 1.0);
        				glVertex2f(-3.0, -1.0);
        				glVertex2f(-1.0, -1.0);
        				glVertex2f(0.0, 1.0);
        				glVertex2f(1.0, -1.0);
        			glEnd();
        			


        뭐 결론적으로는 같은 그림이 나오게 되지만, 차이점이 뭔지는 알고 있으라는 거죠. :-D


    • Quadrilaterals & Polygons(사각형과 다각형)
      사각형과 다각형은 가볍게 설명하고 넘어 갈게요. 사실 삼각형 말고는 잘 사용하지 않고, 실제로 우리가 이렇게 여러가지 도형을 이용해서 마구마구 그리는 일보다는 max나 마야같은 것으로 만들어진 Object를 이용해서 표현하게 되니까요.

      일단 사각형은 두 가지가 있네요. GL_QUADS와 GL_QUAD_STRIP.
         
      GL_QUADS는 사각형 각 하나당 가지고 있는 네 개의 정점에 대해서 계속 써주어야 합니다.
      GL_QUAD_STRIP은 GL_TRIANGLE_STRIP처럼 앞서 나온 두 개의 정점을 연이어 재활용 해서 사각형을 그리게 되는 거구요.(자세한 설명은 생략 :-D)

      폴리곤은 단순히 GL_POLYGON으로 그리려는 모든 정점의 정보에 대해서 언급해야 합니다.
         
참조 : OPENGL GAME PROGRAMMING(Foreword by Mark J.Kilgard)

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

OpenGL Transformations_Viewing(Camera)  (0) 2012.03.15
Understanding Coordiante Transformations  (0) 2012.03.13
OpenGL primitives_1  (0) 2011.12.22
OpenGL states  (0) 2011.12.22
Basic OpenGL programming Full-Screen  (0) 2011.12.12