[OpenGL Tutorial] Output String on the Screen(Outline)

사용자 삽입 이미지지난장에서는 비트맵으로 문자를 출력했었다. 이번에는 문자를 객체로 처리하는 방법인 Outline Font에 대해서 알아보도록 한다. 이 장의 시작은 7장의 소스 코드에서 시작한다.

전역 변수를 2개 추가하자. Outline Font로써 문자를 처리하면 일반 객체처럼 회전이 가능하다. 이를 확인해 보기 위해 문자를 회전시키는데 사용하는 변수 하나와 Outline Font 문자들에 대한 정보를 저장하는 변수 변수인 gmf을 선언한다. 배열이 96개인 이유는 ASCII의 코드중에서 숫자, 영문자와 특수문자에 대해 사용할 수 있도록 하기 위함이다.

GLuint base;
GLfloat rot = 0.0f; // 
GLYPHMETRICSFLOAT gmf[96]; // 

이제 해야 할 일은 모든 문자들을 Outline Font로써 리스트에 등록 저장하는 것이다. 즉, OpenGL에서 사용할수 있도록 폰트의 리스트를 만들어야 한다. 다음의 코드가 폰트의 리스트를 생성하는 코드이다.

GLvoid BuildFont(GLvoid)
{
    HFONT font;
    base = glGenLists(96);
    font = CreateFont(-12,
                0,
                0,
                0,
                FW_BOLD,
                FALSE,
                FALSE,
                FALSE,
                HANGUL_CHARSET,  // <1-1>
                OUT_TT_PRECIS,
                CLIP_DEFAULT_PRECIS,
                ANTIALIASED_QUALITY,
                FF_DONTCARE|DEFAULT_PITCH,
                "Comic Sans MS"); // <1-2>
   
    SelectObject(hDC, font);
   
    wglUseFontOutlines(hDC,  // <2-1>
                32, // <2-2>
                96, // <2-3>
                base, // <2-4>
                0.1f, // <2-5>
                0.5f, // <2-6>
                WGL_FONT_POLYGONS, // <2-7>
                gmf); // <2-8>
}

지난장에서 설명했던 것은 제외하고 새로운 것만을 설명하도록 하겠다.

<1-1>의 코드를 살펴보자. 기억력이 좋은 독자는 지난장에서의 <1-1>의 코드가 무엇이었는지 기억하는가? ANSI_CHARSET이였다. 하지만 한글 윈도우즈에서는 Outline Font를 사용할때 ANSI_CHARSET로 해주면 Outline Font를 얻을수없다. 원하는 것을 얻기위해서는 HANGUL_CHARSET으로 지정해 주어야 한다.

<1-2>에서 폰트를 바꾸었다. 지난장과 같이 폰트를 바꾸지 않아도 상관없다.

이제 가장 핵심이 되는 실제 Outline Font의 리스트를 얻어오는 함수에 대해서 알아보도록 하겠는데 바로 <2-1>에서 보이는 wglUseFontOutlines가 바로 그것이다. 총 8개의 인자가 필요한데 하나 하나 알아보자.

<2-1> 코드는 OpenGL과 연결되어 있는 DC에서 폰트 정보를 얻기 위한 DC의 핸들이다.

<2-2> 코드는 ASCII에서 시작할 코드의 번호이고 <2-3> 코드는 <2-2>코드에서 지정된 코드에서 몇개의 문자를 얻을 것인지를 지정하는 갯수값이다.

<2-4> 코드는 실제 리스트를 만들때 기준이 되는 값을 지정하는 것이다.

<2-5> 코드는 폰트로부터 얼마나 정밀하게 객체를 생성할 것인지를 나타낸다. 0.0값이 가장 정말하게 만들수있다.

<2-6> 코드는 Z축으로 문자 객체의 두깨를 설정한다.

<2-7> 코드가 가질수있는 값은 2가지인데 첫째는 WGL_FONT_POLYGONS와 WGL_FONT_LINES이다. 첫째는 면으로 구성된 폰트 객체를 생성하고 두번째는 라인만으로 구성된 폰트 객체를 생성한다.

<2-8> 코드는 실제 생성된 각각의 문자에 대한 Outline 정보가 저장되는 배열 변수이다.

리스트를 생성했다면 생성된 것들을 반환하는 함수도 필요하다. 다음의 코드가 그와 같은 것이다. 이미 지난장에서 만들었고 그대로 사용하면 된다.

GLvoid KillFont(GLvoid)
{
    glDeleteLists(base, 96);
}

glDeleteLists 함수로써 첫번재 인자는 메모리 상에서 제거할 리스트의 기준값이 오고 두번째는 그 기준에서부터 몇개의 리스트를 제거할 것인지를 나타낸다.

이제 사용할 폰트 리스트가 완벽하게 준비가 되었다. 이제 이 준비된 리스트를 화면상에 찍을 수 있는 함수를 만들어보자. 출력한 물자열을 인자로 받아 출력해 주는 함수이다. 이미 지난장에서 만들었고 그대로 사용하면 된다.

GLvoid glPrint(const char *text)
{
    glPushAttrib(GL_LIST_BIT); //<1>
    glListBase(base - 32); //<2>
    glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); //<3>
    glPopAttrib(); //<4>
}

