WORK 33 · 작품 ⑧ 개발일지 · 박성하

메디아

MEDIA — 게임 캐릭터 하나를 만드는 전 과정
성공한 것보다 실패한 것이 더 많고, 그 실패에 이유가 있다.

서장 · 형태

머리에 큐브를 쓴
호기심 많은 탐험가

  • 메디아는 탐험가다. 궁금한 것이 있으면 일단 가 본다.
  • 머리에는 속이 빈 와이어 큐브를 쓰고 다닌다. 머리에 얹혀 가라앉지 않고 떠서 위아래로 살짝 흔들린다.
  • 가슴에는 네모 구멍이 뚫려 있고, 그 안에 작은 큐브가 박혀 천천히 돈다.
  • 미디어아트 허브 33번으로 등록했다. 비공개 — 직접 주소로만 열린다.
가슴 큐브 근접
가슴 구멍 안에서 도는 작은 큐브. 위쪽은 머리 큐브의 아랫면이다.
서장 · 절대 조건

"큐브가 찌그러지거나
깨져선 절대 안돼"

— 작가 지시. 이 한 문장이 이후 모든 기술 결정의 판정 기준이 된다.

부드러운 몸

곡면은 조금 일그러져도 아무도 모른다. 사람 눈은 유기적 형태의 오차에 관대하다.

딱딱한 큐브

직선과 직각은 조금만 무너져도 즉시 보인다. 모서리 길이가 곧 형태다.

서장 · 오늘 볼 것

여섯 장면 — 다섯은 실패에서 시작한다

① 출발

3D 출력용으로 만들어 둔 옛 파일을 게임 캐릭터의 정본으로 삼는다. 그리고 가슴 구멍이 찌그러진다.

② 최적화

LOD 3단과 렌더 실측 — 링크 하나로 브라우저에서 돌아야 한다.

③ 동작

CC0 동작 72개와 리타게팅 수학, 함정 세 가지.

④ 팔이 큐브를 뚫는다

세 번 고치고, 결국 껐다.

⑤ 진짜 원인

문제는 팔이 아니라 리그의 축이었다. 오늘의 절정.

⑥ 그 밖에

웹캠 따라하기 · 게임 모드 · 탐험 월드(제작 중).

제1막

출발 —
이미 있던 파일에서

새로 모델링하지 않았다. 그 선택이 끝까지 따라온다.

제1막 · 출발점

4090 백업의
홍익 폴더에 있던 m1.blend

  • 원래 3D 출력용으로 만든 파일이다. 화면에서 움직일 생각으로 만든 것이 아니다.
  • 기본 메쉬에 믹사모 포즈를 넣고 손으로 고친 상태였다.
  • 그 파일의 쉼자세(rest pose) — 동작을 넣기 전의 기본 자세를 정본으로 삼기로 했다. 뼈의 회전은 전부 이 자세를 기준으로 잰다.
  • 이 결정이 이후 거의 모든 문제의 뿌리가 된다 — 제5막에서 다시 나온다.
쉼자세
정본으로 삼은 쉼자세. 팔이 완전한 T자가 아니라 아래로 처져 있다.
제1막 · 두 개의 큐브

몸은 평균으로 움직이고
큐브는 그러면 안 된다

  • 캐릭터의 몸은 스키닝으로 움직인다 — 정점 하나가 여러 뼈의 움직임을 가중 평균한 자리로 간다.
  • 가중 평균은 곡면에는 잘 맞는다. 원래 휘어 있으니 조금 더 휘어도 티가 안 난다.
  • 직선은 다르다. 두 뼈의 평균을 취하는 순간 직선이 호가 된다.
  • 그래서 큐브는 섞지 않는다. 뼈 하나에 100%로 물린다.
머리 큐브 근접
속이 빈 와이어 큐브. 안쪽 모서리까지 전부 직선이다.
제1막 · 조건의 검증

"안 찌그러진다"를
눈이 아니라 자로 말한다

  • 두 큐브를 뼈에 강체로 물린 뒤, 77개 동작 전수에서 큐브 모서리 길이 변화를 쟀다. (77 = 내려받은 클립 72개 + 코드로 만든 반복 동작 5개)
  • 판정 기준은 실행 전에 고정한다 — 재고 나서 기준을 바꾸지 않는 것이 이 프로젝트의 규칙이다.
  • 일부 동작만 골라 보면 통과한다. 전수를 재야 "절대"라고 말할 수 있다.
7.5e-8

머리 큐브 모서리 길이 변화 최대

8.0e-9

가슴 큐브 모서리 길이 변화 최대

둘 다 부동소수점 잡음 수준이다. 즉 변형은 일어나지 않았다. 77개 동작 전수 실측.
제1막 · 첫 번째 실패

