GLUT Tutorials

비트맵폰트와 직교투영

2D 폰트인 비트맵폰트의 쓰임새는 대개 사용자에게 정보를 제공하는 것입니다. 예를 들면, 어플리케이션에서 초당 프레임수를 알려주기 위해서 비트맵폰트를 사용할 수 있습니다. 이러한 정보들은 사용자가 카메라를 이리저리 움직이더라도 화면의 고정된 위치에 있어야할 것입니다. 정보를 출력할 위치를 계산할 때 2D 직교투영을 사용하는 것이 원근(투시)투영을 사용하는 것보다 계산하기가 쉽습니다. 그것은 직교투영을 사용하면 픽셀단위로 위치를 정할 수 있기 때문입니다.

비트맵폰트로 글자를 출력할 때에는 우선 원근투영을 사용해서 장면을 그린 다음에, 투영방법을 직교투영으로 변환 후 문자열을 출력합니다. 문자열을 출력하고 나서는 투영방법을 이전투영방법으로 돌려놔야하는데(이 경우는 원근투영으로) 그래야만 다음 프레임때 장면을 제대로 그릴 수 있기 때문입니다.

다음에 나오는 코드는 위에서 설명한 것을 렌더링함수로 표현해본 것입니다.:

void renderScene()
{
    // 장면을 렌더링하는데 필요한 모든 작업을 수행합니다.
    ....

    setOrthographicProjection();
    glPushMatrix();
        glLoadIdentity();
        renderBitmapString(5,30,GLUT_HELVETICA_18,"3D Tech");
    glPopMatrix();
    resetPerpectiveProjection();

    glutSwapBuffers();
}

두개의 새로운 함수인 setOrthograpicProjection 과 resetPerpectiveProjection 을 볼 수 있습니다. 첫번재 함수는 카메라를 조작하기 위해서 행렬모드를 GL_PROJECTION 으로 바꾸는 것으로 시작해서 이전 환경변수(이 예제에서는 원근투영과 그 밖에 다른것들)를 저장하고 투영행렬을 glLoadIdentity() 함수를 사용해서 초기화합니다. 그 다음에 gluOrtho 함수를 사용해서 투영방법을 직교투영을 설정합니다.

setOrthographicProjection 함수는 x 축과 y 축의 범위를 정하는 것이 주된 목적입니다. y 축을 뒤집고 나서 원점을 좌측 위로 옮깁니다. 이렇게 만들면 화면좌표계상에 글씨를 쓰기가 쉽워집니다.

변수 w 과 h 는 다른 곳에서 계산됩니다.(소스코드에서 changeSize 함수를 참고하세요.)

void setOrthographicProjection()
{
    // 행렬을 투영모드로 변경합니다.
    glMatrixMode(GL_PROJECTION);
    // 원근투영에 대한 환경변수를 
    // 가지고 있는 이전 행렬을 저장합니다.
    glPushMatrix();
        // 행렬을 초기화합니다.
        glLoadIdentity();
        // 2D 직교투영을 설정합니다.
        gluOrtho2D(0, w, 0, h);
        // y 축을 뒤집습니다.
        glScalef(1, -1, 1);
        // 좌측아래의 원점을 
        // 좌측위로 옮깁니다.
        glTranslatef(0, -h, 0);
    glMatrixMode(GL_MODELVIEW);
}

resetPerspectiveProjection 함수는 아주 간단합니다. 전에 직교투영을 설정하기 전에 저장해 놓은 원근투영을 다시 돌려놓는 것인데, 행렬모드를 GL_PROJECTION 으로 바꾸고 행렬을 팝(pop)하여 예전 설정들을 다시 돌려놓고 다시 행렬모드를 GL_MODELVIEW 로 설정해 주는게 전부입니다.

void resetPerspectiveProjection()
{
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
}

위의 함수들을 사용하면 문자열을 다닥다닥 붙여서 출력합니다. 물론 빈칸(스페이스키)은 제외입니다. 문자열에서 빈칸이 있으면 빈칸이 출력되겠죠. 여백을 주기 위해서는 현재 레스터 위치마다 간격을 유지해야합니다. 즉 x 좌표 값에 약간씩 여백을 주는 것이죠. 레스터 위치에 간격을 유지하는 방법에는 두가지가 있습니다. 하나는 비트맵폰트 한 글자를 그린 다음에 간격을 계산하는 것이고 두번째는 OpenGL 의 상태기계에 현재 레스터 위치가 어디쯤 되느냐고 묻는 것입니다.

첫번째 방법을 사용하려면 문자의 치수(너비와, 높이)를 알아야됩니다. 높이는 폰트들 높이의 최고값인 상수이지만, 너비는 매우 가변적입니다. 이 점을 위해서 GLUT 에는 문자의 너비를 알려주는 함수가 있습니다. 그 함수는 glutBitmapWidth 이며 아래는 이 함수의 설명입니다.

int glutBitmapWidth(void *font, int character); 

인자 설명:
font - GLUT 에 정의 되어 있는 폰트 중 하나입니다. 설정할 수 있는 값은 앞장을 살펴보세요.
character - 너비를 알고 싶은 문자입니다.

예를들어서, 각 문자간에 일정한 간격을 두고 문자열을 출력해주는 함수를 만들고자 한다면 아래의 코드처럼 만들 수 있습니다.

void renderSpacedBitmapString( float x, float y, int spacing, void *font, char *string)
{
    char *c;
    int x1=x;
    for (c=string; *c != '\0'; c++)
    {
        glRasterPos2f(x1,y);
        glutBitmapCharacter(font, *c);
        x1 = x1 + glutBitmapWidth(font,*c) + spacing;
    }
}

비슷한 예로, 문자마다 일정한 간격을 두어서 세로로 된 문자열을 출력하려면 아래의 코드처럼 만들 수 있습니다.

void renderVerticalBitmapString( float x, float y, int bitmapHeight, void *font, char *string)
{
    char *c;
    int i;
    for (c=string,i=0; *c != '\0'; i++,c++)
    {
        glRasterPos2f(x, y+bitmapHeight*i);
        glutBitmapCharacter(font, *c);
    }
}

bitmapHeight 변수는 쉽게 계산할 수가 있는데 그 것은 각 폰트의 높이 중 최고값을 알기 때문입니다.(소스코드를 참고하세요.)

GLUT 에는 아직 설명하지 않은 비트맵폰트를 위한 함수가 있는데 glutBitmapLength 함수입니다. 이 함수는 문자열의 총 픽셀길이를 계산해줍니다. 이 함수의 반환값은 문자열 내의 모든 문자들의 너비를 더한 값입니다. 다음은 이 함수의 설명입니다.

int glutBitmapLength(void *font, char *string); 

인자 설명:
font - GLUT 에 정의 되어 있는 폰트 중 하나입니다. 설정할 수 있는 값은 앞장을 살펴보세요.
string - 총 픽셀길이를 알고 싶은 문자열입니다.

위에서 살펴본 함수들의 사용법과 쓰임새는 예제를 참고하세요.