3P by GN⁺ | ★ favorite | 댓글 1개
  • Ctrl-Alt-Test 팀은 2022년 11월부터 단편 애니메이션처럼 보이는 실시간 데모를 8kB 안에 담는 실험을 시작해, 2023년 4월 The Sheep and the Flower를 공개함
  • 결과물은 리소스 파일이나 외부 의존성 없이 단일 .exe가 그래픽과 음악을 생성하며, 장면은 GLSL 셰이더와 레이마칭으로 GPU에서 실시간 계산됨
  • 양, 표지판, 카메라, 타임라인은 저장된 모델이나 텍스처가 아니라 SDF, 절차적 3D 텍스처, 하드코딩된 시간 파라미터로 구성됐고 약 25개의 카메라 샷이 수작업으로 조정됨
  • 음악은 OpenMPT4klang으로 만들었고, 악기 수를 16개에서 13개로 줄이고 반복을 늘려 압축 효율을 높임
  • 최종 8kB는 Crinkler 압축과 Shader Minifier 개선으로 가능했으며, 42kB 셰이더 코드는 축소와 압축 후 약 5kB에 들어가 음악과 C++ 코드 공간을 남김

8kB 안에 담긴 단일 실행 파일

  • The Sheep and the Flower는 2023년 4월 공개된 Windows 실행 파일 형태의 실시간 애니메이션임
  • 하나의 .exe 파일이 모든 것을 생성하며, 별도 리소스 파일이나 외부 의존성이 없음
    • 실행에는 Windows와 최신 드라이버가 필요함
  • 목표는 8kB 안에 그래픽, 애니메이션, 연출, 카메라워크, 음악을 모두 넣는 것이었음
  • 소스 코드는 GitHub에 공개됨: ctrl-alt-test/mouton

그래픽·음악·압축에 쓰인 도구들

  • 모든 시각 요소는 GPU에서 실시간 계산
    • GLSL 셰이더가 타임라인 정보, 카메라 설정, 장면 표현까지 포함함
    • 렌더링은 레이마칭(raymarching)으로 수행됨
  • 셰이더는 Shader Minifier로 축소됨
  • 음악은 OpenMPT4klang 신시사이저로 제작됨
    • 악기는 절차적으로 기술됨
    • 음표 목록은 압축됨
  • 코드는 Visual Studio 2022에서 C++로 작성됨
  • 컴파일러 플래그와 초기화는 Leviathan 프레임워크를 기반으로 시작함
  • 최종 실행 파일은 Crinkler로 압축됨

왜 8kB였나

  • 데모씬에는 4kB와 64kB 같은 크기 제한 카테고리가 흔함
  • 기존 작업은 주로 64kB를 목표로 했지만, 8kB는 전혀 다른 제약과 규칙을 제공함
  • 4kB 인트로 기법은 흥미롭지만 제대로 된 스토리텔링에는 너무 제한적이라고 봄
  • Revision demoparty가 몇 년 전 8kB 경쟁 부문을 추가했고, 이를 실험할 좋은 기회로 삼음

두 개의 삼각형으로 그리는 세계

  • 데모씬에서 2008년 이후 표준적으로 쓰인 방식처럼, 화면 전체를 덮는 사각형을 두 개의 삼각형으로 그림
  • 이 사각형 위에서 GLSL 셰이더가 각 픽셀과 각 프레임의 색상을 계산함
  • 셰이더 함수는 좌표와 시간을 입력받아 색상을 반환함
  • 핵심 과제는 이 함수 안에서 양과 장면 전체를 어떻게 표현하느냐였음

SDF로 장면 구성하기

  • 부호 있는 거리장(SDF) 은 공간의 한 점에서 가장 가까운 물체까지의 거리를 계산하는 함수임
    • 표면 위에서는 0을 반환함
    • 물체 내부에서는 음수를 반환함
  • 구나 상자 같은 기본 도형은 간단한 수식으로 표현할 수 있음
  • 여러 거리장을 조합해 장면을 구성함
    • 두 물체의 합집합은 거리의 최솟값으로 만들 수 있음
    • 교집합은 최댓값으로 만들 수 있음
    • 부드러운 합집합(smooth union)을 쓰면 더 유기적인 형태를 만들 수 있음
  • 양은 원뿔, 구 같은 단순 도형을 조립하고 병합해 만들었으며, 몸통과 털은 3D 노이즈 함수로 구성됨