가슴 구멍은 처음에 찌그러졌다

1
증상
몸을 굽히거나 팔을 들면 가슴의 정사각형 구멍이 사다리꼴로 눌렸다.
2
원인 — 구멍은 몸의 일부였다
구멍 주변 정점이 6개 뼈에 섞여 있었다. 큐브는 강체로 물렸지만 구멍 벽은 아니었다. 벽이 흔들리면 안에 든 큐브가 멀쩡해도 형태는 무너져 보인다.
3
조치
구멍 벽 85개 정점을 Spine2에 100%로 고정하고, 그 주변 300개 정점을 완충대로 두어 몸과 벽 사이의 급격한 웨이트 단차를 없앴다.
4
결과
구멍 변의 늘어남·눌림이 정확히 1.0000 — 늘지도 눌리지도 않았다는 뜻이다.
제2막

최적화 —
브라우저에서 돌아야 한다

게임 엔진 없이, 링크 하나로 열리는 것이 전제다.

제2막 · LOD

가까우면 촘촘하게, 멀면 성기게

LOD0
0 ~ 4.5 m
15,988
LOD1
4.5 ~ 9.0 m
7,194
LOD2
9.0 m ~
2,876
  • 단위는 삼각형 수다. 거리 4.5 m · 9.0 m에서 자동으로 전환된다.
제2막 · 렌더 실측

재본 환경과 숫자를 함께 적는다

항목실측값
환경맥북 M3 Max · ANGLE Metal · WebGL2
드로우콜5
삼각형17,430
유휴109 fps
걷기112 fps
동작 재생 중100 fps
파일과 뼈

GLB 952KB → 제5막의 리그 수술 뒤 951KB. 뼈 25개, 믹사모 이름 규약.

숫자를 적을 때의 규칙

"빠르다"는 정보가 아니다. 어느 기계에서 무엇을 하는 중에 몇 fps인지를 적어야 다음 사람이 비교할 수 있다.

제3막

동작 —
남의 몸에서 가져오기

동작 데이터는 만드는 것보다 옮기는 것이 어렵다.

제3막 · 동작 확보

믹사모 대신 CC0 1.0을 골랐다

Mesh2MotionCC0 1.0 — 저작권 포기 공개. 출처 표시 의무도 없다
162개 내려받음일단 전부 받아 눈으로 확인
72개 선정게임에서 실제로 쓸 것만
뼈 이름 대응표언리얼 마네킹 규약 → 믹사모 규약
  • 내려받은 동작은 언리얼 마네킹 뼈 이름이었다. 우리 리그는 믹사모 이름이다.
  • 대응표를 만들어 옮겼다 — pelvis→Hips, thigh_l→LeftUpLeg 등.
  • 라이선스를 먼저 확인하고 받는다. 나중에 확인하면 이미 작업이 얹혀 있어 되돌리기 어렵다.
제3막 · 72개의 구성

무엇을 골랐는지가 캐릭터의 성격이 된다

탐험
22
익살
18
감정
11
이동
10
대기
6
4
전투
1

탐험 22 · 익살 18이 절반을 넘고 전투는 1개다. 이 분포 자체가 "호기심 많은 탐험가"라는 설정의 구현이다.

제3막 · 동작 라이브러리

같은 몸으로 72가지를 한다

제3막 · 리타게팅이 왜 필요한가

같은 회전값이 리그마다 다른 자세가 된다

  • 리타게팅 — 한 뼈대에서 만든 동작을 다른 뼈대에 옮겨 얹는 일이다.
  • 동작 파일에 담긴 것은 자세가 아니라 회전값이다. 그리고 회전은 언제나 그 뼈의 쉼자세를 기준으로 잰다.
  • 쉼자세가 다르면 같은 회전값이 다른 자세로 풀린다.
  • 실측: 뼈 이름을 100% 맞춰 그대로 복사했더니 다리가 180도 뒤집혔다.
켤레변환이란

같은 "오른쪽으로 30도"라도 어디를 정면으로 잡느냐에 따라 도착지가 달라진다.

켤레변환은 두 리그의 정면 차이를 앞뒤로 끼워 넣어 상쇄하는 연산이다. 원본 기준으로 돌아갔다가, 회전하고, 우리 기준으로 되돌아온다.

제3막 · 리타게팅 수학

이 두 줄이 이 강의의 핵심 개념이다

델타(뼈마다 한 번만 계산)
D_b = (원본 쉼자세 월드회전)⁻¹ · (우리 쉼자세 월드회전)

