1993년처럼 그래픽 만들기
출처: Hacker News
Catlantean 3D는 1년 넘게 여가 시간에 천천히 만들어 온 사이드 프로젝트이며, 내년에는 스팀에 출시할 예정입니다.
브라우저가 video 태그를 지원하지 않습니다.
내 목표는 90년대 초반에 흔히 사용되던 기술들을 활용해 완전하고 배포 가능한 1인칭 슈팅 게임을 만드는 것이었으며, 동시에 현대 컴파일러와 플랫폼 추상화 레이어를 사용할 수 있는 여유를 갖는 것이었습니다.
실제로 의미하는 바는, 내가 스스로에게 어리석게 부과한 제약 조건은 다음과 같습니다:
- 게임은 에셋을 포함해 전부 처음부터 직접 만들어야 함
- 모든 렌더링을 손수 구현해야 함
- 모든 사운드 믹싱을 손수 구현해야 함
- 목표 해상도 320×240
- 색상은 256색만 사용
- 부동소수점 사용은 허용하지만 동작은 플랫폼 간에 일관되어야 함
- 게임 로직은 결정론적 동작을 보장하기 위해 고정소수점으로, 렌더링은 결정론성이 크게 중요하지 않으므로 부동소수점 사용
- 완성도 높고 다듬어진, 재미있는 게임이어야 함(기술 시연이 아님)
플랫폼 추상화 레이어는 허용되지만, 매우 제한된 것처럼 가장해야 합니다(합리적인 범위 내에서):
- 픽셀을 기록할 프레임 버퍼
- 키보드/마우스 입력
- 샘플을 기록할 오디오 버퍼
- 파일 시스템 I/O
- AI 관련 로직은 금지
이게 비현실적이라고 생각한다면, 실제로 비현실적인 것이기 때문입니다.
하지만 나는 여전히 진행 중이며, 오늘은 개발 블로그에서 흔히 간과되는 에셋 제작에 대해 이야기하려 합니다.
참고: 여기서 보여지는 모든 내용은 진행 중인 작업이며, 크게 변경될 수 있습니다.
목차
변경 로그
- 2026-06-09 – 공개.
Palette Rendering
VGA Graphics
VGA 하드웨어의 Mode 13h는 320×200, 256색 그래픽 모드로, 한 세대의 PC 게임을 정의했습니다. 프로그래머 입장에서는 매우 단순했습니다: 각 픽셀은 256색 팔레트의 인덱스를 나타내는 1바이트로 표현되는 선형 프레임 버퍼가 있었기 때문이죠.
픽셀을 그리려면 특정 주소에 바이트를 쓰면 끝났습니다. 셰이더나 VRAM 같은 개념은 전혀 없었습니다.
픽셀당 1바이트이며, 그 바이트는 실제 RGB 값을 담고 있는 팔레트의 인덱스입니다. 이는 흥미로운 제약을 만들죠; 현대 게임에서는 수백만 색을 자유롭게 사용할 수 있지만, 화면의 모든 픽셀이 256색 중 하나만 될 수 있다면 에셋 제작은 전혀 다른 문제가 됩니다. 색을 선택할 때마다 신중하고 의도적인 선택이 필요합니다.

Doom과 Duke Nukem 같은 게임이 바로 이런 제약을 잘 활용한 좋은 예입니다. 이 그래픽에는 제한 때문에 생기는 선명함과 명료함이 있습니다. 제한은 의도적인 선택을 강제하고, 의도적인 선택은 보기 좋게 보입니다.
Catlantean 3D는 그 느낌을 재현하려는 시도이지만, 한 가지 전제가 있습니다 – 나는 실제로 VGA Mode‑X, 즉 320×240 해상도를 목표로 하고 있습니다. 그 이유는 4:3 화면에 320×200을 표시하면 픽셀이 정사각형이 아니게 되기 때문입니다! 가장 정통적인 방식이지만, 나는 객관적인 이유보다는 개인적인 선호 때문에 이 문제를 피하기로 했습니다.
그렇다면 이 제한 안에서 그래픽을 어떻게 만들 수 있을까요?
The Palette
모든 것은 768바이트, 즉 256색 × 3채널(RGB)에서 시작됩니다. 이 색들은 수많은 시행착오를 거쳐 신중히 선택되었습니다.

이 정확한 색들을 고른 주요 이유는 다음과 같습니다:
- 투명도를 위한 색 하나(선명한 핑크)
- 순수 흰색 하나
- 순수 검정색 하나
- 피를 표현해야 하니 빨간색이 많이 필요
- 빨강·초록·파랑 키와 색코드 문을 만들기 위해 초록·파랑 계열
- 게임 배경은 고양이 신앙을 풍자한 고대 이집트와 비슷한 ‘캣란티스’이므로, 사막 색(노랑·갈색) 다수
- 사이버 개인(강아지 인간) 점령 하에 있는 기술 시설이 많아 회색 계열 다수
- 회색만 쓰면 지루해지니 베이지 톤을 섞어 따뜻함을 보강(어두워질 때 대체 색)
- 나머지는 텍스처를 만들면서 필요에 따라 채워 넣음 – 주관적이며 “그냥 맞아 보였다” 정도로 설명
팔레트는 한 번에 완성되지 않았습니다. 에셋을 만들고, 테스트하고, 다시 수정하는 과정을 반복하면서 점진적으로 완성되었습니다.
아래는 실제 게임에 사용된 스프라이트와 텍스처 예시입니다:



The Colormap
Catlantean 3D는 전통적인 레이캐스터입니다. 맵은 모두 같은 크기의 타일들로 이루어져 있으며, 일부는 벽, 일부는 바닥·천장이 있는 빈 공간입니다. 화면의 각 컬럼마다 DDA 알고리즘을 사용해 타일맵을 탐색하고, 레이와 충돌한 지점을 찾아 해당 텍스처를 샘플링해 벽 컬럼을 그립니다. 바닥·천장은 이후에 가로 스캔라인으로 채워 화면을 완성합니다.
레이캐스팅 자체는 이미 수많은 블로그와 웹사이트에서 다루어졌으니 여기서는 자세히 설명하지 않겠습니다. 대신 제가 가장 간과하기 쉬운 조명 부분을 다루고 싶습니다.
팔레트만 사용하고 특별한 효과를 넣지 않으면, 게임 세계는 다음과 같이 평평하고 인상적이지 않게 보입니다:

우리가 원했던 모습은 이렇습니다. 플레이어와 멀어질수록 조명이 약해지고, 맵 타일의 한쪽 면이 다른 쪽보다 약간 어두워지는 것을 볼 수 있습니다. 이는 깊이감을 부여합니다.

현대 하드웨어 가속 렌더러라면 셰이더 하나로 손쉽게 구현합니다 – 정점이 카메라와 얼마나 떨어졌는지에 따라 색 벡터에 부동소수점 계수를 곱해 어두워진 색을 얻으면 되죠.
하지만 팔레트 렌더러에서는 색 개념이 없고, 오직 팔레트 인덱스만 존재합니다. 특정 색의 더 어두운 변형을 찾으려면 팔레트를 일일이 탐색해 “더 어두운” 색을 찾아야 하는데, 화면의 모든 픽셀에 대해 매번 전체 팔레트를 순회하면 너무 느립니다.
대신 우리는 전처리를 통해 런타임에 거리 기반 색 인덱스를 빠르게 찾을 수 있도록 합니다.
팔레트를 한 줄로 나열하면 다음과 같습니다…
![](https