레이마칭으로 픽셀을 결정하는 방식

  • 레이마칭은 SDF를 이용해 3D 장면 안에서 광선을 조금씩 전진시키는 렌더링 기법임
  • 전통적인 레이트레이싱처럼 교차점을 수학적으로 바로 계산하지 않음
  • SDF가 충돌 없이 이동할 수 있는 거리를 알려주고, 그만큼 광선을 전진시키는 과정을 반복함
  • 거리가 0 또는 0에 가까워지면 교차점을 찾은 것으로 처리함
  • 교차점이 생기면 현재 픽셀이 양, 하늘, 다른 물체 중 무엇에 해당하는지 알 수 있음
  • 조명에는 표면 법선이 필요하며, 주변의 기울기를 계산해 법선을 추정함
  • 그림자는 교차점에서 태양 방향으로 또 다른 광선을 보내 중간에 물체가 있는지 확인해 계산함

작은 표현력으로 이야기를 전달하는 법

  • 데모에는 목소리나 글자가 없고, 캐릭터도 하나뿐임
  • 양은 걷기, 머리와 눈 움직임 정도만 가능하며, 회전도 할 수 없음
  • 이 제약 안에서 감정을 전달하기 위해 이야기와 무관한 요소를 제거함
    • 처음에는 사막과 모래언덕, 하늘을 생각했지만 이야기에는 필요 없다고 판단함
    • 배경은 순백으로 유지함
    • 의미를 전달하는 표지판과 눈을 제외하고 텍스처를 줄임
  • 작은 범위를 유지해 디테일, 폴리싱, 카메라워크, 편집, 음악 동기화에 집중함
  • 각 샷은 수작업으로 구성됐고, 애니메이션은 흐름이 자연스럽게 느껴지도록 여러 번 반복 조정됨

카메라와 연출

  • 카메라는 이야기 전달 도구로 사용됨
  • 양이 오랜 시간 방황하는 장면에는 넓은 샷을 사용해 고독감을 만듦
  • 표지판을 알아차리는 순간에는 눈의 익스트림 클로즈업을 사용함
  • 양이 표지판을 바라볼 때는 머리 쪽으로 느리게 줌인해 집중도를 높임
  • 양의 흥분은 여러 표현을 겹쳐 전달함
    • 만화적인 2D 효과
    • 빨라진 걸음
    • 강조된 꼬리 움직임
    • 과장된 머리 흔들림
    • 음악의 극적인 변화

빠른 반복을 위한 개발 방식

  • 소스 코드에는 많은 하드코딩 상수가 들어 있음
    • 양의 눈 크기
    • 카메라 이동 속도
    • 각 샷의 길이
    • 꽃의 색상 같은 값들이 반복 조정됨
  • 빠른 피드백을 위해 셰이더를 런타임에 다시 컴파일해 1초 안에 그래픽을 갱신함
  • 애니메이션과 카메라 조정을 위해 플레이어가 필요했음
    • 일시정지
    • 재생
    • 시간 제어
    • 셰이더 라이브 리로드 후 즉시 확인
  • 음악과 애니메이션을 정확히 동기화하려면 음악 지원도 필수였음
  • 초기 프로토타입은 Shadertoy에서 만들었고, 이후 KodeLife를 거쳐 C++ 프로젝트로 옮김