매 프레임
우리 지역회전(t) = D_부모⁻¹ · 원본 지역회전(t) · D_자신
  • D_자신은 우리 뼈의 기준을 원본의 기준으로 바꿔 준다. D_부모⁻¹은 부모가 이미 바꿔 놓은 기준을 되돌린다.
  • 이 계산을 빼먹으면 이름이 100% 맞아도 다리가 180도 뒤집힌다.
제3막 · 함정 ①

부모를 잘못 고르면 몸이 통째로 눕는다

그렇게 생각했는지
D_부모는 "부모 뼈"의 델타다. 그러니 대응표에 있는 뼈들 중에서 부모를 찾으면 된다고 봤다.
틀림
무엇이 틀렸는지
골반(Hips)의 부모는 뼈가 아니라 아마추어 노드 — 뼈 전체를 담는 바깥 객체다. 대응표에는 당연히 없다. 그 노드의 회전을 통째로 놓쳤다.
증상
어떻게 알았는지
캐릭터가 90도 누운 채로 걸었다. 뿌리에서 놓친 회전이라 모든 뼈에 그대로 실렸다.
고침
대응된 뼈가 아니라 실제 부모 객체로
부모 델타는 씬 그래프의 실제 부모에서 계산한다. 뼈 계층이 아니라 변환 계층이 기준이다.
제3막 · 함정 ②

세기를 줄이는 방향을
잘못 고르면 다리가 꺾인다

  • 왜 그렇게 생각했는지 — 동작을 약하게 하려면 "안 움직인 상태", 즉 항등회전 쪽으로 보간하면 될 것 같았다.
  • 무엇이 틀렸는지 — 우리 허벅지의 쉼자세 지역회전이 약 180도다. 항등은 "안 움직임"이 아니라 완전히 다른 자세였다.
  • 어떻게 알았는지 — 세기를 낮출수록 다리가 옆으로 90도 꺾였다.
  • 고침 — 항등이 아니라 그 뼈의 쉼자세 지역회전 쪽으로 당긴다. 실측: 다리 옆벌림 0.99 → 0.16.
다리 근접
다리는 이 캐릭터에서 가장 잘 틀어지는 부위다. 확인은 늘 근접에서 한다.
제3막 · 함정 ③

쿼터니언은 같은 자세를 두 번 표현한다

증상

키프레임 사이에서 팔다리가 먼 길로 한 바퀴 돌아 도착한다. 두 키프레임의 자세는 가까운데 보간 경로만 멀다.

원인과 고침

쿼터니언 q−q같은 자세다. 인접 키프레임이 반대 반구에 놓이면 보간이 반대편으로 돈다.

인접 프레임끼리 부호를 맞춰 같은 반구로 모아 둔다.

세 함정 모두 이름 대응이 완벽한 상태에서 일어난다. "이름이 맞으니 됐다"는 판단이 위험한 이유다.

제4막

팔이 큐브를
뚫고 지나간다

세 번 고쳤다. 그리고 껐다.

제4막 · 문제의 정의

이건 버그가 아니라
기하학이다

  • 큐브 한 변 0.81 m, 위팔 길이 0.337 m.
  • 어깨는 큐브 아래 모서리 바로 밑에 있다. 팔을 들면 팔꿈치를 지나 큐브가 차지한 공간 안으로 들어간다 — 코드가 재는 점도 손이다.
  • 동작 데이터가 잘못된 것이 아니다. 이 몸에 이 크기의 큐브를 씌운 이상 반드시 일어난다.
  • 따라서 선택지는 둘뿐 — 큐브를 줄이거나, 팔을 비켜 주거나. 큐브 크기는 작품의 형태이므로 건드릴 수 없다.
어깨 팔꿈치 손 — 침투를 재는 점, 큐브 안쪽에 들어와 있다 0.81 m 0.337 m 위팔
2차원 단면 도해 — 두 길이만 실측값이다.
제4막 · 1차 시도 — 실패

상자는 구가 아니다

  • 왜 그렇게 생각했는지 — 물체 안에 들어간 점은 중심 반대 방향으로 밀면 밖으로 나온다. 구에서는 참이다.
  • 무엇이 틀렸는지 — 상자의 면 근처에서 그 방향은 면과 거의 나란하다. 밖으로 빼는 대신 표면을 따라 미끄러뜨리고, 옆면으로 다시 밀어 넣는다.
  • 어떻게 알았는지 — 팔굽혀펴기·지쳐 무릎꿇기에서 침범이 0 → 4로 악화했다. 켜서 나빠진 것이다.

직관이 맞는 도형과 틀리는 도형이 있다. 어느 도형에서 참인지를 먼저 물어야 한다.

구 — 항상 맞는다 중심 반대 = 가장 짧은 탈출 상자 — 빗나간다 점선=중심 반대 · 실선=실제 최단
제4막 · 2차 시도 — 수치는 좋아졌다

