2015. 2. 7.

[OpenGL]사각형 그리기

 OpenGL을 사용하여 사각형을 그려보자. 단순하지만 기초적으로 알고 지나가야할 GLUT 함수들이 있다. 이런 함수에 대해서 언급하고자 한다.


사각형 그리기
 OpenGL 설치 및 설정에서 본 코드이다. 메인함수 내에 존재하는 코드는 모두 GLUT 함수로 구성되었다. 이 함수들은 앞으로도 계속 사용되므로 알아보고 넘어가자.
  • glutCreateWindow("OpenGL Draw Rect"): GLUT에게 새로운 윈도우를 생성하라는 명령이다. 사각형이 그려지는 윈도우가 바로 이 함수에 의해 만들어진다. 인자로 전달되는 문자열은 윈도우 상단의 타이틀바(title bar)에 나타난다.
  • glutDisplayFunc(MyDisplay): 이 함수는 'MyDisplay'라는 함수를 디스플레이 이벤트에 대한 콜백 함수로 등록한 것이다. 이 함수의 정의를 찾아보면 다음과 같다. extern void APIENTRY glutDisplayFunc(void (*func)(void)). 매개변수를 함수 포인터로 되어있다. 즉 매개변수로 전달한 함수는 디스플레이 이벤트마다 호출된다. 디스플레이 이벤트는 별일없으면 계속 발생한다.
  • glutMainLoop(): OpenGL의 메인함수는 항상 이 함수로 끝난다. 이 함수의 역할은 이벤트 루프를 돌리는 것이다. 이벤트별로 콜백 함수 등록을 모두 마쳤으니 이벤트 루프로 진입하라는 의미이다. 모든 지엘 프로그램은 항상 glutMainLoop() 함수로 끝난다.
 결국 윈도우를 하나 생성해서 디스플레이 이벤트 때마다 어떤 함수를 호출할지(무엇을 그릴지) 정하고 이벤트 루프를 돌린다.


디스플레이 콜백 함수

 콜백 함수(Callback Function)는 응용 프로그램이 이벤트를 처리하는 방법을 말한다. 이는 이벤트 처리기(Event Handler)라도고 하는데, 이벤트 타입별로 프로그램이 수행해야 할 내용을 함수로 나타낸 것이다. 예를 들어 키보드에서 'W' 키를 누르면 바라보는 물체가 확대되도록 한다던지, 'esc'키를 누르면 현재 응용 프로그램을 종료하도록 한다던지 등의 내용이 바로 콜백 함수에 정해진다. 즉 디스플레이 이벤트는 별일이 없다면 매 프레임마다 발생할 것이고, 콜백 함수로 정해진 MyDisplay() 함수가 반복적으로 호출될 것이다. 이제 화면을 실제로 그리는 MyDisplay() 함수를 살펴보자.
 위 코드는 디스플레이 콜백 함수인 MyDisplay() 함수이다. 실제로 화면에 그리는 내용이 이 함수에 구현되어 있다.

  • glBegin(GL_POLYGON): gl에게 그리기를 시작함을 알리는 함수이다. 무엇을 그릴지에 대한 정보는 인자로 전달된다. POLYGON을 그린다고 gl에 알린다. 그러면 다음에 제시되는 정점(vertex) 좌표들로 기하물체가 그려지게 된다. 여기서는 4개의 정점 좌표로 사각형을 그리고 있다. 참고로 정점 좌표는 윈도우의 한 가운데를 (0.0, 0.0, 0.0)으로 정의된 좌표계로 표시된다. 기하물체를 구성하는 정점 좌표 설정이 끝나면 glEnd() 함수를 호출한다.
  • glEnd(): 기하물체를 구성하는 정점 좌표에 대한 설정이 끝났다는 것을 gl에 알린다. 그러면 glBegin() 함수와 glEnd() 함수 사이에 있는 정점 정보들로부터 하나의 기하물체를 완성하고 그릴 수 있게 된다.
  • glFlush(): 그래픽 명령어를 처리하려면, 이 명령어를 수행하는 GPU(그래픽 프로세서)는 렌더링 파이프라인(Rendering Pipeline) 상의 프로세서와 교신해야 한다. 이는 I/O를 발생시키는 것이므로 하나의 명령어를 파이프라인으로 처리하는 것이 아니라 여러 명령어를 일정 분량 쌓아두었다가 한꺼번에 파이프라인 프로세서에 전달하는 것이 유리하다. 즉 명령어를 쌓아두었다가 파이프라인 프로세서에 전달하는 방식으로 처리된다. 하지만 glFlush() 함수는 더이상 명령어를 쌓지말고 지금가지 쌓인 명령어를 모두 프로세서에 바로 전달하도록 강제하는 함수이다. 렌더링을 위한 준비가 완료된 후 호출하는 함수로 이 함수를 통해 명령어를 GPU에 전달하지 않으면 아무것도 그려지지 않는다. 실제로 이 함수를 주석으로 처리하고 실행해보고, 주석을 해제하고 실행해보도록 하자.


