프로그래밍/컴퓨터그래픽스

GLUT 콜백 함수 정리

studylida 2025. 4. 5. 16:21

 

🪟 GLUT 윈도우 콜백 함수 완전 정복 (기초 개념부터 설명!)

OpenGL을 배우다 보면 콜백 함수(callback function)라는 개념이 자주 나와요.
특히 GLUT 라이브러리를 사용할 때는, 콜백 함수를 등록해서 윈도우 이벤트에 반응하는 것이 기본 중의 기본이에요!

그런데 처음 프로그래밍을 배우는 입장에서는 “콜백 함수가 뭔데?”,
“내가 직접 호출하지도 않는 함수를 왜 등록해?” 같은 궁금증이 들 수 있어요.


📣 콜백 함수란 무엇인가요?

콜백 함수는 특정 이벤트가 발생했을 때, 시스템이 자동으로 호출해주는 함수예요.

우리가 직접 호출하지 않아도, GLUT가 알아서 적절한 타이밍에 호출해줘요.


🎯 쉬운 비유: 음식 배달 앱

  • 음식 배달 앱에서 주소를 등록하면, 배달원이 도착했을 때 그 주소로 자동으로 찾아오죠?
  • GLUT도 비슷하게 콜백 함수(주소)를 등록해두면,
  • 이벤트(배달 도착)가 발생했을 때, 알아서 해당 함수를 호출해줘요.
현실 세계 OpenGL 프로그램
주소 등록 콜백 함수 등록
배달 도착 윈도우 이벤트 발생
배달 시작 시스템이 함수 호출

✅ GLUT에서 콜백 함수가 왜 필요할까요?

OpenGL에서는 화면 그리기, 창 크기 조절, 창 닫기 같은 이벤트가 다양하게 발생해요.

하지만 우리가 그 이벤트를 직접 감지하긴 어려워요.
그래서 GLUT에게 미리 "이런 일이 생기면 이 함수를 대신 호출해줘" 하고 등록해두는 거예요.


🧩 GLUT 윈도우 콜백 함수 3종류 정리

이제 본격적으로, GLUT에서 사용하는 윈도우 콜백 함수들을 살펴볼게요!
모두 main() 함수에서 미리 등록해줘야 정상적으로 작동해요.


1️⃣ glutReshapeFunc() – 창 크기가 바뀔 때 호출됨

void glutReshapeFunc(void (*func)(int w, int h));

📌 언제 호출되나요?

  • 윈도우가 처음 생성될 때
  • 창의 크기가 변경될 때

🧠 왜 필요할까?

OpenGL은 좌표계나 뷰포트 설정이 중요한데, 창의 크기가 바뀌면 이를 반영해 뷰포트를 다시 설정해야 해요.
안 그러면 그래픽이 깨지거나 비율이 이상하게 보일 수 있어요!

💡 함수 예시

void reshape(int w, int h) {
    glViewport(0, 0, w, h);
}
glutReshapeFunc(reshape);

2️⃣ glutDisplayFunc() – 화면을 그릴 때 호출됨

void glutDisplayFunc(void (*func)());

📌 언제 호출되나요?

  • 프로그램이 처음 시작될 때
  • glutPostRedisplay()다시 그리기 요청할 때

🧠 왜 필요할까?

OpenGL에서 실제로 화면에 보이는 그림은 이 함수 안에서 정의돼요.
따라서 모든 그리기 코드는 이 함수 안에 들어가야 해요.

💡 함수 예시

void display() {
    glClear(GL_COLOR_BUFFER_BIT);
    // 도형 그리기 등...
}
glutDisplayFunc(display);

3️⃣ glutCloseFunc() – 창이 닫힐 때 호출됨

void glutCloseFunc(void (*func)());

📌 언제 호출되나요?

  • 사용자가 창을 닫을 때
  • 프로그램이 종료될 때

🧠 왜 필요할까?

메모리 정리, 로그 출력, 저장 기능 등 종료 전 정리 작업이 필요할 수 있어요.
이럴 때 이 콜백 함수를 등록해두면 유용해요!

💡 함수 예시

void onClose() {
    printf("프로그램이 종료됩니다!\n");
}
glutCloseFunc(onClose);

📜 전체 예시 코드

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutCreateWindow("GLUT 윈도우 콜백 예제");

    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
    glutCloseFunc(onClose);

    glutMainLoop();
    return 0;
}