4klang으로 만든 음악

  • 음악은 이야기 전달에 중요한 요소였고, 여러 분위기와 특정 시점의 전환이 필요했음
  • 4kB 인트로에서 쓰는 도구를 그대로 사용하되, 더 복잡한 구성을 위해 공간을 더 배정함
  • 4klang synthesizer는 음악 소프트웨어에서 사용할 수 있는 플러그인을 제공하고, 어셈블리 파일을 내보냄
  • 이 어셈블리 파일은 데모와 함께 컴파일·링크됨
  • 데모 실행 시 신스가 별도 스레드에서 파형 사운드를 절차적으로 생성해 사운드카드로 보냄
  • 처음 만든 음악은 예상보다 컸고, 크기를 줄이기 위해 여러 조정을 거침
    • 악기 수를 16개에서 13개로 줄임
    • 엔딩 테마를 전체 음악 템포에 맞게 다시 작곡함
    • 반복을 늘려 압축이 잘 되도록 구성함
    • 배경음의 음표 길이처럼 귀로는 거의 느껴지지 않는 변경으로 압축률을 개선함
  • 음악의 전체 구조는 유지하면서 품질 손실을 최소화하고 공간을 절약함

애니메이션과 시간 동기화

  • 데모는 사전 계산이나 캐시 없이 매 프레임 모든 것을 다시 평가함
  • 성능에는 불리하지만, 어떤 요소든 시간에 의존해 변할 수 있어 애니메이션에는 유리함
  • 전체 데모는 약 25개의 수작업 카메라 샷으로 구성됨
  • 각 샷은 18개 파라미터가 시간에 따라 어떻게 변하는지 기술함
    • 각 물체의 위치
    • 양의 상태
    • 카메라 위치
    • 초점
    • 카메라가 바라보는 대상 등
  • 예시로 camPos = vec3(22., 2., time*0.6-10.); 한 줄만으로 샷의 카메라 선형 이동을 표현함
  • time은 데모 시작 이후 절대 시간이 아니라 샷 시작 이후 시간을 뜻함
    • 샷을 삽입, 삭제, 조정해도 나머지 데모에 영향을 덜 줌
  • 단순 선형 보간은 기계적으로 보일 수 있어, 여러 경우에 smoothstep을 사용함
  • 타임라인 코드는 vertex shader 안에 있음
  • 샷 정의에 반복 코드가 많지만, 반복은 압축에 유리하게 작용함

텍스처와 재질

  • 전통적인 렌더러에서는 2D 텍스처를 3D 모델 표면에 입힘
  • 레이마칭 방식에서는 텍스처 좌표를 쉽게 계산하기 어려워, 대신 3D 텍스처를 즉석 계산
  • 레이마처가 렌더링할 3D 위치를 찾으면, 해당 3D 좌표를 텍스처 함수에 전달함
  • 교통 표지판은 수학으로 삼각형이나 사각형 테두리를 만들고, 내부 콘텐츠는 여러 함수를 조합해 그림
    • 식당 기호는 검은 타원형 모양 4개와 홈을 만드는 흰색 모양 2개로 구성됨
  • 시각적 다양성을 위해 텍스처뿐 아니라 재질도 다르게 설정함
    • 예를 들어 양의 발굽은 프레넬 계수를 이용해 다른 반사를 갖도록 처리함

눈을 살리는 가짜 반사

  • 개발 중 오랫동안 눈이 생기 없고 밋밋하게 보였음
  • 눈은 캐릭터 디자인과 이야기 전달에서 중요한 요소였음
  • 만화 캐릭터 참고 이미지에서는 홍채가 많지만 필수는 아니며, 동공은 항상 크게 그려지는 경향을 확인함
  • 반짝이는 눈을 만들려면 눈 안에 빛 반사가 필요했음
  • 기존 조명 방정식으로는 태양과 카메라가 특정 위치에 있을 때만 반사가 생겼음
  • 이를 보완하기 위해 표면 법선 벡터를 수정해 태양 반사가 생길 확률을 높임
  • 추가로 환경 매핑을 사용함
    • 일반적으로 게임에서는 실제 환경 반사를 단순화해 최적화하는 데 쓰임
    • 여기서는 실제 세계가 완전한 흰색에 가깝지만, 텍스처로 디테일을 가짜로 추가함
  • 눈의 흰자와 동공에는 빈 세계보다 훨씬 복잡한 반사가 들어감
    • 여러 가짜 광원
    • 어두운 지면과 푸른 하늘을 흉내 내는 그라디언트