여섯 면 중
가장 얕은 쪽으로 뺀다

  • 큐브를 AABB(축 정렬 상자)로 보고, 여섯 면까지의 침투 깊이를 각각 잰다. 그중 가장 얕은 면의 법선으로 뺀다.
  • 아래쪽 면에는 0.5배 가중을 줬다 — 팔은 아래로 내리는 편이 자연스럽다.
  • 안전장치: 보정 후에도 침투가 줄지 않으면 원래 각도로 원복한다. 켜서 나빠질 길을 원리적으로 막았다.
총 침범158 → 7
문제 동작17개 → 2개
악화한 동작0
기준값 158은 이 회차의 '보정 끔' 값이다. 동작 77개를 시각으로 훑어 재기 때문에 회차마다 잡히는 프레임이 달라 기준값도 달라진다 — 그래서 회차 안에서만 비교해야 한다.
100 160 110 90 ← 최소 2차원 단면 — 실제로는 여섯 면을 모두 잰다
제4막 · 3차 — 수치가 좋아도 실패다

"튕겨내니까 되려 어색해졌어 …
뚝뚝 끊기고"

— 작가 지적. 침범 수치는 158에서 7로 줄어든 상태였다.

원인 ① 즉시 켜고 즉시 끈다

침투가 감지된 프레임에 보정을 한 번에 꽂고, 벗어나는 프레임에 한 번에 뺐다. 그 두 프레임에서 각도가 계단처럼 튄다.

원인 ② 되먹임 순환

보정된 위치로 다음 프레임의 침투를 쟀다. 보정이 다음 보정의 입력이 되니, 목표 자체가 매 프레임 진동한다.

지표 하나가 좋아졌다고 끝난 것이 아니다. 사람이 보는 것은 매 프레임의 변화량인데, 그건 재고 있지 않았다.

제4막 · 3차 고침

목표를 연속으로 만들고, 천천히 붙인다

1
되먹임을 끊는다
목표 각도는 언제나 동작 클립의 원래 각도에서 출발해 계산한다. 보정 결과는 다음 계산에 들어가지 않는다.
2
목표를 연속으로 만든다
침투 깊이에 비례한 최소 회전만 요구한다. 깊이가 매끄럽게 변하면 목표도 매끄럽게 변한다.
3
지수 완화로 적용한다
걸릴 때는 시간상수 0.07초, 풀릴 때는 0.22초. 빨리 피하고 천천히 돌아온다 — 비대칭이 자연스럽다.
4
이번엔 '끊김'도 잰다
침범 182 → 18(이 회차의 끔 기준값은 182다 — 앞의 158과 다른 회차라 7과 18을 직접 견주면 안 된다), 악화 0. 그리고 프레임당 어깨 회전 변화의 p95(큰 쪽 5%를 뺀 값)·최대가 보정을 끈 상태와 동급임을 확인했다.
제4막 · 결말

"팔큐브 회피는 안하는게 나을거 같아
그냥 상시로 꺼줘"

— 작가 판정. 기능은 기본 꺼짐으로 남았다.

기술적으로는 성공했다

침범은 1/10로 줄었고, 악화한 동작은 0이며, 끊김 지표도 통과했다. 세 번째 판까지 가서 얻은 결과다.

그래도 껐다

작가의 판정은 "안하는게 나을거 같아" 한 줄이었고, 이유는 따로 붙지 않았다. 작품이 원하지 않으면 끈다.

지운 것이 아니라 껐다. 판단이 바뀌면 다시 켤 수 있어야 하고, 그때 다시 만들 필요는 없어야 한다.

제5막 · 절정

진짜 원인은
눈에 보이지 않았다

"매번 이렇게 수정을 해야 할 순 없자나"

제5막 · 작가의 질문

"우리 모델의 리깅을 제대로 고쳐서
다음부터 이런일이 없게 해야 하는거 아닐까?"

  • 여기까지의 고침은 전부 증상 대응이었다. 리타게팅 보정, 팔 회피, 세기 보간 방향 — 매번 개별 사례를 막았다.
  • 질문이 바뀌자 진단이 시작된다 — 왜 우리 리그에서만 이런 일이 반복되나.
  • 진단 결과: 우리 리그는 이름만 믹사모였고, 쉼자세의 뼈 축이 표준과 어긋나 있었다. 제1막에서 정본으로 삼은 바로 그 쉼자세다.
표준 축 우리 리그 = 위치·길이 동일
뼈는 위치만이 아니라 축(roll)을 갖는다. 화살표가 뼈의 지역 축이다.
제5막 · 실측 — 이 덱의 절정

표준 믹사모와 뼈별로 얼마나 어긋나 있었나