자! 이제 화면상에 글자를 그려주는 함수까지 준비가 되었다. 이제 위에서 만든 것들을 사용만 하면 되겠다. 먼저 initGL(GLvoid) 함수에 위에서 만든 BuildFont 함수를 추가함으로써 폰트 리스트를 생성하도록 한다.

int InitGL(GLvoid)
{
    glShadeModel(GL_SMOOTH);
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
    glClearDepth(1.0f);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
   
    ///////////////////////// NEW //////////////////////////
    BuildFont();
    glEnable(GL_LIGHTING); // <1>
    glEnable(GL_LIGHT0); // <2>
    glEnable(GL_COLOR_MATERIAL); // <3>
    ///////////////////////// NEW //////////////////////////
   
    return TRUE;
}

위의 코드를 보면 새롭게 추가된 코드가 3개이다. <1>과 <2>는 빛을 켜는 코드이고 <3>은 색상을 추적하여 물체의 재질을 설정하는 코드이다. 이 코드가 새로운 독자는 빛과 재질에 대한 장을 보기 바란다.

그리고 WinProc 함수의 WM_CLOSE를 처리하는 부분(이 부분은 프로그램이 종료될때 실행된다)에서 KillFont 함수를 추가하여야 하는데 이미 지난장에서 추가하였다.

case WM_CLOSE:
{
    ///////////////////////// NEW //////////////////////////
    KillFont();
    //////////////////////// NEW //////////////////////////
    PostQuitMessage(0);

    return 0;
}

자 이제 문자열을 화면상에 그려주는 것만 남았다. 무언가를 그려주는 코드는 항상 DrawGLScene(GLvoid) 부에 위치했고 이번에도 역시 마찬가지이다. 다음의 코드를 살펴보자.

int DrawGLScene(GLvoid)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glTranslatef(0.0f, 0.0f, -6.0f);
   
    glColor3f(0.90f, 0.9f, 1.0f);
    glScalef(1.0f, 2.0f, 1.0f);
    glRotatef(rot, 0.0f, 1.0f, 0.0f);
    glTranslatef(-1.0f, -0.3f, 0.0f);
    glPrint("Dip2K");
    rot += 1.0f;
   
    return TRUE;
}

DrawGLScene 함수의 구현은 모두 새롭게 작성되어졌다.

기본 흐름은 옅은 파랑색으로 재질을 설정하며 Y축으로 물체(문자)를 늘렸다. 그리고 매 순간마다 Y축으로 1도씩 회전시키는 에니메이션으로써 분명하게 문자의 3차원임일 확인한다. 비트맵의 경우 특별하게 문자의 위치를 지정하는 함수가 따로 있었던데 반해서 Outline Font는 단지 객체이므로 따로 그럴 필요가 없다.

다음은 최종적인 이 프로그램의 실행 결과이다.


사용자 삽입 이미지
어떤가? 드디어 우리가 원하는 결과를 얻었다.

“[OpenGL Tutorial] Output String on the Screen(Outline)”에 대한 11개의 댓글

    1. OSG는 오픈 씬 그래프를 말씀하시는건가요? 아직까지는 계획은 없지만.. OSG의 명성은 익히 들어왔던터라… 매우 관심을 두고 있긴합니다.. 아마도…. 3차원 GIS 엔진을 개발할 시간, 빠르면 아마도 2010년 중반이 되지 않을까 싶은데요.. 그때 집중해서 살펴보지 않을까 싶습니다.. OSG에 대해 공유할만한 역량이 된다면 꼭 좋은 자료를 공유하도록 하겠습니다.

  1. 넵 오픈신그래프 입니다^^ 와아!! 역시 관심을 두고 계시는군요! GIS 쪽에서 3차원 분야는 대부분 OSG를 쓰는것으로 알고있어서 여쭤본거였습니다. 2010년이 기대되는군요!

    1. 구현할 뷰포트의 개념에 따라 다르겠지만.. 위의 방식대로 동일한 방법으로 글자를 표시하도록 뷰포트를 구현하는게 가능합니다..

  2. HANGUL_CHARSET으로 설정했다면 한글을 사용하겠다는 거 아닌가요…?
    한글에 대한 내용이 전혀 없어서 궁금합니다.
    가능하시다면 한글 처리방법도 좀 알려주세요…
    moon_shine@nate.com

  3. 기존에 텍스쳐맵핑을 했던 소스에 outline폰트를 추가해서 공부해 봤습니다. 그런데 텍스쳐만출력할 경우에는 텍스쳐가 정상적으로 나오지만 폰트 출력까지 하게 되면 이상하게 텍스쳐의 앞면과 뒷면이 바뀝니다. 원래 정상일 때 보이는 부분은 컬링이 되어서 보이지 않고 보이지 않아야 할 부분들이 보입니다. 혹시 무슨 이유에서 그런지 알고 계신가요?

    1. 설명만으로 오류를 잡기가 쉽지 않고.. 단지 짐작만을 말씀드리면…. 아웃라인 폰트가 텍스쳐 맵핑에 영향을 줬다는 결론인데… 아웃라인 폰트와 텍스쳐 맵핑과는 관계가 없는 기능이라고 봐두 무방한데…. 그렇다면.. 아웃라인쪽에서 메모리 침범과 같은 상황이 발생해 텍스쳐에 영향을 준게 아닌지 싶습니다.. 아웃라인 부분을 좀더 살펴보시길 바랍니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다