🖱⌨⏰ GLUT 마우스 / 키보드 / 타이머 콜백 함수 정리

 

🖱 마우스 콜백 함수

1️⃣ glutMouseFunc — 마우스를 클릭할 때

void glutMouseFunc(void (*func)(int button, int state, int x, int y));

🧠 왜 필요할까?

사용자가 마우스를 클릭했을 때 어떤 동작을 하고 싶을 때 필요해요.
예를 들어 좌클릭하면 점 찍기, 우클릭하면 메뉴 열기 같은 경우!

📌 전달되는 정보

  • button: 누른 버튼 (GLUT_LEFT_BUTTON, GLUT_RIGHT_BUTTON 등)
  • state: 버튼 상태 (GLUT_DOWN, GLUT_UP)
  • x, y: 클릭 위치 (좌측 상단 기준)

💡 예시

void mouse(int button, int state, int x, int y) {
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
        printf("좌클릭 위치: (%d, %d)\n", x, y);
}
glutMouseFunc(mouse);

2️⃣ glutMotionFunc — 마우스를 누른 채 움직일 때

void glutMotionFunc(void (*func)(int x, int y));

🧠 왜 필요할까?

드래그하면서 그리기, 선택 상자 그리기 등에서 유용해요.

📌 전달되는 정보

  • x, y: 마우스 현재 위치

💡 예시

void drag(int x, int y) {
    printf("드래그 중: (%d, %d)\n", x, y);
}
glutMotionFunc(drag);

3️⃣ glutPassiveMotionFunc — 마우스를 움직이기만 할 때

void glutPassiveMotionFunc(void (*func)(int x, int y));

🧠 왜 필요할까?

마우스를 클릭하지 않아도, 커서 위치를 추적하거나 강조 효과를 주고 싶을 때 사용해요.

📌 전달되는 정보

  • x, y: 마우스 현재 위치

💡 예시

void hover(int x, int y) {
    printf("마우스 위치: (%d, %d)\n", x, y);
}
glutPassiveMotionFunc(hover);

⌨ 키보드 콜백 함수

glutKeyboardFunc — 키보드를 눌렀을 때

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

🧠 왜 필요할까?

키보드 입력으로 화면 전환, 캐릭터 조작, 단축키 기능 등을 구현할 수 있어요.

📌 전달되는 정보

  • key: 누른 키의 ASCII 코드 (예: 'a', '1', 'q')
  • x, y: 키를 누른 시점의 마우스 위치

💡 예시

void key(unsigned char key, int x, int y) {
    if (key == 'q') exit(0); // q 키로 종료
}
glutKeyboardFunc(key);

⏰ 타이머 콜백 함수

glutTimerFunc — 일정 시간 후 호출

void glutTimerFunc(unsigned int time, void (*func)(int id), int timerId);

🧠 왜 필요할까?

애니메이션, 주기적 위치 갱신, 타임 이벤트에 유용해요.

📌 전달되는 정보

  • time: 호출까지 대기 시간 (밀리초 단위)
  • func: 호출할 함수
  • timerId: 여러 타이머를 구분할 수 있는 ID

💡 예시

void tick(int id) {
    printf("타이머 호출됨! ID: %d\n", id);
    glutTimerFunc(1000, tick, id); // 1초 후 다시 호출
}
glutTimerFunc(1000, tick, 1);

📋 GLUT 메뉴 생성 함수 정리 — 마우스 우클릭 메뉴 만들기!

GLUT에서는 마우스 버튼(주로 오른쪽 클릭)을 눌렀을 때 메뉴를 띄우는 기능을 사용할 수 있어요.
이를 위해 제공되는 함수들이 바로 glutCreateMenu, glutAddMenuEntry, glutAddSubMenu, glutAttachMenu 입니다.

이번 글에서는 이 함수들을 초보자도 이해하기 쉽게 정리해볼게요!


🍔 메뉴를 만든다는 건?

“화면에 보이지는 않지만, 오른쪽 클릭 시 나타나는 메뉴를 등록해두는 것”

GLUT에서는 메뉴 항목마다 숫자 ID를 부여하고,
선택되었을 때 호출될 콜백 함수를 등록해야 해요.


✅ 1. glutCreateMenu() — 메뉴 생성 + 콜백 등록

GLint glutCreateMenu(void (*func)(int id));

🧠 왜 필요할까?

메뉴를 만들고, 항목이 선택되었을 때 처리할 함수를 등록하는 함수예요.