45.5°
발가락
20.0°
어깨
17.2°
정강이
9.5°
위팔
7.9°
척추
7.3°
목 · 머리
7.0°
허벅지
4.3°
아래팔
3.6°
골반
0.74°

기준은 표준 믹사모 파일(Sad Walk.fbx)의 쉼자세다. 발 45.5°는 발이 거의 반 직각만큼 다른 축을 보고 있었다는 뜻이다.

제5막 · 왜 오래 못 봤나

스켈레톤 뷰는
축을 그리지 않는다

  • 스켈레톤 헬퍼는 뼈의 머리 위치를 잇는 선만 그린다. 축(roll)은 그리지 않는다.
  • 그래서 수술 전후 뼈대 렌더의 MD5 해시가 완전히 동일했다.
  • 바꾼 것이 있는데 그림은 한 픽셀도 다르지 않았다 — 이것이 이 문제가 오래 숨어 있던 이유다.
수술 전 · 후 뼈대 렌더
MD5 2f155465031d1945ec263bf35b95605e — 동일
제5막 · 눈으로 판정할 수 없는 것

옛 리그와 새 리그 — 화면으로는 구별되지 않는다

이건 개선 전후 비교가 아니다. 뷰어가 리타게팅으로 축 차이를 보정하기 때문에 둘 다 제대로 나온다. 화면이 같다는 사실 자체가, 이 문제를 화면으로 판정할 수 없다는 증거다.

제5막 · 수술

뼈의 '방향만' 표준 축으로 다시 맞춘다

바꾼 것 — 하나뿐

각 뼈의 쉼자세 축 방향. 헤드리스 블렌더로, 원본이 아니라 복사본에서 실행했다.

블렌더 파일은 작가 확인 없이 고치지 않는다 — 이 프로젝트의 규칙이다.

보존한 것 — 나머지 전부
  • 관절 위치 · 뼈 길이
  • 메시 · 스킨 웨이트
  • 머리 큐브 · 가슴 큐브
  • 저장해 둔 포즈 2개

"방향만 바꾼다"는 말은 쉽지만, 블렌더에서 뼈 방향을 건드리면 연결된 거의 모든 것이 따라 움직인다. 다음 장이 그 목록이다.

제5막 · 수술 중 만난 함정 다섯

따라 움직이는 것들을 하나씩 붙잡는다

함정무슨 일이 일어나고, 어떻게 막았나
연결된 뼈edit_bone.use_connect먼저 꺼야 한다. 연결된 채로 두면 부모의 꼬리를 따라 자식의 머리 위치가 끌려간다 — 관절 위치가 바뀐다.
큐브큐브는 뼈에 부모돼 있다. 축을 바꾸면 딸려 움직인다 → 수술 후 matrix_world를 재대입해 원래 자리로 복원.
저장 포즈저장해 둔 포즈 2개는 옛 축 기준의 회전값이라 그대로는 무의미하다 → 옛 GLB를 다시 불러 월드 자세를 읽고, 새 축으로 다시 구웠다.
Blender 5.2Action.fcurves 속성이 아예 없다. layers[].strips[].channelbag(slot)으로 접근해야 한다 — 버전 가정이 코드를 조용히 깨뜨린다.
내보내기눈 아이콘 토글 숨김(hide_get)은 hide_viewport다르다. 내보내기에 use_visible=True가 필요했다.
제5막 · 검증

"보존했다"도 숫자로 말한다

무엇을 확인했나실측판정
뼈 방향 — 표준과의 각도차0.0000°통과
관절(헤드) 위치 이동9.0e-8통과
메시 정점 이동1.8e-7통과
큐브 이동1.2e-7통과
저장 포즈 회전차0.0000°통과

다섯 종 전부 통과해야 수술 성공이다. 하나라도 걸리면 복사본을 버리고 다시 한다 — 그래서 복사본에서 작업한다.

제5막 · 결과

보정이 거의
필요 없는 리그가 됐다

  • 새 리그에서 믹사모 소스와의 리타게팅 보정 델타가 최대 0.03°다.
  • 사실상 표준 리그다. 게임엔진에 넣어도 믹사모 동작이 보정 없이 맞는다.
  • 즉 이 수술은 한 동작을 고친 것이 아니라, 앞으로 들어올 모든 동작의 문제를 없앤 것이다.
  • GLB 크기 952KB → 951KB. 작품은 아무것도 잃지 않았다.
0.03°

새 리그와 표준 믹사모 사이의 보정 델타 최대값 (수술 전 최대 45.5°)

증상을 하나씩 막던 작업이, 원인을 고치자 한 번에 끝났다. 작가의 질문 — "매번 이렇게 수정을 해야 할 순 없자나" — 이 이 장을 열었다.

제6막 · 웹캠 따라하기

