- 게임보이 컬러에서 실시간 3D 셰이딩을 구현한 프로젝트로, 플레이어가 빛의 궤도를 조작하며 물체를 회전시킬 수 있음
-
정규화 벡터와 램버트 셰이딩(dot product) 계산을 기반으로, 구면좌표계를 이용해 연산을 단순화함
-
곱셈 명령이 없는 SM83 CPU의 제약을 극복하기 위해 로그 변환과 룩업 테이블을 활용, 8비트 정밀도로 연산 수행
-
자기 수정 코드(self-modifying code) 를 사용해 약 10%의 성능 향상을 달성하고, 프레임당 15개의 타일을 렌더링함
- AI를 활용한 코드 생성은 대부분 실패했으며, 핵심 알고리듬과 셰이더는 직접 작성한 수작업 코드로 완성됨
프로젝트 개요
- 게임보이 컬러에서 실시간으로 이미지를 렌더링하는 게임을 제작
- 플레이어는 궤도 형태의 빛을 조작하며 물체를 회전시킴
- 전체 코드는 GitHub 저장소(nukep/gbshader)에서 공개됨
3D 제작 과정
-
Blender를 사용해 초기 룩 개발(lookdev)을 진행, 결과가 시각적으로 만족스러워 프로젝트를 진행함
-
Cryptomatte와 커스텀 셰이더를 이용해 노멀맵(normal map) 을 생성
- 찻주전자(teapot) 모델은 카메라를 회전시켜 PNG 시퀀스로 노멀맵을 출력
- 게임보이 컬러 모델의 화면 부분은 별도 장면으로 렌더링 후 합성
수학적 기반
- 노멀맵은 각 픽셀의 법선 벡터를 인코딩하는 벡터 필드로 사용
-
램버트 셰이딩은 v = N·L 형태의 내적(dot product)으로 계산
- 구면좌표계로 변환해 v = sinNθ sinLθ cos(Nφ−Lφ) + cosNθ cosLθ 형태로 단순화
- 모든 벡터의 반지름 r=1로 가정해 연산량을 줄임
게임보이에서의 구현
-
Lθ(빛의 세로각) 을 상수로 고정하고, Lφ(빛의 회전각) 만 플레이어가 조작
- ROM에는 각 픽셀을 (Nφ, log(m), b) 형태로 저장
-
곱셈 명령 부재를 해결하기 위해 로그 변환과 룩업 테이블(log, pow)을 사용
- 부호(sign) 비트를 상위 비트에 저장해 음수 연산 지원
- 모든 스칼라 값은 -1.0~+1.0 범위의 8비트 분수로 표현
- 덧셈은 선형 공간에서, 곱셈은 로그 공간에서 수행
- 127을 분모로 사용해 ±1을 모두 표현 가능하게 함
cos_log와 핵심 연산
-
cos_log는 log(cos x) 형태의 결합 룩업으로, 곱셈을 로그 덧셈으로 대체
- 픽셀당 연산량
- 뺄셈 1회, cos_log 조회 1회, 덧셈 1회, pow 조회 1회, 덧셈 1회
- 총 3회 덧셈/뺄셈, 2회 룩업 수행
성능
-
프레임당 15개의 타일을 처리, 일부 빈 행은 더 빠르게 계산
- 픽셀당 약 130 사이클, 빈 행은 3 사이클 소요
- CPU의 약 89% 가 셰이더 연산에 사용되며, 나머지는 입력 및 I/O 처리에 사용
자기 수정 코드(Self-Modifying Code)
- 프레임당 약 960픽셀을 처리하는 핵심 루프를 최적화하기 위해 명령어 자체를 수정
- 상수를 직접 코드에 삽입해 변수 로드보다 빠른 연산 수행
- 예: sub a, 8이 sub a, variable보다 12사이클 빠름
- 전체적으로 약 11,520 사이클(10%) 절감
AI 활용 시도
- 전체 프로젝트의 95%는 수작업으로 작성
- AI는 Game Boy 어셈블리(SM83) 작성에 어려움을 겪음
- AI 사용 내역
- Python: OpenEXR 레이어 읽기
- Blender: 장면 자동화 스크립트
- SM83: 일부 기능 스니펫 (예: VRAM DMA)
- 실패한 시도
- AI로 셰이더 어셈블리 코드 생성 시도 → 비효율적이고 오류 다수
- Claude Sonnet 4 모델을 이용해 의사코드에서 어셈블리 생성 시도
- 일부 작동했으나 느리고, Z80과 SM83 혼동 등의 오류 발생
- 최종 코드는 수동으로 완전 재작성
결론 및 교훈
- AI는 단순 스크립트에는 유용하지만, 정확성과 검증이 필수
- OpenEXR 처리 코드에서 AI가 채널 정렬 오류(BGR vs RGB) 를 일으켜 수주간 버그 발생
- 경험을 통해 “AI 사용 시 검증이 가장 중요하다”는 교훈을 강조
- 프로젝트는 레거시 하드웨어의 한계를 극복한 실험적 셰이더 구현 사례로 평가됨