GLUT Tutorials

키보드입력

GLUT 를 사용하면 일반키뿐만 아니라 특수키(F1 키나 화살표키)의 키보드입력을 감지하는 어플리케이션을 만들 수 있습니다. 이번 장에서는 키입력을 감지하는 방법과 GLUT 를 통해서 어떤 정보를 얻을 수 있는지 그리고 얻은 정보를 어떻게 다루는지 알아보겠습니다.

지금쯤이면 다아시겠지만, 어떤 이벤트를 처리하는 과정을 제어하려면 "내가 제공하는 어떤 함수가 그 이베트를 처리할 것이다" 라고 GLUT 에게 알려줘야합니다. 지금까지 우리는 "창을 다시 그릴 필요가 있을땐 이 함수를 사용하면 돼" "시스템이 휴면(Idle) 상태에 있을 때는 이 함수를 사용해" "창 크기가 변할 땐 이 함수를 사용하고~ 알았지~" 라고 GLUT 를 통해서 윈도우즈 운영체제 시스템에 알려준 것입니다.

키보드 이벤트를 처리할 때도 마찬가지로 같은 일을 해줘야합니다. GLUT 를 통해 윈도우 시스템에 "어떤 함수가 키가 입력되었을 때 필요한 처리를 할 것이다" 라고 알려줘야하죠. 이벤트가 발생했을 때 특정 함수가 실행되게끔 만드는 것을 "콜백함수의 등록" 이라고 합니다.

GLUT 는 키입력 키보드 이벤트를 처리하는 콜백함수를 등록하기 위해서 두개의 함수를 제공합니다. 처음 것은 glutKeyboardFunc 함수인데 일반키를 처리하는 함수를 윈도우 시스템에 알려줄 때 사용합니다. 일반키는 문자키, 숫자키 그리고 ASCII 코드로 표현되는 키들을 말합니다. 다음은 이 함수의 설명입니다.

void glutKeyboardFunc(void (*func) (unsigned char key, int x, int y)); 

인자 설명:
func - 일반키 입력 이벤트를 처리하는 함수의 이름. NULL 을 전달하면 GLUT 는 일반키 입력을 무시합니다.

glutKeyboardFunc 함수에 인자로 사용되는 함수는 세 개의 인자를 가지고 있습니다. 처음 것은 입력되는 키의 ASCII 코드를 나타내고 나머지 두 개의 인자들은 키가 눌렸을 때의 마우스 위치를 나타냅니다. 마우스 위치의 시작점은 창의 클라이언트 영역의 왼쪽-위(left-top) 코너입니다.

키입력을 처리하는 예를 들어보면, ESC(ESCAPE) 키가 눌렸을 때 어플리케이션이 끝나는 함수를 생각할 수 있겠죠. glutMainLoop 함수는 앞에서도 말했듯이 무한루프를 제공합니다. 즉, 절대 값을 반환하지 않고 계속해서 어플리케이션이 실행된다는 것이죠. 이 루프를 나가는 방법은 오직 exit 시스템 함수를 호출하는 길뿐입니다. 이렇게 하기 위해서 ECS 키가 눌렸을 때 exit 시스템 함수를 호출하도록 함수를 만듭니다. 따라서 ESC 키가 눌리면 어플리케이션은 종료되죠.(stdlib.h 파일을 포함하는 것을 잊지마세요). 다음은 지금까지 말한 것을 코드로 나타낸 것입니다.:

void processNormalKeys(unsigned char key, int x, int y) 
{
    if (key == 27) 
    exit(0);
}

glutKeyboardFunc 함수 인자의 모양대로 함수를 만들어 보았습니다. 만약 인자와 다르게 함수를 만들었다면 VC 가 컴파일 에러를 나불댈 거에요. 설마 이런걸 원하는 건 아니겠죠?

자~ 그럼 이제 특수키의 입력을 다뤄보죠. GLUT 는 glutSpecialFunc 함수를 제공하는데 이 함수를 사용해서 특수키 이벤트를 처리하는 함수를 등록할 수 있습니다. 다음은 이 함수의 설명입니다.

void glutSpecialFunc(void (*func) (int key, int x, int y)); 

Parameters: 인자 설명:
func - 특수키 입력 이벤트를 처리하는 함수의 이름. NULL 을 전달하면 GLUT 는 특수키 입력을 무시합니다.