사람의 좌표를
캐릭터의 좌표로

  • 미디어파이프 Pose Landmarker. 라이브러리는 로컬에 벤더링했고 모델은 5.8MB, GPU 실패 시 CPU로 폴백한다.
  • 입력은 세계 랜드마크 — 엉덩이가 원점, x는 이미지 오른쪽(=사람의 왼쪽), y는 아래, z는 카메라 쪽이 음수.
  • 캐릭터 좌표는 +X 왼쪽 · +Y 위 · +Z 앞. 거울 모드는 좌우 뼈 교환 + (−x, −y, −z).
  • 방향은 EMA(지수이동평균)로 완화하고, visibility < 0.5인 마디는 건드리지 않는다.
정직하게 적을 것 — 아직 못 한 검증

실제 카메라로는 검증하지 못했다. 개발 환경에서 카메라 권한이 막혀 있었다.

대신 합성 랜드마크 3종으로 좌표 변환만 확인했다. 즉 "수식이 맞다"까지가 확인된 것이고, "실제로 잘 따라한다"는 아직 모른다.

모르는 것을 모른다고 적어 두면, 다음 사람이 거기서부터 시작한다.

제6막 · 게임 모드

GTA식 3인칭 — 조작과 실측

조작동작
WASD이동
마우스시점
Space점프
Shift웅크림
Ctrl달리기
클릭액션 재생
실측
걷기 속도1.9 m/s
질주 속도4.2 m/s
점프 높이0.86 m
클립 전환상태기계 + 0.12초 크로스페이드
동작 72개를 상태기계로 묶는다. 클립을 즉시 바꾸면 자세가 튀므로 0.12초 동안 섞는다.
제6막 · 게임 모드의 함정 셋

세 가지 모두 "당연해 보이는 것"에서 나왔다

1
A와 D가 반대로 나갔다
"오른쪽 = +X"라고 가정했다. 카메라가 +Z를 볼 때 화면 오른쪽은 −X다. 가정을 버리고 외적으로 확정했다.
2
멀리 가면 그림자가 사라졌다
원인은 그림자 카메라의 시야가 ±3m로 고정이라서. 시야를 넓히면 그림자 해상도가 떨어지므로, 조명을 캐릭터와 함께 움직였다.
3
"느리고 미끄러지듯"
작가 지적. 원인은 걸음 박자가 이동 속도보다 느린 것 — 발이 지면을 밀지 못하고 미끄러진다. 클립 재생속도를 걷기 1.5배 · 질주 1.3배로 올려 지면 속도와 맞췄다.
탐험 월드 전경
제6막 · 탐험 월드
기본 도형으로 지은 세계 — 제작 중
모델을 사 오지 않고 구 · 큐브 · 원뿔 · 팔면체만으로 지었다. 형태 언어를 캐릭터(구를 감싼 큐브)와 맞추기 위해서다. 충돌 · NPC · 말풍선 · 상호작용 · 일지 등 모듈 7개로 나눠 병렬로 만들고 있다. 아직 완성되지 않았다.
제6막 · 탐험 월드 (제작 중)

여섯 구역 — 각각 다른 움직임을 요구한다

구역을 먼저 정하면 필요한 동작이 정해진다 — 오르기 · 헤엄. 둘 다 고른 72개 안에 있다.

제7막 · 디지털 정원

"검정 공간에 흰색 선으로 풀과 나무 꽃들이 자라나고 …
선 끝에는 들이 있고 … 생장 방식에 따라서 자라나고"

작가가 인스타그램 릴 하나를 링크로 건네며 한 말이다. 영상은 열지 못했다 —
남은 것은 이 문장뿐이었고, 문장이 곧 명세가 됐다.

"열어 보지 못했다"를 그대로 말하는 편이 낫다. 본 척하고 만들면 어긋난 자리를 찾을 수 없다.

붉은 톤 군집 사이의 메디아
제7막 · 디지털 정원
선으로만 지은 정원
면(mesh)이 하나도 없다. 선 25,779마디끝점 6,963개가 전부다. 실측 — 773포기 · 6구역 · 정원 드로우콜 11.
제7막 · 생장

심는 것이 아니라 자라게 둔다

생장 규칙은 grow.js 한 파일에 모았다 — three.js 를 부르지 않는다. 규칙(어느 쪽으로 · 얼마나 · 몇 번 갈라지나)과 그리기를 갈라 두면 규칙만 따로 시험할 수 있다. 여덟 갈래: 풀 · 갈대 · 고사리 · 덤불 · 꽃 · 종꽃 · 나무 · 세계수.

제7막 · 첫 벽

