[App 개발] NeHe Lesson 17
본문
폰트때문에 힘드신 것 다 압니다. 지금까지 제가 만든 강의에서는 단순히 글자를 출력하는 것뿐만 아니라 3D 글자, 텍스쳐 매핑된 글자를 출력할 수 있고, 변수를 처리할 수도 있었습니다. 그러나 만일 여러분의 컴퓨터가 비트맵이나 아웃라인 글꼴을 지원하지 않는다면 어떻게 될까요?
다행히 Giuseppe D’Agata 가 전혀 다른 글꼴 강의를 만들어 주었습니다. 첫 번째 글꼴 강의에서 텍스쳐를 이용하여 화면에 글자를 출력하는 방법을 언급했던 것을 기억하신다면 그것을 어떻게 만들 수 있을까요? 텍스쳐를 이용하여 화면에 글자를 출력할 때 먼저 여러분은 좋아하는 그림 프로그램을 열고 글꼴을 선택하여 출력하고자 하는 글자나 문구를 넣을 것입니다. 그리고 비트맵을 저장하고 여러분의 프로그램에서 텍스쳐로 읽어들이겠죠. 엄청 많은 글자, 혹은 시시각각 변하는 문장을 처리하기에는 효과적이지 않은 방법입니다.
이 프로그램은 단지 하나의 텍스쳐를 이용해서 화면에 256개의 글자를 출력할 수 있습니다. 일반적인 글꼴은 16픽셀 넓이에 16픽셀 높이라는 것을 생각하십시오. 만약 여러분이 표준 256x256 텍스쳐를 생각한다면 한 줄에 16개의 글자를 넣고 세로줄로 16개의 글자를 넣을 수 있습니다. 좀 더 자세히 설명하면 텍스쳐는 256픽셀 넓이이고, 한 글자는 16픽셀 넓이입니다. 256 나누기 16은 16이죠. :)
그래서 2D텍스쳐 글꼴 데모를 만들어 봅시다. Lesson 1에서 사용했던 코드를 확장하겠습니다. 프로그램 첫 번째 부분에 math와 stdio 헤더를 포함합니다. 문자를 sin과 cos함수로 화면을 움직이도록 하기 위해 math 라이브러리가 필요하고, 텍스쳐를 만들기 위해 우리가 사용할 파일이 있는지를 확인하기 위해 stdio 라이브러리가 필요합니다.
#include <windows.h>
#include <math.h>
#include <stdio.h>
#include <gl\\gl.h>
#include <gl\\glu.h>
#include <gl\\glaux.h>
HDC hDC=NULL;
HGLRC hRC=NULL;
HWND hWnd=NULL;
HINSTANCE hInstance;
bool keys[256];
bool active=TRUE;
bool fullscreen=TRUE;
출력 리스트를 가리키기 위한 변수 base를 선언하겠습니다. 그리고 만들어진 텍스쳐를 담을 변수 texture[2]를 선언합니다. 텍스쳐 1은 글꼴 텍스쳐이고 텍스쳐 2는 간단한 3D 물체를 만드는 데 쓰일 것입니다.
실행 루프에 사용될 변수 loop를 선언합니다. 마지막으로 우리가 만드는 간단한 3D 물체를 화면에서 움직이고 회전하도록 만드는 데 쓰일 변수 cnt1과 cnt2를 더합니다.
GLuint base;
GLuint texture[2];
GLuint loop;
GLfloat cnt1;
GLfloat cnt2;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
이제 텍스쳐를 읽는 코드입니다. 이전에 소개된 텍스쳐 매핑 강의 코드와 동일한 것입니다.
AUX_RGBImageRec *LoadBMP(char *Filename)
{
FILE *File=NULL;
if (!Filename)
{
return NULL;
}
File=fopen(Filename,"r");
if (File)
{
fclose(File);
return auxDIBImageLoad(Filename);
}
return NULL;
}
다음의 코드도 지난 강의에서 사용되었던 코드에서 약간 수정한 것입니다. 만일 이 함수의 각 코드가 어떤 일을 하는지 이해가 안 되시면 지난 강의를 참조하십시오.
우리는 TextureImage[]에 두 개의 RGB 이미지를 담을 것입니다. 텍스쳐를 읽거나 저장하는 부분을 확실히 하시는 것이 좋습니다. 하나의 잘못된 숫자라도 메모리 낭비나 잘못된 종료를 일으킬 수 있습니다
int LoadGLTextures()
{
int Status=FALSE;
AUX_RGBImageRec *TextureImage[2];
다음 줄이 가장 중요하게 살펴보셔야 할 곳입니다. 만약 여러분이 숫자 2를 다른 것으로 바꿔 넣으시면 큰 문제가 발생하게 됩니다. 주의하십시오. 이 숫자는 TextureImages[]를 선언할 때 사용했던 숫자와 같아야 합니다.
우리가 읽어들일 텍스쳐는 font.bmp (글꼴) 과 bumps.bmp 입니다. 두 번째 것은 여러분이 원하시는 것을 바꾸셔도 됩니다. 저는 그렇게 창조적인 편은 아니어서 제가 쓰는 텍스쳐는 약간 단조로울 것입니다.
memset(TextureImage,0,sizeof(void *)*2);
if ((TextureImage[0]=LoadBMP("Data/Font.bmp")) &&
(TextureImage[1]=LoadBMP("Data/Bumps.bmp")))
{
Status=TRUE;
그 다음으로 조심해야 할 부분입니다. 저는 많은 사람으로부터 이런 이메일을 많이 받아봤습니다. “왜 텍스쳐 한 개밖에 보이지 않지요?”, “왜 내 텍스쳐는 하얗게 나오지요?”. 보통 이 부분이 원인입니다. 만약에 숫자 2를 1로 바꿔넣으면 텍스쳐 하나밖에 만들어지지 않게 되고 두 번째 텍스쳐는 흰색으로만 보이게 됩니다. 만약에 2를 3으로 바꾸었다면 프로그램은 충돌하게 됩니다.
여러분은 glGenTextures()를 한 번만 호출해야 합니다. glGenTextures()를 호출하고 텍스쳐를 모두 만드십시오. 저는 glGenTextures() 호출을 텍스쳐를 만들기 전 매번 호출하는 사람들을 본 적이 있습니다. 그렇게 되면 새로 만드는 텍스쳐가 이미 만들어진 텍스쳐를 덮어쓰게 됩니다. 먼저 몇 개의 텍스쳐를 만들지를 결정하고, glGenTextures()를 한 번 호출한 다음 모든 텍스쳐를 만들어주는 것이 좋은 방법입니다. 특별한 이유 없이 glGenTextures()를 루프 안에서 호출하는 것은 좋은 방법이 아닙니다.
glGenTextures(2, &texture[0]);
for (loop=0; loop<2; loop++)
{
glBindTexture(GL_TEXTURE_2D, texture[loop]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[loop]->sizeX, TextureImage[loop]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);
}
}
다음 코드는 텍스쳐를 만들기 위하여 읽어들이는 비트맵이 메모리를 다 사용했으면 메모리와 RGB 이미지 레코드도 풀어줍니다. 만약 우리가 3개의 이미지로 텍스쳐를 만들었다면 우리는 세 개의 RGB 이미지 레코드를 검사해서 풀어주어야 합니다.
for (loop=0; loop<2; loop++)
{
if (TextureImage[loop])
{
if (TextureImage[loop]->data)
{
free(TextureImage[loop]->data);
}
free(TextureImage[loop]);
}
}
return Status;
}
이제 우리 글꼴을 만들 차례입니다. 조금 자세히 이 부분을 설명하도록 하겠습니다. 그렇게 복잡한 것은 아닙니다만 이해해야 할 수식이 있습니다. 수학 좋아하는 사람 별로 없다는 것은 잘 알고 있습니다.
GLvoid BuildFont(GLvoid)
{
다음의 두 변수는 글꼴 텍스쳐 내부의 각 글자의 위치를 기억하는 데에 사용됩니다. cx는 텍스쳐 내부의 왼쪽에서 오른쪽으로의 위치를 담고 있고 cy는 위 아래의 위치를 담고 있습니다.
float cx;
float cy;
다음은 OpenGL에게 256개의 출력 리스트를 만들 것을 알립니다. 변수 base는 출력 리스트의 첫 번째 위치를 가리킵니다. 두 번째 출력 리스트는 base+1이 되고, 세 번째는 base+2가 됩니다.
두 번째 줄에서는 우리 글꼴 텍스쳐 texture[0]을 선택합니다.
base=glGenLists(256);
glBindTexture(GL_TEXTURE_2D, texture[0]);
이제 루프를 시작합니다. 256개의 글자를 만들어서 각각을 출력 리스트에 저장합니다.
for (loop=0; loop<256; loop++)
{
다음의 첫 번째 줄은 약간 복잡해 보일 것입니다. % 부호는 loop 를 16으로 나눈 나머지수를 뜻합니다. cx는 글꼴 텍스쳐의 수평 방향으로 움직입니다. 이 코드 뒷부분에서 우리는 cy 값에서 1을 빼서 아래에서 위로 움직이지 않고 위에서 아래로 움직이는 것을 볼 것입니다. % 부호는 설명하기 까다롭지만 한 번 시도해 보겠습니다.
우리가 신경써야 할 부분은 (loop%16) 입니다. /16.0f는 결과를 텍스쳐 좌표로 변환하는 것입니다. 따라서 loop 변수값이 16이었다면 cx 값은 16/16 의 나머지값, 0이 됩니다. 그러나 cy는 16/16, 1입니다. 따라서 우리는 한 글자 높이만큼 내려오게 되고 오른쪽으로는 움직이지 않게 됩니다. 이번에는 loop 값이 17이었다고 하면, cx는 17/16, 1.0625입니다. 나머지값 .0625는 1/16th 입니다. 오른쪽으로 한 글자 이동하게 됩니다. cy 는 계속해서 1입니다. 왜냐하면 소숫점 위의 숫자만을 고려하기 때문입니다. 18/16은 2/16이 되고 오른쪽으로 두 글자 이동하고, 위아래는 계속 한 글자입니다. 만약에 loop 가 32였다면 32를 16으로 나눈 나머지가 없으므로 cx는 0이 되고, 소숫점 위의 숫자가 2이기 때문에 cy는 2가 되고, 글꼴 텍스쳐의 위에서 두 글자 밑으로 이동하게 됩니다. 이해가 되시죠?
cx=float(loop%16)/16.0f;
cy=float(loop/16)/16.0f;
휴우~ 됐습니다. 이제 cx 와 cy 변수의 값에 따라 글꼴 텍스쳐로부터 개별 글자들을 골라서 2D 글꼴을 만들 수 있습니다. 다음 줄에서는 base 값에 loop 값을 더합니다. 이렇게 하지 않으면 모든 글자가 첫 번째 출력 리스트에 만들어지게 되는데, 아무도 그런 것은 원하지 않을 것이기 때문에 base 에 loop 를 더하여 만들어지는 각 글자는 그 다음 마련된 출력 리스트에 저장되도록 만듭니다.
glNewList(base+loop,GL_COMPILE);
만들고자 하는 출력 리스트는 선택되었고, 이제 글자를 만들겠습니다. 사각형에 글꼴 텍스쳐의 한 글자 모양을 텍스쳐로 입혀서 만듭니다.
glBegin(GL_QUADS);
cx와 cy변수는 0.0f와 1.0f 사이의 아주 작은 부동소숫점 값을 갖게 됩니다. 만약에 cx와 cy값이 모두 0이라면 코드의 첫 번째 줄은 실제로는 glTexCoord2f(0.0f,1-0.0f-0.0625f)가 됩니다. 0.0625는 정확히 텍스쳐의 16분의 1이고, 글꼴의 넓이와 높이가 됩니다. 텍스쳐 좌표는 텍스쳐의 왼쪽 아래가 될 것입니다.
우리는 glVertex3f(x,y,z) 대신에 glVertex2i(x,y) 를 사용합니다. 우리 글꼴은 2D이기 때문에 z값이 필요없습니다. 왜냐하면 우리는 정사영법 화면을 사용하기 때문에 화면 안쪽으로 이동해야 할 필요가 없기 때문입니다. 정사영법 화면에서 그리는 작업은 xy 좌표값을 지정하는 것으로 끝입니다. 우리 화면은 0 부터 639 픽셀, 0 부터 479 픽셀이기 때문에 부동소숫점이나 음수값이 필요가 없습니다.
정사영 화면을 설정하게 되면 좌표(0,0)은 화면의 왼쪽 아래가 되고, (640,480)은 화면의 오른쪽 위가 됩니다. x축에서 0 은 화면의 왼쪽 끝이고 639는 오른쪽 끝입니다. y축에서 0은 화면 밑바닥이고 479는 화면 맨 위가 됩니다. 투시도법을 사용할 필요가 없고 유닛보다 픽셀 단위로 작업하기를 좋아하시는 분에게는 손쉬운 방법이 되겠습니다.
glTexCoord2f(cx,1-cy-0.0625f);
glVertex2i(0,0);
다음 텍스쳐 좌표는 마지막 텍스쳐 좌표의 오른쪽에서 16분의 1, 정확히 한 글자 넓이만큼 이동한 위치입니다. 따라서 이것은 텍스쳐의 오른쪽 밑 지점입니다.
glTexCoord2f(cx+0.0625f,1-cy-0.0625f);
glVertex2i(16,0);
세 번째 텍스쳐 좌표는 글자의 맨 오른쪽에서 위로 16분의 1, 정확히 한 글자의 높이만큼 올라간 위치입니다. 이것은 글자의 오른쪽 위 지점에 해당합니다.
glTexCoord2f(cx+0.0625f,1-cy);
glVertex2i(16,16);
마지막으로 왼쪽으로 이동하여 글자의 왼쪽 위 지점의 텍스쳐 좌표를 지정합니다.
glTexCoord2f(cx,1-cy);
glVertex2i(0,16);
glEnd();
마지막으로 오른쪽으로 10픽셀만큼 이동하여 텍스쳐의 오른쪽에 위치하도록 합니다. 이동하지 않게 되면 글자들이 그 위에 덮어 씌워지게 됩니다. 우리가 쓰는 글꼴의 폭이 좁기 때문에 오른쪽으로 16픽셀씩 움직일 필요가 없습니다. 그렇게 하면 글자들 사이에 많은 공간이 보이게 될 것입니다. 10픽셀씩 이동하면 그런 간격을 없앨 수 있습니다.
glTranslated(10,0,0);
glEndList();
}
}
다음 코드는 프로그램을 종료하기 전에 출력 리스트를 풀어주는 다른 글꼴 강의에 사용했던 코드와 동일한 코드입니다. base 에서 시작되는 256개의 출력 리스트가 모두 지워질 것입니다.
GLvoid KillFont(GLvoid)
{
glDeleteLists(base,256);
}
다음 코드는 모든 화면 출력이 이루어지는 곳입니다. 대부분이 새로운 코드이기 때문에 각 줄마다 아주 자세하게 설명하겠습니다. 짧은 노트: 변수 출력, 글자 크기 변경, 글자 간격 등 여러 가지 기능을 추가할 수 있고, 출력 이전의 상태로 복구하기 위한 여러 가지 점검사항이 추가될 수 있습니다.
glPrint()함수에는 세 개의 인수가 있습니다. 첫 번째는 화면의 x 축 위치 (왼쪽 오른쪽 위치)입니다. 두번째는 화면의 y 축 위치 (위아래, 0이 아래이고 숫자가 커질수록 위)입니다. 그리고 출력하고자 하는 실제 문자열이고, 마지막으로 set이라는 변수입니다. 여러분이 Giuseppe D’agata가 만든 비트맵을 보시면 글꼴이 두 개 있습니다. 첫 번째 글자들은 기본형이고 두 번째 글자들은 이탤릭입니다. 만일 set 변수를 0으로 놓으면 첫 번째 글자 셋이 선택되고 1이나 그 이상이면 두 번째 글꼴 셋이 선택될 것입니다.
GLvoid glPrint(GLint x, GLint y, char *string, int set)
{
먼저 set 변수가 0이나 1, 둘 중에 하나인지 확인합니다. 만약 set이 1보다 크다면 이 값을 1로 바꾸어 줍니다.
if (set>1)
{
set=1;
}
이제 글꼴 텍스쳐를 선택합니다. 혹시라도 우리가 화면에 출력하기 전에 다른 텍스쳐가 선택되었을 경우를 생각해서 이렇게 하는 것입니다.
glBindTexture(GL_TEXTURE_2D, texture[0]);
Depth testing 을 비활성화시킵니다. 이렇게 하는 이유는 블렌딩이 잘 되도록 하기 위해서입니다. 이렇게 하지 않게 되면 글자들이 뒤로 가 있게 되거나 블렌딩이 제대로 이루어지지 않을 수 있습니다. 만약에 화면에 글자를 블렌딩하지 않고 검은 공간들이 글자 주변에 보이지 않게 하려면 그대로 활성화시켜도 됩니다.
glDisable(GL_DEPTH_TEST);
다음 코드들은 아주 중요합니다. 먼저 프로젝션 매트릭스를 선택한 다음 glPushMatrix()를 호출합니다. 이것은 현재 매트릭스를 저장하는 함수입니다. 계산기의 메모리 버튼같은 기능입니다.
glMatrixMode(GL_PROJECTION);
glPushMatrix();
이제 프로젝션 매트릭스가 저장되었고 매트릭스를 리셋하고 정사영 화면을 설정합니다. 첫 번째와 세 번째의 숫자 0은 화면의 왼쪽 아래를 뜻합니다. 원한다면 회면의 왼쪽 끝을 -640으로 만들 수도 있습니다만 불필요하게 음수값을 이용해야 할 필요는 없습니다. 두 번째와 네 번째 숫자는 화면의 오른쪽 위입니다. 사용하고자 하는 화면 해상도와 값을 일치시키는 것이 좋습니다. 화면에는 깊이가 없기 때문에 z 값은 -1 과 1로 설정합니다.
glLoadIdentity();
glOrtho(0,640,0,480,-1,1);
이제 우리 모델뷰 매트릭스를 선택하고 glPushMatrix()함수로 현재 세팅을 저장합니다. 그 다음에 모델뷰 매트릭스를 리셋하여 우리가 만드는 정사영 화면에서 쓸 수 있도록 합니다.
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
투시도법 설정은 저장되어 있고 우리 정사영법 화면이 설정되었으므로 이제 우리 글자를 그릴 수 있게 되었습니다. 먼저 글자를 그리고자 하는 화면 위치로 이동하는 것으로 시작하겠습니다. 우리는 실제 픽셀로 작업을 하고 있기 때문에 부동소숫점 값은 중요하지 않으므로, 여기서는 glTranslatef()함수 대신 glTranslated()를 사용하겠습니다. 어짜피 반 픽셀만 그리는 것은 할 수 없으니까요.
glTranslated(x,y,0);
다음 줄에서는 어떤 글꼴을 쓸 것인지를 선택합니다. 만약 두 번째 글꼴 셋을 쓰고자 하면 출력 리스트 base 에 128 (128은 256의 반) 을 더합니다. 128을 더하면 앞의 128글자를 건너뛰게 됩니다.
glListBase(base-32+(128*set));
이제 남은 일은 화면에 글자를 그리는 것입니다. 다른 글꼴 예제에서 하던 것과 같은 방식으로 하게 됩니다. 우리는 glCallLists()함수를 씁니다. strlen(string)은 문자열의 길이 (몇 개의 글자를 그릴 것인가) 이고, GL_UNSIGNED_BYTE는 각 글자들이 부호 없는 바이트 크기(0부터 255사이의 값)로 되어 있다는 것을 뜻합니다. 마지막으로 변수 string은 화면에 출력하고자 하는 실제 문자들을 갖고 있습니다.
glCallLists(strlen(string),GL_UNSIGNED_BYTE,string);
이제 원래의 투시도법 공간으로 되돌립니다. 프로젝션 매트릭스를 선택하고 glPopMatrix()함수로 앞에서 glPushMatrix()로 저장했던 세팅을 부릅니다. 저장하던 순서와 정 반대의 순서로 이 작업을 합니다.
glMatrixMode(GL_PROJECTION);
glPopMatrix();
이제 모델뷰 매트릭스를 선택하고 같은 작업을 반복합니다. glPopMatrix()함수를 이용하여 정사영 화면을 설치하기 전의 모델뷰 매트릭스로 되돌립니다.
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
마지막으로 depth testing 을 활성화시킵니다. 만일 앞에서 depth testing 을 비활성화하지 않았다면 이것은 건너뛰어도 됩니다.
glEnable(GL_DEPTH_TEST);
}
ReSizeGLScene()함수에서는 변경사항이 없고, InitGL()함수로 갑시다.
int InitGL(GLvoid)
{
먼저 텍스쳐 만드는 코드로 이동합니다. 만약 텍스쳐 만들기가 실패하면 FALSE를 받게 되고, 우리는 어떤 오류가 발생한 것을 알게 되어 프로그램을 종료하게 됩니다.
if (!LoadGLTextures())
{
return FALSE;
}
만약 오류가 없으면 글꼴 만드는 코드로 이동합니다. 글꼴 만드는 데에 특별히 탈 날 일이 없으므로 오류 검사는 안 하겠습니다.
BuildFont();
그 다음은 일반적인 OpenGL 의 설정입니다. 배경화면을 검은 색으로 지우고 depth 를 1.0으로 지웁니다. Depth test와 블렌딩 모드를 선택합니다. Smooth shading을 활성화하고 마지막으로 2D 텍스쳐 매핑을 활성화합니다.
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0);
glDepthFunc(GL_LEQUAL);
glBlendFunc(GL_SRC_ALPHA,GL_ONE);
glShadeModel(GL_SMOOTH);
glEnable(GL_TEXTURE_2D);
return TRUE;
}
다음의 코드에서는 씬을 만듭니다. 먼저 3D 물체를 그리고 글자를 다음에 그려서 글자들이 3D 물체들에 가려지지 않고, 3D 물체들 위에 보이도록 합니다. 3D 물체를 그리기로 결정한 이유는 투시도법과 투시도법을 동시에 사용할 수 있다는 것을 보여드리기 위함입니다.
int DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
bumps.bmp 로 만든 텍스쳐를 이용하여 작고 간단한 3D 물체를 만들겠습니다. 화면 안쪽으로 5유닛 이동하여 3D 물체를 화면에서 볼 수 있도록 하겠습니다. 그리고 z 축으로 45도 회전합니다. 사각형을 시계 방향 45도로 회전시켜 사각형이라기보다는 다이아몬드 모양으로 만들겠습니다.
glBindTexture(GL_TEXTURE_2D, texture[1]);
glTranslatef(0.0f,0.0f,-5.0f);
glRotatef(45.0f,0.0f,0.0f,1.0f);
45도 회전이 끝나면 변수 cnt1 과 30의 곱으로 x 축과 y 축을 중심으로 회전을 시킵니다. 마치 다이아몬드가 모서리를 중심으로 회전하는 것처럼 물체가 회전할 것입니다.
glRotatef(cnt1*30.0f,1.0f,1.0f,0.0f);
블렌딩을 비활성화하고 (3D 물체를 solid하게 합니다) 색상을 밝은 흰색으로 선택한 후 텍스쳐 매핑된 사각형을 그립니다.
glDisable(GL_BLEND);
glColor3f(1.0f,1.0f,1.0f);
glBegin(GL_QUADS);
glTexCoord2d(0.0f,0.0f);
glVertex2f(-1.0f, 1.0f);
glTexCoord2d(1.0f,0.0f);
glVertex2f( 1.0f, 1.0f);
glTexCoord2d(1.0f,1.0f);
glVertex2f( 1.0f,-1.0f);
glTexCoord2d(0.0f,1.0f);
glVertex2f(-1.0f,-1.0f);
glEnd();
사각형을 그린 즉시 x 축과 y 축 중심으로 90도 회전시키고 다음 사각형을 그립니다. 두 번째 사각형은 첫 번째 사각형의 중심을 뚫고 지나가게 되어 멋있는 모양을 만들게 됩니다.
glRotatef(90.0f,1.0f,1.0f,0.0f);
glBegin(GL_QUADS);
glTexCoord2d(0.0f,0.0f);
glVertex2f(-1.0f, 1.0f);
glTexCoord2d(1.0f,0.0f);
glVertex2f( 1.0f, 1.0f);
glTexCoord2d(1.0f,1.0f);
glVertex2f( 1.0f,-1.0f);
glTexCoord2d(0.0f,1.0f);
glVertex2f(-1.0f,-1.0f);
glEnd();
두 개의 텍스쳐 매핑된 사각형을 그린 다음 블렌딩을 활성화하고 글자를 그립니다.
glEnable(GL_BLEND);
glLoadIdentity();
다른 글꼴 예제에서 쓰던 색상 만들기 코드를 사용하겠습니다. 화면 위를 움직이면서 점차로 색상이 변경됩니다.
glColor3f(1.0f*float(cos(cnt1)),1.0f*float(sin(cnt2)),1.0f-0.5f*float(cos(cnt1+cnt2)));
이제 글자를 그립니다. glPrint()함수를 사용합니다. 첫 번째 인수는 x 위치입니다. 두 번째 인수는 y 위치입니다. 세 번째 인수 (“NeHe”)는 화면에 출력하고자 하는 글자이고, 네 번째 인수는 사용하고자 하는 글꼴 셋입니다. (0-기본,1-이탤릭)
짐작하신 대로, sin과 cos함수와 cnt1, cnt2 카운터를 이용하여 글자를 화면에서 움직일 것입니다. 혹시 sin, cos 함수가 어떤 일을 하는지 모르시면 앞에 있는 글꼴 예제를 참조하십시오.
glPrint(int((280+250*cos(cnt1))),int(235+200*sin(cnt2)),"NeHe",0);
glColor3f(1.0f*float(sin(cnt2)),1.0f-0.5f*float(cos(cnt1+cnt2)),1.0f*float(cos(cnt1)));
glPrint(int((280+230*cos(cnt2))),int(235+200*sin(cnt1)),"OpenGL",1);
군청색으로 화면 밑에 제작자의 이름을 그립니다. 그 다음에 밝은 흰색 글씨로 이름을 다시 한 번 그리는데, 약간 오른쪽에 그립니다. 그림자 모양을 만드는 것입니다. (만약 블렌딩이 활성화되지 않으면 이 효과가 나오지 않을 것입니다.)
glColor3f(0.0f,0.0f,1.0f);
glPrint(int(240+200*cos((cnt2+cnt1)/5)),2,"Giuseppe D'Agata",0);
glColor3f(1.0f,1.0f,1.0f);
glPrint(int(242+200*cos((cnt2+cnt1)/5)),2,"Giuseppe D'Agata",0);
마지막으로 카운터들을 다른 분량만큼 증가시킵니다. 이렇게 해서 글자들이 움직이고 3D 물체가 회전하게 됩니다.
cnt1+=0.01f;
cnt2+=0.0081f;
return TRUE;
}
KillGLWindow(), CreateGLWindow(), WndProc()등은 변경사항이 없습니다.
마지막은 KillFont()를 KillGLWindow()에 추가합니다. 이것을 추가하는 것이 중요한데, 프로그램을 종료하기 전에 모든 것을 정리하는 역할을 합니다.
if (!UnregisterClass("OpenGL",hInstance))
{
MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hInstance=NULL;
}
KillFont();
}
최신글이 없습니다.
최신글이 없습니다.
댓글목록 0