특수키가 입력 되었을 때 삼각형의 색을 바꾸는 코드를 만들어보겠습니다. F1 키를 누르면 삼각형이 빨갛게 되고 F2 키를 누르면 녹색으로, F3 키를 누르면 파랗게 됩니다.

void processSpecialKeys(int key, int x, int y)
{
    switch(key)
    {
        case GLUT_KEY_F1 : 
            red = 1.0; 
            green = 0.0; 
            blue = 0.0; break;
        case GLUT_KEY_F2 : 
            red = 0.0; 
            green = 1.0; 
            blue = 0.0; break;
        case GLUT_KEY_F3 : 
            red = 0.0; 
            green = 0.0; 
            blue = 1.0; break;
    }
}

GLUTKEY* 는 glut.h 파일에 정의되어 있는 상수입니다. 정의되어 있는 모든 상수를 살펴볼까요?

GLUT_KEY_F1            F1 키
GLUT_KEY_F2            F2 키
GLUT_KEY_F3            F3 키
GLUT_KEY_F4            F4 키
GLUT_KEY_F5            F5 키
GLUT_KEY_F6            F6 키
GLUT_KEY_F7            F7 키
GLUT_KEY_F8            F8 키
GLUT_KEY_F9            F9 키
GLUT_KEY_F10        F10 키
GLUT_KEY_F11        F11 키
GLUT_KEY_F12        F12 키
GLUT_KEY_LEFT        왼쪽 방향키(←)
GLUT_KEY_RIGHT        오른쪽 방향키(→)
GLUT_KEY_UP            위쪽 방향키(↑)
GLUT_KEY_DOWN        아래쪽 방향키(↓)
GLUT_KEY_PAGE_UP    Page Up 키
GLUT_KEY_PAGE_DOWN    Page Down 키
GLUT_KEY_HOME        Home 키
GLUT_KEY_END        End 키
GLUT_KEY_INSERT        Insert 키

위에서 정의한 processSpecialKeys 코드를 컴파일하려면 red, green, blue 변수를 코드 시작부분에 선언해야합니다. 그리고 위의 코드대로 효과를 나타낼려면 렌더링을 수행하는 renderScene 함수를 고쳐야하겠죠.

...
// 모든 변수를 1.0 으로 초기화하는데 
// 이렇게 하면 삼각형이 흰색으로 그려집니다.
float red=1.0, blue=1.0, green=1.0;

void renderScene(void)
{
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glRotatef(angle,0.0,1.0,0.0);
    // 여기서 실제 색을 설정합니다.
    // glColor 는 앞으로 그려질 물체들의 색을 결정합니다.
   glColor3f(red,green,blue);

    glBegin(GL_TRIANGLES);
        glVertex3f(-0.5,-0.5,0.0);
        glVertex3f(0.5,0.0,0.0);
        glVertex3f(0.0,0.5,0.0);
    glEnd();
    glPopMatrix();

    angle++;
    glutSwapBuffers();
}

이제는 GLUT 에 키보드 이벤트를 처리하는 함수가 무엇인지를 말해줘야합니다. 즉, GLUT 의 glutKeyboardFunc 와 glutSpecialFunc 함수를 사용할 때란 거죠. 이 함수들은 어디서나 호출할 수 있습니다. 이 말은 키보드이벤트 함수가 아무때나 바뀔 수 있다는 얘기입니다. 하지만 대부분 이렇게 하지 않습니다. 그래서 우리는 main 함수에서 이 함수들을 호출하겠습니다. 아래에 main 함수가 있습니다. 키보드 처리부분이 추가되었네요.

void main(int argc, char **argv)
{
    glutInit(&argc, argv);

    // 이중버퍼를 설정합니다. 
    glutInitDisplayMode(GLUT_DEPTH|GLUT_DOUBLE|GLUT_RGBA);

    glutInitWindowPosition(100,100);
    glutInitWindowSize(320,320);
    glutCreateWindow("3D Tech - GLUT Tutorial");
    glutDisplayFunc(renderScene);
    glutIdleFunc(renderScene);
    glutReshapeFunc(changeSize);

    // 새로 추가된 코드입니다.
    glutKeyboardFunc(processNormalKeys);
    glutSpecialFunc(processSpecialKeys); 

    // 깊이 검사를 수행합니다.
    glEnable(GL_DEPTH_TEST);
    glutMainLoop();
}

Visual C++ 로 만든 프로젝트파일을 받아서 실행해 보세요. ( glut3.zip )

