캐릭터가 걷고 뛰기 시작하자 가장 어색한 것은 옷이었습니다. 처음 만든 치마는 다리에 붙어 같이 움직였고, 바람이 불어도 비단처럼 가볍게 날리거나 플라스틱처럼 버텼습니다. 해답은 모양이 아니라 무게에 있었습니다. 옷감 재질마다 무게를 정하고, 그 무게로 천이 움직이게 다시 만들었습니다.
핵심 — 옷감마다 면 밀도를 정하다
모든 옷감 재질에 면 밀도(1제곱미터당 무게, kg/m²)를 정했습니다. 기본값은 천 0.25, 식물섬유 0.30, 양모 0.45, 깃털 0.12, 가죽 0.85이고, 두께에 따라 얇음 0.6배·보통 1배·두꺼움 1.8배를 곱합니다. 여러 겹으로 꿰맨 옷은 겹의 무게를 더하고, 직접 재단한 옷은 실제로 움직이는 조각들의 면적으로 가중 평균합니다.
이렇게 정한 면 밀도에 천의 면적을 곱해 입자마다 질량을 나눠 줍니다. 그 결과 같은 바람, 같은 몸놀림이라도 가죽 치마는 묵직하게 늦게 따라오고, 얇은 천 치마는 가볍게 날립니다. 게임 플레이에서 보기 좋은 무게를 찾기 위해 치마에는 따로 질량 배율을 두어 조절했습니다.
무게와 함께 성질을 셋으로 나눴습니다. 유연함은 얼마나 잘 접히는지(굽힘), 질량은 바람과 관성 같은 바깥 힘에 얼마나 둔하게 반응하는지, 감쇠는 흔들림이 얼마나 빨리 가라앉는지를 맡습니다. 감쇠는 손으로 정하지 않고 실제 면 질량과 유연함에서 계산합니다. 처음에는 무게만 늘렸더니 잔흔들림이 그대로 남아, 감쇠를 질량에 묶는 것으로 해결했습니다.

풀이 방식 — XPBD 표면 천
주름치마는 렌더 메시를 하나의 연속된 중간면으로 이어 붙여, 모든 주름 모서리를 입자로 둡니다. 안쪽·바깥쪽 면이 같은 입자를 나눠 쓰기 때문에, 주름이 몸에 눌려 펴져도 천이 갈라지지 않습니다.
입자 사이는 XPBD(확장 위치 기반 동역학) 제약으로 묶었습니다. 모서리는 거의 늘어나지 않는 길이 제약, 마주 보는 꼭짓점끼리는 약한 굽힘 제약입니다. 굽힘 제약이 약하기 때문에 다리가 밀면 주름이 자연스럽게 벌어집니다. 각 제약은 컴플라이언스(부드러움)를 시간 간격의 제곱으로 나눈 값과 누적 라그랑주 승수로 풀어, 프레임 속도가 달라도 같은 단단함을 유지합니다. 입자마다 역질량을 곱해 보정하므로, 앞에서 정한 무게가 그대로 움직임에 반영됩니다.
고정하는 곳은 허리띠의 윗선뿐입니다. 예전에는 치마의 위쪽 3분의 1을 몸에 붙여 두었는데, 이제는 허리 위 8%만 붙고 나머지 92%는 자유롭게 움직입니다. 대신 너무 늘어나지 않도록 허리선까지의 최대 거리(테더)를 둡니다. 몸이 빨리 움직일수록 한 프레임을 더 잘게 나눠(최대 32번) 풀어, 급하게 돌거나 점프할 때도 천이 몸을 뚫지 않습니다.

몸과의 접촉, 바람과 물
치마는 골반·허벅지·정강이의 실제 단면과 부딪힙니다. 다리는 치마를 끌고 가지 않고 밀어내기만 합니다. 그래서 걸을 때는 주름이 다리에 밀려 벌어졌다 닫히고, 멈추면 몸을 따라 내려앉습니다.
바람은 머리카락과 같은 공기 속도를 씁니다. 바람을 등진 쪽 자락이 더 크게 열리고, 아래에서 올라오는 바람에는 밑단이 들립니다. 물에 들어가면 물살과의 상대 속도로 끌림을 받고, 젖은 천은 약하게 가라앉습니다.


성능 — 무거운 계산은 입을 때 한 번
재질·질량·주름 깊이는 옷을 입거나 바꿀 때 한 번만 계산합니다. 매 프레임 하는 일은 뼈 행렬 몇 개를 읽고 입자를 푸는 것뿐입니다. 풀이는 Burst로 컴파일한 잡에서 돌고, 직접 재단한 옷은 GPU 계산 셰이더로 같은 규칙을 풉니다. 매 프레임 메모리를 새로 잡지 않아(할당 0바이트) 옷을 여러 벌 입어도 끊김이 생기지 않습니다.
머리카락도 무게부터
머리카락도 같은 길을 걸었습니다. 처음에는 머리 모양을 지키려고 가닥을 원래 방향으로 붙들었는데, 그 결과 플라스틱 가발처럼 보였습니다. 무게를 먼저 주고, 원래 모양은 이웃 가닥 사이의 휨만 약하게 기억하게 바꿨습니다. 급하게 돌면 옛 방향으로 살짝 미끄러졌다 따라옵니다.
물리는 한 장의 사진으로 판단할 수 없어, 고칠 때마다 옛 버전과 새 버전을 같은 동작으로 나란히 찍어 비교하고 결정했습니다.