후처리와 시각 스타일

  • 최종 분위기와 이미지 품질은 후처리에서 다듬음
  • 사용한 후처리는 다음과 같음
    • 컬러 그레이딩
    • 감마 보정
    • 약한 비네팅
    • 앨리어싱을 줄이기 위한 2패스 FXAA 필터
  • 눈의 별 효과나 엔딩 화면 효과처럼 일부 효과도 후처리 단계에서 구현됨
    • 이 효과들은 순수 2D이며 3D 세계에는 존재하지 않음
  • 한때 오래된 만화풍 스타일도 실험함
    • 손그림처럼 보이게 하는 윤곽선 검출
    • 흑백 렌더링
    • 그레인과 노이즈
  • 논의 끝에 이 실험은 포기하고 더 깨끗하고 현대적인 외형에 집중함

Crinkler 압축

  • 핵심 아이디어는 데이터를 저장하지 않고, 데이터를 생성하는 코드를 저장하는 방식임
  • 음악은 연주할 음표 목록과 각 악기의 명령 목록을 저장함
  • Crinkler는 데모씬과 1kB~8kB 인트로를 위해 설계된 압축 도구임
  • 실행 파일은 자체 압축 해제가 가능해야 하므로, Crinkler는 나머지 실행 파일을 풀 수 있는 작은 어셈블리 코드를 포함함
  • Crinkler는 크기 최적화를 우선함
    • 압축 알고리듬은 시간이 걸림
    • 압축 해제는 비교적 느림
    • 수백 MB의 RAM을 사용함
  • 하지만 Crinkler만으로는 충분하지 않았고, 셰이더 소스 코드 42kB를 실행 파일 안에 넣기 위해 추가 축소가 필요했음

Shader Minifier 개선

  • 셰이더 소스 코드는 최종 바이너리에 포함되므로 가능한 작아야 함
  • 손으로 축소할 수도 있지만, 유지보수와 빠른 반복에 문제가 생김
  • Shader Minifier는 2011년부터 개발된 셰이더 축소 도구임
  • 공백과 주석을 제거하고, 변수 이름을 줄이며, 더 많은 변환을 수행함
  • 8kB 인트로는 4kB보다 훨씬 많은 코드를 포함해 새로운 문제가 생겼고, 필요한 기능을 추가하기 위해 한 달 동안 데모 작업을 멈춤
  • 좋은 축소를 위해 단순 축소기가 아니라 Closure Compiler와 비슷한 소스-투-소스 컴파일러가 필요했음
  • Shader Minifier가 수행하는 변환에는 다음이 포함됨
    • 변수와 함수 이름 변경
    • 변수 인라이닝
    • 상수 산술식 평가
    • 함수 인라이닝
    • 죽은 코드 제거
    • 선언 병합
  • 코드 자체 크기를 줄이는 것만으로는 충분하지 않고, 압축 친화적인 출력이 필요했음
    • 어떤 변환은 코드 크기를 줄이지만 압축 후 크기를 늘릴 수 있음
    • 데모를 반복 개발하는 동안 압축 후 크기를 계속 확인해야 했음
  • 새 Shader Minifier 개선으로 압축 바이너리에서 약 600바이트를 절약함
  • 최종적으로 42kB 셰이더 코드는 축소와 압축 후 약 5kB에 들어감
    • 남은 공간에 음악과 C++ 코드가 들어감

다른 작업 위에 쌓인 결과

  • 이 데모는 레이마칭 기법, 음악 생성 소프트웨어, 압축 알고리듬 등 다른 사람들이 만든 작업 위에 만들어짐
  • Shader Minifier에 추가된 새 기능은 앞으로 더 나은 데모를 만드는 데 도움이 되기를 기대함
  • 8kB 카테고리는 4kB보다 더 많은 가능성을 제공하며, 더 인기를 얻기를 바란다는 기대가 남아 있음
  • 비교를 위해 원문 텍스트는 약 21,000자이며, 그대로라면 21kB가 필요함

댓글과 토론

