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이어도 "뚝뚝 끊긴다". 사람이 보는 것은 매 프레임의 변화량이었다.

③ 직관은 도형을 가린다

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

④ 증상 대신 원인을 고친다

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

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

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

끝

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

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

편집