입력 콜백 함수

 glutMainLoop() 함수는 이벤트별로 콜백 함수 등록을 다 마치고 이벤트 루프로 진입하라는 명령이었다. 위의 코드에서는 디스플레이 콜백 함수만 존재한다. OpenGL에서는 GLUT 라이브러리를 통해서 윈도우 및 입출력을 제어할 수 있다. GLUT를 사용하여 프로그래머는 필요한 콜백 함수를 등록하고, 해당 콜백 함수에 원하는 내용을 구현하면 된다. 이벤트가 발생하면 해당 콜백 함수를 호출하는 것은 GLUT가 알아서 처리한다. 프로그래머가 등록한 콜백 함수는 콜백 테이블(Callback Table)에 저장되고, GLUT는 이벤트가 발생하면 이 테이블을 참조하여 콜백 함수를 호출한다. 이벤트는 큐(Queue)를 통해 관리된다. GLUT는 이벤트 루프를 돌면서 큐에 이벤트가 있으면 가장 먼저 발생한 이벤트를 가져와서 테이블을 참조하여 함수를 호출한다.

 그렇다면 이번에는 입력 콜백 함수를 만들어보자. 디스플레이 콜백 함수를 등록하는 glutDisplayFunc() 함수와 마찬가지로 입력 콜백 함수를 등록하는 GLUT 함수도 당연히 있다. glutKeyboardFunc(void (*func)(unsigned char key, int x, int y)) 함수가 바로 입력 콜백 함수를 등록하는 함수이다.

 'Q' 키를 누르면 프로그램 실행을 종료하는 키보드 이벤트를 만들고 싶다면 어떻게 해야할 것인가? 먼저 'Q' 키를 눌렀을 때 프로그램을 종료하도록 하는 코드를 콜백 함수에 넣어야 한다. glutKeyboardFunc() 함수의 인자를 보면 unsigned char 한 개와 int 두 개를 매개변수로 하는 함수의 포인터이다. 여기서 char가 바로 어떤 키를 눌렀는지를 의미한다. 아래의 코드는 'Q' 키를 누르면 종료되는 콜백 함수이다.


 이 외에도 마우스 콜백, 메뉴 콜백, 타이머 콜백 등이 있다. 이런 콜백 함수를 정의하고 GLUT를 사용하여 등록해주면 된다.


정리하면

  • 특정 이벤트가 발생하면 실행되는 함수를 콜백 함수가로 부른다.
  • OpenGL에서 이벤트는 큐로 관리되며 이벤트 루프를 돌면서 이벤트를 처리한다.
  • 콜백 함수를 작성하고, 이 함수를 GLUT를 통해서 등록하면 이벤트 처리는 GLUT에서 알아서 해준다.

댓글 없음:

댓글 쓰기