컴퓨터 그래픽스와 OpenGL 그리고 학기 프로젝트
3D 프로그래밍을 처음 경험할 수 있는 컴퓨터 그래픽스 전공 수업을 떠올리며 이 글을 쓴다. 일단 컴퓨터 그래픽스 수업은 내가 재학 중인 산기대 게임공학과에서 2학년 전공 필수로 교과목체계가 잡혀있으니 피해갈 수 없다.
컴퓨터 그래픽스
교과목 개요: 그래픽스의 기본 입출력, 그래픽 환경 설정에 관해서 알아보고, 그래픽 라이브러리의 사용법을 익힌다. 2차원에서의 물체의 표현 및 변환에 관해서 학습한다. 또한, 3차원에서의 물체의 표현 방법, 그리고 모델링 등의 다양한 알고리즘을 배우고 실습한다.
수업 목표: 그래픽스 API인 OpenGL을 기반으로 2차원 및 3차원 그래픽스 알고리즘을 구현하고, 3차원 애플리케이션 프로그램을 개발하도록 한다.
수업은 일주일에 두 번 4시간이다. 이론 2시간, 실습 2시간 형태로 수업을 진행한다. 이론은 컴퓨터 그래픽스에 대한 전반적인 개요를 시작으로 수학적인 지식이나 원리에 기반을 둔 여러 가지 그래픽스 기법을 공부한다. 실습은 학습한 이론을 바탕으로 컴퓨터 그래픽 라이브러리 OpenGL을 사용하여 애플리케이션을 구현한다.
OpenGL과 실습
OpenGL
OpenGL은 실리콘 그래픽스 사에서 만든 2, 3차원 그래픽스 표준 API 규격이다. 즉, 그래픽스 하드웨어 드라이버가 제공하는 기능을 OpenGL 인터페이스를 사용하여 그래픽스 응용 프로그래밍 또는 게임 프로그램을 작성할 수 있다고 생각하면 된다.
실습
실습은 OpenGL의 메인 라이브러리인 gl과 유틸리티 라이브러리 glu, 그리고 윈도우 프레임워크 성격을 가지고 있는 glut 라이브러리를 사용해 진행한다. 매주 3 ~ 6개의 실습 문제를 과제로 주어지고, 이를 채점함으로써 학생의 성적을 매긴다. 또한, 실습 문제 이외에도 학기 중 두 번의 과제를 주고 난이도는 실습 문제보다 높다. 기간은 약 2주 정도 준다. 한마디로 과제의 연속이고, 낙오자가 속출한다. 중간 / 기말고사는 그래픽스 이론에 대한 이해도를 채점한다.
느낀 점
항상 2D 게임만 제작해본 경험이 있기에 3D에 대한 막연한 두려움과 새로운 기술을 배운다는 설렘으로 시작했다. 결론은 매우 훌륭한 수업이었다. 매주 있는 실습 과제에서 경험해본 3D 기법은 충분히 나의 욕구를 채웠다.
그러나 조금 아쉬운 점도 있다.
- 프레임워크 성격을 가진 glut 라이브러리를 사용한다.
- 이 프레임 워크는 이벤트 핸들링, 게임 루프, 왼도우 등 모두 제공 하므로 게임 프레임워크 설계에서는 게임공학과적인 배움을 경험할 수 없었다.
- 고정된 기능 파이프라인(Fixed Function Pipeline) 방식이다.
- 정점, 텍스쳐, 조명 등을 모두 셰이더 프로그래밍을 배제하고 고정된 파이프라인 안에서 API를 이용해 접근한다.
이 방식의 수업은 장단점이 모두 존재하는 데 장점은 쉽게 3D를 경험할 수 있으므로 3D에 대한 두려움을 극복시켜준다는 것이고, 단점은 3학년 교과목인 DirectX를 이용한 3D 프로그래밍에서 매우 낯설어 한다는 것이다. 3학년이 된 후 DirectX 수업에서 OpenGL에서 경험한 glVertex, glNormal, glMaterial, glLight 와 같은 함수를 찾으며 절망에 빠지는 학우들을 많이 봤다.
학기 프로젝트
수많은 과제와 중간/기말고사 이외에도 학기 프로젝트를 진행해야 한다. 한 학기 동안 최대 3인 팀을 구성해 게임을 제작하는 프로젝트로, 나는 게임공학과에 복수 전공 중인 디자인과 친구와 2인 팀을 구성했다. 이 친구는 프로그래밍을 잘 못 하지만 3D 모델링을 잘한다는 점에서 나와 궁합이 맞았다.
게임은 timelocker라는 슈팅게임을 모작하기로 했다.
하지만...
그런데 엉뚱하게도 학기 내내 나의 관심은 다른데 쏠려있었다. 학기 내내 나는 나만의 게임 프레임워크, 고정된 파이프라인 방식이 아닌 파이프라인을 이해하며, 셰이더 프로그래밍을 해야 한다는 욕심에 사로잡혀있었다. 타이머 클래스, 윈도우 클래스 등 게임에 필요한 기초 클래스부터 셰이더 컴파일, 정점, 인덱스, 텍스쳐 버퍼, 조명 프래그먼트 계산 등 2달여간 텀프 제출일 전까지 혼자 낑낑대며 작성해 나갔지만 결국 게임에 적용할 정도로 완성도 있는 프레임워크는 작성하지 못했다.
지금 생각해보면 너무 무리했다. 게임 개발이 아닌 게임 프레임워크 개발에 텀프 제출 하루 전날까지 투자를 했으며(하지만 이때 그 무엇보다 재밌었다.), 정작 게임 개발은 제출 당일 오전에 결국 glut 프레임워크를 이용하며, 고정된 파이프라인 아래에서 개발했다.
게임을 할 때 항상 개발자의 시선으로 바라본다. 평소 timelocker 게임을 할 때도 "이 박스 이펙트는 어떤 식으로 구현되어있을까?", "뒤에 지형이 사라지는 건 어떤 원리일까?" 이렇게 생각하며 게임을 하고 나름 머릿속에서 코드를 생각하기에, 짧은 시간 안에 게임을 만들 수 있었던 것 같다.
개발 과정
약 2시간 후 모델러 팀원이 도착한다는 말을 듣고 glut 라이브러리를 이용해 기본적인 게임 프레임워크를 제작합니다. 이때 glut 라이브러리는 c로 작성된 라이브러리이기 때문에 객체지향과는 조금 안 맞는 부분이 있습니다. glut의 이벤트 핸들링에서, 전역 변수 사용을 극히 싫어하므로 람다식과 캡처를 이용한 접근을 해보지만, 모종의 이유로 자꾸 실패합니다. 결국, freeglut 라이브러리의 glutMainLoopEvent()를 사용함으로써 전체적인 게임 루프를 커스텀 합니다.
캐릭터 오브젝트를 생성하고 키보드 입력에 따른 이동을 구현합니다. 모델링을 띄우기로 했습니다. 애니메이션까지 구현하기 위해 가장 많이 쓰이는 모델 확장자는 fbx입니다. 그러나 저는 fbx 로더를 만들 시간도 열의도 없었습니다. 그냥 obj 파일 형식을 받습니다. 파일을 열어보니 v, vt, t 등으로 이루어진 문자열입니다. 구글 검색을 해보니 정점, 노말, 텍스쳐 등의 위치라고 합니다. 간단한 파일처리와 파싱 로직을 만들고 OpenGL의 고정된 파이프라인에 때려 박습니다.
모델링 애니메이션을 구현합니다. obj 파일은 애니메이션을 지원하지 않으므로 그냥 2D 게임처럼 obj를 여러 프레임 만들어 놓고 바꿔주는 식으로 구현합니다. 정점이 몇 개 없어서 그런지 훌륭히 애니메이션이 됩니다.
텍스쳐를 입힙니다. 여기서 문제는 모델러가 텍스쳐를 만들 시간이 충분하지 않으므로 단색으로 처리한 후 glLight 함수를 이용해서 조명처리를 합니다.
몬스터를 생성합니다. 몬스터를 생성할 시 일일이 패턴으로 할지 무작위로 생성할지 잠깐 고민을 하다가, 1000px를 100m 단위로 생각해서 100m마다 레벨을 증가시켜 레벨에 맞게 생성하는 주기를 바꿉니다.
다음은 총알 생성입니다. 그냥 생성해서 쏩니다.
충돌처리를 구현합니다. 3차원 게임이지만 y축은 고정이므로(이 당시 점프가 없습니다.) 그냥 2D 게임처럼 범위 기반, AABB Collision을 사용해 금방 처리합니다.
점수, 다시 하기 등의 로직과 UI를 구현합니다.
충돌 후 박스가 터져야 합니다. 이 부분은 그냥 사각형을 랜덤한 크기로 여러 개 만든 후 xyz 3방향으로 랜덤하게 이동을 통해 구현합니다.
행복하게 잠이 듭니다.
다음날 학교에 가니 오전 발표한 친구들이 교수님이 점프와 시점변경을 아주 좋아한다는 정보를 줍니다.
시점변환을 줍니다. 이때 조금 헤맸지만 glutlookat 함수에서 바라보는 지점을 어떻게 세팅하느냐가 관건이었습니다.
점프 기능을 넣습니다. 요런? 점프를 주니 y축이 바뀝니다. 충돌처리에 y축도 검사하는 로직을 추가합니다.
사운드를 넣을 차례입니다. 비주얼 스튜디오의 훌륭한 도구인 nuget에서 사운드 라이브러리를 하나 찾아 넣습니다. 구글을 이용해 API 사용법을 익히고 wav 파일을 재생시킵니다.
마지막으로 게임이 좀 더 역동적으로 보이기 위해 피격 후 카메라를 일정 시간 동안 흔드는 방식으로 떨림 효과를 추가합니다.
비록 OpenGL을 이용한 프레임워크 제작에는 실패했지만 현재 졸업 프로젝트를 위한 Direct3D를 이용한 프레임워크 제작에는 많은 도움이 되고 있다.