WebGL 은 선 굵기를
무시한다

  • 작가 요청은 "좀 더 선이 굵어야 해". 그런데 LineBasicMaterial.linewidth 는 대부분의 브라우저에서 값을 넣어도 1픽셀로 그려진다. 버그가 아니라 규격이다.
  • 해법은 선을 선으로 그리지 않는 것 — three 의 LineSegments2 는 마디마다 사각형 두 장을 세워 굵기를 만든다.
  • 굵기는 재질 단위 값이라 마디마다 다르게 줄 수 없다. 그래서 굵기를 15단계로 나누고 단계마다 재질을 하나씩 뒀다.
  • 단계 경계는 산술평균이 아니라 기하평균으로 갈랐다 — 굵기는 더하기가 아니라 곱하기로 느껴진다.
굵은 흰 가지
실측 굵기 분포 — 0.010m 17,947마디 · 0.024m 4,872 · 0.058m 2,108 · 0.140m 649 … 0.534m 3마디(세계수 밑동).
제7막 · 작가 지적 ①

"나뭇가지 두께 차가 너무 큰 거 같구 …
끝에 점의 크기가 좀 더 커야 할 거 같아"

왜 그렇게 보였나

굵기를 가지 차수에 비례해 줄이기만 했다. 하한이 없으니 세계수처럼 여러 번 갈라지는 나무는 잔가지가 0에 수렴한다 — 밑동은 판때기, 끝은 실오라기가 된다.

고친 것

굵기에 하한(wMin)을 뒀다. 세계수는 밑동 0.55 · 하한 0.020. 끝점 크기도 0.038 → 0.065 로 올렸다.

한 번은 과하게 고쳐 잔가지가 0.142m 흰 덩어리가 됐다. 숫자만 보면 "두께 차가 줄었다"가 맞다 — 눈으로 보지 않았으면 그대로 뒀을 것이다.

제7막 · 작가 지적 ②

"끝이 번지지 않고
깔끔하게 벡터 느낌으로"

  • 점은 알파를 가운데에서 가장자리까지 선형으로 흘려 그리고 있었다. 부드럽긴 한데 번져 보인다.
  • 고친 방식 — 안은 꽉 채우고, 테두리 한 픽셀만 부드럽게. 화면에서 그 점이 몇 픽셀인지를 fwidth 로 물어 그만큼만 흐린다.
  • 같은 톤 안에서 변화가 읽히도록 크기는 ±45%, 밝기는 ±22% 흩었다.
가까이서 본 끝점
끝점 6,963개가 Points 하나로 그려진다 — 점마다 크기·색이 다르지만 드로우콜은 1이다.
제7막 · 색

흰색이 흰색으로 보이지 않았다

  • 작가: "메디아는 흰색, 다른 NPC 들도 각자 특징적인 색상이". 그런데 메디아는 잿빛에 푸른 기가 돌았다.
  • 원인 — 정원의 선은 조명을 받지 않는다(LineMaterial). 그래서 조명을 낮게 잡아 뒀는데, 그 조명이 닿는 것은 메디아와 주민뿐이었다. 세상을 어둡게 하려던 값이 캐릭터만 어둡게 했다.
  • 조명을 3배 가까이 올리고 하늘빛을 중립으로 바꿨다. 선은 조명을 안 받으니 세상은 그대로다. 실측 몸통 픽셀 (196, 199, 207).
  • 주민에게는 각자의 색을 줬다 — 깜박 호박빛 · 여울 물빛 · 이끼 이끼빛. 멀리서도 누구인지 알아보게.
붉은 군집 사이에 선 흰 메디아
여섯 구역은 저마다 색조를 갖는다 — 빈 자리 · 붉은 무리 · 노란 갈대 · 푸른 그늘 · 흰 무리 · 자란 것들.
제8막 · 첫화면

"ME-DIA 라고 하고 … 아래에 메뉴를 만들어줘
마치 게임 첫화면처럼"

작품을 열면 곧바로 조작 패널이 뜨는 것이 아니라,
메디아가 돌아다니며 포즈를 취하는 화면 하나가 먼저 온다.

포즈는 피셔–예이츠로 섞어 한 바퀴 다 돌기 전에는 같은 것이 다시 나오지 않는다 — 무작위로 뽑으면 같은 포즈가 연달아 나와 고장처럼 보인다. 3.8~5.6초마다 바뀌고, 카메라는 64초에 한 바퀴 돈다.