📌 인자 설명

  • func: 사용자가 메뉴를 선택했을 때 호출할 콜백 함수

💡 반환값

  • 생성된 메뉴의 ID를 반환 (나중에 서브 메뉴 등록에 사용 가능)

✅ 2. glutAddMenuEntry() — 메뉴 항목 추가

void glutAddMenuEntry(const char *label, int value);

📌 인자 설명

  • label: 메뉴에 표시될 항목 이름
  • value: 선택되었을 때 전달되는 ID (콜백 함수의 매개변수로 전달됨)

✅ 3. glutAddSubMenu() — 서브 메뉴 연결

void glutAddSubMenu(const char *label, int subMenuId);

📌 인자 설명

  • label: 부모 메뉴에 표시될 이름
  • subMenuId: 미리 만든 서브 메뉴의 ID

✅ 4. glutAttachMenu() — 메뉴를 버튼에 연결

void glutAttachMenu(int button);

📌 인자 설명

  • button: 어떤 버튼에 메뉴를 연결할지 (GLUT_RIGHT_BUTTON 등)

🛠 메뉴 생성 예제

📜 메뉴 구성

우클릭 메뉴 (CreateMenu)
├─ Point       (0)
├─ Line        (1)
├─ Polygon     (2)
├─ Color 메뉴
│   ├─ Red     (0)
│   ├─ Green   (1)
│   └─ Blue    (2)
└─ Exit        (3)

💡 코드 예시

// Color 서브메뉴 생성
GLint ColorMenuId = glutCreateMenu(ColorMenu);
glutAddMenuEntry("Red", 0);
glutAddMenuEntry("Green", 1);
glutAddMenuEntry("Blue", 2);

// 메인 메뉴 생성
GLint CreateMenuId = glutCreateMenu(CreateMenu);
glutAddMenuEntry("Point", 0);
glutAddMenuEntry("Line", 1);
glutAddMenuEntry("Polygon", 2);
glutAddSubMenu("Color", ColorMenuId);
glutAddMenuEntry("Exit", 3);

// 마우스 우클릭에 메뉴 연결
glutAttachMenu(GLUT_RIGHT_BUTTON);

📦 콜백 함수 예시

void ColorMenu(int id) {
    if (id == 0) printf("Red 선택!\n");
    else if (id == 1) printf("Green 선택!\n");
    else if (id == 2) printf("Blue 선택!\n");
}

void CreateMenu(int id) {
    if (id == 0) printf("Point 선택!\n");
    else if (id == 3) exit(0); // Exit 선택 시 종료
}

🔄 glutPostRedisplay() — 화면을 다시 그릴 때 쓰는 보조 함수

OpenGL 프로그램에서는 어떤 상황에서든 화면을 다시 그려야 할 순간이 생깁니다.
예를 들어:

  • 애니메이션처럼 객체가 움직일 때
  • 사용자의 입력으로 장면이 바뀔 때
  • 다른 이벤트로 화면이 갱신되어야 할 때

이때 필요한 함수가 바로 👉 glutPostRedisplay()입니다!


🧠 함수 설명

void glutPostRedisplay();
  • glutDisplayFunc()에 등록된 화면 그리기 함수(display 함수)강제로 다시 호출합니다.
  • 즉, "다시 그려줘!" 하고 GLUT에게 요청하는 역할이에요.

🎯 언제 사용할까?

  • 애니메이션 구현 시 객체가 계속 움직여야 할 때
  • 타이머 함수나 키보드 이벤트 등에서 장면을 갱신할 때

💡 사용 예시

void display() {
    glClearColor(1.0, 1.0, 1.0, 1.0); // 배경 색
    glClear(GL_COLOR_BUFFER_BIT);    // 화면 초기화

    // 객체 그리기
    // 위치나 상태 변화

    // 다음 프레임을 위해 다시 그리기 요청
    glutPostRedisplay(); 
}

📌 TIP

  • glutPostRedisplay()를 호출해도 즉시 다시 그리는 건 아니에요.
  • 대신 GLUT 이벤트 루프가 다음 번 화면 갱신 시점에 display()를 호출하도록 예약합니다.

이 함수는 콜백 함수는 아니지만, 콜백 함수들과 함께 자주 사용되는 필수 도구입니다.
특히 타이머 함수와 함께 쓰면 움직이는 그래픽을 구현할 수 있어요!