Hacker News 의견들
  • 이 영화가 영감을 받은 원작 영화의 제작자임: https://www.youtube.com/watch?v=khWXdkryBE4
    원작은 Blender, SVG, 오디오 파일만 해도 MB 단위인데, 리메이크가 8kB 안에 들어간 게 정말 인상적임
    Creative Commons 라이선스가 실제로 쓸모 있었다니 반갑고, 다만 같은 고슴도치 캐릭터와 오디오 멜로디를 써줬으면 더 좋았을 것 같음
  • Sloot이 살아났다!!!
    https://en.m.wikipedia.org/wiki/Sloot_Digital_Coding_System
    • Sloot은 안 될지 몰라도, 아래 압축 알고리즘은 영화 전체를 몇 kB에 넣는 데 꽤 효과적이었음
      데모시너들을 잔뜩 모으고, 영화를 보여주고, 컴퓨터가 있는 방에 한동안 가둬둠
      큰 화면, 큰 사운드 시스템, 맥주, 정크푸드가 있으면 더 잘 작동하고, 결과 실행 파일을 받으면 됨
    • 그건 아무것도 아님. Shakespeare 전집을 출력하는 1바이트 프로그램을 소개함:
      a
      성능을 위해 Rust로 작성한 인터프리터가 a 명령을 평가하면서 런타임에 포함된 특정 페이로드를 STDOUT으로 출력함
    • “1999년, 발명품 판매 계약 체결을 며칠 앞두고 Sloot은 심장마비로 갑자기 사망했다. 소스 코드는 회수되지 않았고, 그 기술과 주장은 재현되거나 검증된 적이 없다”
      모두에게 정말 지독한 불운이었음
    • 기사를 대충 훑고 내린 빠른 해석은, 그가 해시 알고리즘을 비기술자들에게 잘못 설명했다는 쪽임
      그 사람들은 a) 단방향 함수라는 점과 b) 설령 단방향이 아니더라도 여러 입력이 같은 해시에 매핑될 수 있다는 점을 이해하지 못했을 듯함
  • 데모신 영상은 꽤 재미있는데 터무니없이 덜 알려져 있음
    https://www.youtube.com/playlist?list=PLRQwR4-_0PR9HBI_GZs1nTn7q5LTkGeCN
    retro game mechanics처럼 좋은 시각 자료로 내부를 풀어주는 채널이 있으면 좋겠음
    • 데모를 YouTube 영상으로 보는 건 약간 반칙 같지만, 20년 전보다 무작위 실행 파일을 내려받아 실행하는 데 더 조심스러워져서 영상이 있어서 다행임
      영상이 없던 때에는 압축 해제와 처리 과정을 꽤 기다린 뒤에야 감탄하거나 실망할 수 있었음
      예전엔 쉬는 시간에 데모를 많이 봤고, 당시 정신 상태에 완벽한 배경이었음
    • 내 역대 최애 데모를 공유할 기회 같음. Still의 Intrinsic Gravity: https://youtu.be/-ZxPhDC-r3w
    • 동의함. .kkrieger 같은 것의 내부 구조를 볼 수 있다면 굉장할 듯함
      https://en.wikipedia.org/wiki/.kkrieger
    • AGI를 풀게 되면 지능을 위한 데모신을 보고 싶음. 얼마나 압축할 수 있을까?
      거기서 더 나아가, 서로 지능적인 에이전트들로 이루어진 세계를 얼마나 작게 만들 수 있을까?
      덧붙임: 비추천하는 사람들, 꿈꾸지 않나? 앞으로 정말 미친 듯이 환상적인 것들이 많이 기다리고 있음
    • shader wars가 떠오름
  • 뒤의 기술은 꽤 다르다는 걸 알지만, 예전의 .kkrieger가 떠오름. 3D FPS 전체를 96k로 압축했는데 정말 멋졌음
    https://en.m.wikipedia.org/wiki/.kkrieger
  • 데모신을 많이 보고 제작 방식을 읽다 보면 한계를 이해하게 되고, 양 같은 요소, 특히 다리의 원뿔 단면은 아주 압축된 방정식으로 표현하고 비슷하게 애니메이션화할 수 있다는 게 분명해짐
    그런데 내가 알기로 대부분의 데모는 GPU와 강력한 3D 가속 능력에 의존하는 반면, 2D 일본 애니풍 데모는 작은 용량에서는 드물거나 거의 없어 보임
    실제로 3D 애니메이션이 더 쉬운 걸까?
    덧붙이면 “mouton”은 프랑스어로 “양”이고, 그래서 “mutton”임
    • 설명한 방식 자체가 데모에서 3D가 흔한 이유를 보여주는 듯함. 3D의 기하 형태는 최소한의 저장 공간만 필요한 닫힌 형식의 방정식으로 표현할 수 있음
      반면 2D 스타일 애니메이션을 저장하는 ‘수학적’ 방법은 윤곽선을 SVG 곡선으로 저장하고, 영역 채색에는 공간 채움 알고리즘을 쓰는 정도임
      대충 계산해도 곡선의 단일 호나 선 하나가 기하 부피 전체 설명만큼 저장 공간을 먹을 수 있고, 그 곡선들의 애니메이션까지 저장하려면 3D 부피의 작은 변환 행렬에 비해 훨씬 더 많은 공간이 필요함
      렌더링 알고리즘의 복잡도도 실제 알고리즘 복잡도와 공간 복잡도 모두에서 늘어날 것 같음
      단서: 비트맵 여러 장을 저장해 플립북처럼 재생하는 것도, 변화분에서 전체 프레임을 생성하는 절차적 압축 해제 알고리즘을 쓴다면 ‘수학적’이라고 주장할 수는 있음
      하지만 그런 방식이 존재한다고 보지 않고, 3D 부피 설명에 비하면 공간 요구량이 엄청날 것임
    • 대부분의 2D 애니메이션도 결국 3D 공간의 사건을 묘사하므로, 일반적으로는 3D 사건을 직접 설명하는 편이 훨씬 쉬울 것임
  • 8kB보다는 조금 크지만, 양에 관한 예상 밖으로 어두운 애니메이션을 좋아한다면 Blender Foundation의 오픈 무비 Cosmos Laundromat을 추천함
    이 재단의 영화들은 기술 데모 치고는 항상 이상하게 어두운 편인데, 이 작품은 특히 잘 나온 것 같았음
    [0] https://www.youtube.com/watch?v=Y-rmzh0PI3c
    • 미리 경고하자면, 영화는 “to be continued”로 끝나고 Wikipedia 페이지에는 이렇게 적혀 있음:
      “이 영화는 원래 장편 영화의 시작점으로 의도되었다. 단편 후속작은 각본과 디자인이 있었지만 제작에 들어가지는 못했다. 2020년에 [제작자]는 이 한 편이 프로젝트의 전부가 될 것이라고 발표했다”
      흥미로운 콘셉트의 결말을 영영 못 본다는 실망을 겪고 싶지 않다면 건너뛰는 게 나을 수도 있음
    • 관련해서 임의로 덧붙이면, Netflix 덕분에 영화 전체가 18,192장의 비압축 EXR 이미지 시퀀스로 S3에서 내려받을 수 있게 공개되어 있음
      DaVinci Resolve에서 HDR 실험할 때 유용했음
      [1] https://opencontent.netflix.com/#h.uyzoa2bivz2j
    • “이상하게 어둡다”는 표현은 네덜란드식 유머 감각을 꽤 잘 설명하는 것 같고, 아마 주된 이유도 그쪽일 듯함
  • 8KB는 데모신의 한 부문임. Pouet에 이런 작품들을 잘 정리한 색인이 있음
    0. https://www.pouet.net/prodlist.php?type%5B%5D=8k&page=1
  • Razor 1911. 순수한 향수임
    • warez와 크래킹, 그리고 FILE_ID.DIZ와 .nfo 파일의 ASCII 아트로 가장 잘 알려진 그룹임
      수십 년 전의 좋은 기억이 많이 떠오름 :-)
    • 아직도 어딘가에서 멋진 예술을 만들고 있다는 걸 보니 반가웠음
  • 정말 놀랍고, 제약이 있는 상황에서도 이야기가 이렇게 웃기고 창의적인 점이 좋음. 진짜 인상적임
    바이트를 줄이기 위해 어떤 식으로 꼼수와 절감을 했는지 기술적인 세부사항을 더 듣고 싶음
  • 웃기면서도 완전히 놀라움. 드디어 결말을 예측 못 한 영화를 봤음