ME-DIA 첫화면
제8막 · 첫화면
ME-DIA
메뉴 넷 — 메디아 월드 입장 · 캐릭터 포즈 스튜디오 · 설정 · 이 작품에 대하여. 첫화면이 떠 있는 동안 조작 패널과 전체화면 버튼은 감춘다(html.mc-title). 작품의 첫 인상은 도구가 아니라 몸이어야 한다.
정원에 흩어진 색색의 도형
제8막 · 물리 도형
밀리고, 들리고, 쌓인다
"공 같은 거랑 네모나 여러 색색의 도형들이 … 밀려나고 … 집어서 옮기고 쌓을 수 있게." 24개. 고정 1/120초 적분 · 쓸기-가르기 넓은판정 · 순차 임펄스 2회. 0.055 m/s 이하로 0.40초면 재운다 — 안 재우면 쌓아 둔 탑이 영원히 떤다.
제8막 · 쌓기가 안 됐다

"놓으면 바닥으로 떨어진다" — 원인이 둘이었다

1
받침을 제 밑면 아래에서만 찾고 있었다
든 물건은 가슴 높이 0.95m에 떠 있다. 밑면은 0.60. 받침 상자의 윗면은 0.78 — 내 밑이 아니어서 탈락했다. 얹으려는 자리는 원래 발밑보다 높다. 기준을 밑면에서 윗면으로 바꿨다.
2
고쳐도 안 됐다 — 몸이 상자를 밀어낸다
캐릭터 반지름 0.32 + 상자 반쪽 0.336. 다가서면 상자가 먼저 밀려나서, 앞 0.62m 에 든 물건이 상자 발자국까지 0.11m 모자랐다. 딱 겹칠 때만 얹으면 한 번도 안 얹힌다.
3
옆으로 0.45m 까지 봐주고, 고르면 한가운데로 끌어다 얹는다
실측 — 놓은 뒤 공 밑면 0.667 · 상자 윗면 0.671(허용 파고듦 0.004). 3층 탑이 8초간 그대로. 밀기 2.39m.

①만 고치고 "됐다"고 했으면 거짓말이 됐다. 고친 뒤에도 같은 값을 다시 재야 하는 이유다.

3층으로 쌓인 도형
제8막 · 물리 도형
3층 — 상자 · 판 · 기둥
상자에는 회전을 아예 두지 않았다. 90° 돌린 상자는 변 길이를 맞바꾼 상자와 같으므로, 축정렬만 쓰면 충돌 상자와 그린 모양이 어긋날 여지가 사라진다 — 쌓기가 정확해지는 값이다.
종장 · 되돌린 것들

측정해 보니 더 나빠져서 되돌린 시도 다섯

시도기대했던 것
웨이트 스무딩경계의 주름이 부드러워질 것
거리기반 웨이트 재계산손으로 칠한 것보다 일관될 것
영향 반경 상한먼 뼈의 간섭을 끊어 낼 것
리토폴로지정점 배치가 고르면 변형이 안정될 것
어깨 회전 분배한 관절에 몰린 회전을 나누면 자연스러울 것

다섯 모두 그럴듯했고, 다섯 모두 재보니 더 나빴다. 되돌릴 수 있게 해 두면 이런 시도를 부담 없이 할 수 있다. 부정적 결론도 결론이다.

종장 · 작업 방식

역할을 나누고, 경계를 문서로 정한다

작가

기획하고 판정한다. "어색하다", "이건 끄자" — 최종 판단은 사람의 몫이다.

AI는 구현하고 측정한다. 숫자를 만들어 판정의 근거를 댄다.

규칙 — 사고에서 나왔다

한 번 작가의 웨이트 작업을 덮어썼다. 그 뒤로 블렌더 파일은 작가 확인 없이 고치지 않는다.

"나한테 블랜더에서
확인 시키고 진행해"

규칙은 미리 상상해서 만드는 것이 아니라, 실제로 터진 사고에서 한 줄씩 늘어난다.

종장 · 정리

이 작업에서 확인된 다섯 가지

① 눈에 안 보이는 문제가 있다

뼈 축은 스켈레톤 뷰에 그려지지 않는다. 화면이 같아도(MD5 동일) 데이터는 다를 수 있다.

② 지표 하나로 판정하지 않는다

침범 158 → 7이어도 "뚝뚝 끊긴다". 사람이 보는 것은 매 프레임의 변화량이었다.

③ 직관은 도형을 가린다

"중심 반대로 밀면 나온다"는 구에서만 참이다. 어디서 참인지를 먼저 묻는다.

④ 증상 대신 원인을 고친다

"매번 이렇게 수정을 해야 할 순 없자나" — 질문 하나가 증상마다 이어지던 개별 수정을 한 번의 수술로 바꿨다.

⑤ 작품이 원하지 않으면 끈다

기술적으로 성공한 기능도 작품이 원하지 않으면 끈다. 판정은 사람이 한다.

고친 것보다
재본 것이 남는다.

만든 순서대로가 아니라 실패한 순서대로 적었다.
다음 사람이 같은 자리에서 멈추지 않도록.
메디아는 아직 탐험 중이다.