CTRL, ALT 그리고 SHIFT 검사

가끔 변경키(modifier key)가 눌렸는지 알고 싶을 때가 있습니다. 변경키는 CTRL, ALT 또는 SHIFT 키를 말합니다. GLUT 는 변경키가 눌렸는지를 알려주는 함수를 제공합니다. 이 함수는 키보드나 마우스 입력 이벤트처리 함수 내에서 호출되어야합니다. 다음은 이 함수의 설명입니다.

int glutGetModifiers(void);

이 함수의 반환값은 아래 세개의 정의된 상수중 하나이거나 OR 비트 연산되어 합쳐진 값입니다. ( glut.h 에 정의되어 있습니다. )

  • GLUT_ACTIVE_SHIFT - SHIFT 키나 Caps Lock 키를 눌렀을 때 반환값에 설정됩니다. 만약 두개의 키가 같이 눌려 있다면 반환값에 이 상수가 설정되지 않습니다.
  • GLUT_ACTIVE_CTRL - CTRL 키를 눌렀을 때 반환값에 설정됩니다.
  • GLUT_ACTIVE_ALT - ALT 키를 눌렀을 때 반환값에 설정됩니다.

윈도우 시스템이 가끔 변경키를 가로챌때가 있는데, 이를 해결할 콜백함수가 없기 때문에 조심해야합니다. 그래서 변경키를 어떻게 다뤄야하는지 알아보기위해서 processNormalKeys 함수를 약간 고쳐봅시다. 여기서는 r 키를 누르면 red 변수가 0.0 으로 설정되고 ALT + r 을 누르면 1.0 으로 설정된다고 가정합니다. 다음 코드는 약간의 트릭입니다.:

void processNormalKeys(unsigned char key, int x, int y)
{
    if (key == 27) 
        exit(0);
    else if (key=='r')
    {
        int mod = glutGetModifiers();
        if (mod == GLUT_ACTIVE_ALT)
            red = 0.0;
        else
            red = 1.0;
    }
}

위의 코드를 가지고 실행을 한다면, 'R' 키를 눌렀을 때는 아무런 일도 일어나지 않습니다. 그건 눌린 키가 'R' 이지 'r' 이 아니기 때문이죠. 그래서 소문자를 대문자로 바꾸기 위해서 또는 ASCII 코드로 정의된 어떤 것을 나타내기 위해서 SHIFT 변경키를 사용하면 안됩니다. SHIFT 키는 F1 과 같은 시스템키와만 사용할 수 있습니다.(위의 코드를 GLUT_ACTIVE_SHIFT 로 해놓고 실행하면 원하는 결과가 실행되지 않는다.)

void processNormalKeys(unsigned char key, int x, int y)
{
    if (key == 27) 
        exit(0);
    // 'R' 을 감지하는 것이라면 잘못된 코드입니다.
    else if (key=='r')
    {
        int mod = glutGetModifiers();
        if (mod == GLUT_ACTIVE_SHIFT)
            ...
    }
}

마지막으로 알아볼 것은 CTRL+ALT+F1 을 어떻게 감지하느냐는 것입니다. 여기서는 두개의 변경키를 동시에 감지해야합니다. 이걸 위해서는 원하는 변경키 상수를 OR 비트 연산을 사용해야합니다. 다음 코드는 red 변수를 CTRL+ALT+F1 이 눌렸을 때 변경되도록 바꾼 것입니다.

void processSpecialKeys(int key, int x, int y)
{
    int mod;
    switch(key)
    {
    case GLUT_KEY_F1 : 
        mod = glutGetModifiers();
        if (mod == (GLUT_ACTIVE_CTRL|GLUT_ACTIVE_ALT))
        {
            red = 1.0; green = 0.0; blue = 0.0;
        }
        break;
    case GLUT_KEY_F2 : 
        red = 0.0; 
        green = 1.0; 
        blue = 0.0; break;
    case GLUT_KEY_F3 : 
        red = 0.0; 
        green = 0.0; 
        blue = 1.0; break;
    }
}

이제 키보드와 마우스 이벤트 함수를 콜백함수로 등록해주세요. 이벤트 처리하기 위한 콜백함수를 등록하면 어플리케이션의 성능이 조금 떨어집니다. 왜냐하면 시스템이 항상 이벤트를 감시하고 있기 때문이죠. 그래서 키보드나 마우스 이벤트가 필요없을 때에는 콜백함수를 등록하지 말아주세요 :)