스킨 조인트
고친 사람 github-actions[bot]
스킨 조인트는 3차원 캐릭터의 겉면 하나를 움직일 뼈대 마디를 골라 번호를 매겨 줍니다. 이 마디 하나하나가 조인트입니다. 겉면의 꼭짓점은 이 번호로 자기가 따라갈 조인트를 가리킵니다.
쉽고 빠른 이해
스킨 조인트는 겉면 하나가 따라 움직일 조인트의 목록입니다. 팔 겉면의 목록에 위팔·아래팔·손을 올리면 셋은 0번·1번·2번이 됩니다. 팔꿈치 꼭짓점은 「0번과 1번을 따라간다」고만 적습니다.
목록이 없으면 꼭짓점은 뼈대 전체에서 매긴 번호를 써야 합니다. 그러면 번호가 커집니다.
화면을 그릴 때는 조인트마다 행렬을 하나씩 그래픽 카드에 넘깁니다. 조인트가 움직인 만큼 꼭짓점을 옮기는 행렬입니다. 목록이 없으면 이 행렬도 뼈대 전체만큼 늘어납니다. 목록을 두면 번호도 작고 넘길 행렬도 목록 길이만큼입니다.
돌아가는 방식은 이렇습니다.
- 모형을 만들 때 목록을 정합니다. 칸마다 그 조인트의 처음 자세에 맞춘 행렬을 붙여 둡니다. 나중에 얼마나 움직였는지 잴 기준입니다
- 화면을 한 장 그릴 때마다 목록 순서대로 조인트가 처음 자세에서 움직인 만큼을 행렬 하나로 만듭니다
- 꼭짓점은 자기가 적은 번호로 그 행렬들 가운데 몇 칸을 찾아 씁니다
대가는 번호가 목록에 묶인다는 것입니다. 목록 순서를 바꾸면 꼭짓점에 적힌 번호를 전부 다시 써야 합니다. 목록에 없는 조인트는 뼈대에 있어도 그 겉면을 휘게 하지 못합니다.
상세
학교 전체로 보면 학생 번호는 네 자리입니다. 반 출석부에서는 같은 학생이 1번부터 30번 가운데 하나로 불립니다. 담임은 「7번」이라고만 불러도 누구인지 압니다.
스킨은 겉면 하나를 뼈대에 붙이는 데 필요한 것을 묶어 둔 것입니다. 무엇이 들어가는지는 아래 「스킨이라는 묶음」에서 봅니다.
스킨 조인트는 그 묶음에 든, 겉면이 쓸 조인트를 순서대로 늘어놓은 목록입니다. 목록에서 몇째 칸인지가 그 조인트의 번호가 됩니다. 뼈대에 조인트가 예순 개 있어도 팔 겉면의 목록에는 위팔·아래팔·손 셋만 오를 수 있습니다. 그러면 팔 겉면 안에서 이 셋은 0번·1번·2번으로 불립니다.
이 절은 먼저 조인트와 스킨이 무엇인지 짚습니다. 이어서 꼭짓점이 목록 번호로 조인트를 찾는 과정을 작은 코드로 따라갑니다. 그다음 목록의 칸마다 붙는 행렬과 그 행렬이 그래픽 카드로 넘어가는 순서를 봅니다. 끝으로 목록을 따로 두는 까닭과 목록에 안 오른 조인트를 봅니다.
조인트와 뼈대
3차원 캐릭터는 대개 두 겹으로 만듭니다. 바깥 겹은 눈에 보이는 겉면입니다. 겉면은 수많은 꼭짓점을 이은 삼각형으로 이루어집니다. 꼭짓점은 정점이라고도 합니다.
안쪽 겹은 화면에 그리지 않는 뼈대입니다. 뼈대가 움직이면 겉면이 따라 휩니다.
뼈대는 조인트라는 마디를 이어 붙여 만듭니다. 조인트는 관절 하나와 그 관절에서 뻗은 뼈 하나를 함께 가리킵니다. 실무에서는 뼈나 본(bone)이라고도 부릅니다. 이 편에서는 조인트로 부릅니다.
조인트는 부모와 자식으로 이어집니다. 위팔이 부모이고 아래팔이 자식이면, 위팔을 돌릴 때 아래팔과 손이 함께 따라 돕니다. 자식의 자세는 부모를 기준으로 적습니다. 꼭대기 조인트부터 이 상대 자세를 차례로 이어 붙여 내려가면 조인트마다 장면 안에서의 자세가 나옵니다. 이 방식을 계층 변환이라고 합니다.
장면에 놓인 것 하나하나를 노드라고 부릅니다. 조인트도 노드의 하나입니다. 노드는 장면 전체에서 번호를 하나씩 받습니다. 아래에서 「노드 번호」라고 하면 이 장면 전체의 번호입니다.
스킨이라는 묶음
겉면을 뼈대에 붙여 함께 움직이게 하는 일을 스키닝이라고 합니다. 앞에서 말한 스킨은 이 일에 필요한 것을 겉면 하나 몫으로 묶어 둔 것입니다. 어느 조인트를 쓰는지와 그 조인트들의 처음 자세가 이 묶음에 들어갑니다.
스킨 조인트는 이 묶음의 중심입니다. 꼭짓점이 따라갈 조인트를 정하는 기준이 이 목록이기 때문입니다. 아래 소절들의 행렬과 번호는 전부 이 목록의 순서에 맞춰 놓입니다.
꼭짓점이 조인트를 찾는 과정
꼭짓점마다 따라갈 조인트의 번호가 붙습니다. 이 번호를 본 인덱스라고 합니다. 팔꿈치 꼭짓점이라면 위팔과 아래팔을 가리키는 번호 둘이 붙습니다.
번호 옆에는 그 조인트를 얼마나 따를지 적은 비율도 붙습니다. 이 비율을 스킨 가중치라고 합니다. 팔꿈치가 위팔과 아래팔을 반씩 따르면 비율은 0.5 와 0.5 입니다.
번호와 비율은 위치·색처럼 꼭짓점마다 따로 붙는 값입니다. 이런 값을 정점 속성이라고 합니다.
본 인덱스는 노드 번호가 아닙니다. 스킨 조인트 목록의 몇째 칸인지를 적습니다. 그 칸에 적힌 노드 번호를 한 번 더 읽어야 어느 조인트인지가 나옵니다.
아래 코드는 위팔·아래팔·손이 장면에서 노드 10·11·12 번인 경우입니다. 팔꿈치 꼭짓점은 목록의 0번과 1번 칸을 따라갑니다.
joints = [10, 11, 12] # 스킨 조인트
v_idx = [0, 1] # 팔꿈치의 번호
joints[v_idx[0]] # 10 (위팔)
joints[v_idx[1]] # 11 (아래팔)
꼭짓점에는 0 과 1 만 적혀 있습니다. 10 과 11 이라는 노드 번호는 목록을 거쳐서야 나옵니다. 그래서 같은 꼭짓점 데이터라도 어느 목록과 짝을 짓느냐에 따라 다른 조인트를 따라가게 됩니다. 거꾸로 목록 순서를 바꾸면 꼭짓점에 적힌 번호도 전부 다시 맞춰야 합니다.
한 뼈대를 나눠 쓰는 여러 목록
캐릭터 하나에 겉면이 여럿인 일이 흔합니다. 팔 겉면과 몸통 겉면을 따로 만들어도 둘은 같은 뼈대를 따라 움직여야 합니다. 이때 겉면마다 자기 스킨을 둡니다. 스킨마다 자기 목록이 따로 있습니다.
아래 그림은 한 뼈대를 두 목록이 나눠 가리키는 모양입니다. 팔 목록은 위팔·아래팔·손을, 몸통 목록은 몸통 조인트와 위팔을 가리킵니다. 위팔은 두 목록에 다 오르면서 번호가 다릅니다.
flowchart TD
subgraph SK["뼈대 · 노드 번호"]
N9["9 몸통"]
N10["10 위팔"]
N11["11 아래팔"]
N12["12 손"]
N13["13 손잡이"]
end
subgraph A["팔 겉면의 스킨 조인트"]
A0["0번 칸"]
A1["1번 칸"]
A2["2번 칸"]
end
subgraph B["몸통 겉면의 스킨 조인트"]
B0["0번 칸"]
B1["1번 칸"]
end
A0 --> N10
A1 --> N11
A2 --> N12
B0 --> N9
B1 --> N10
위팔은 팔 겉면에서는 0번이고 몸통 겉면에서는 1번입니다. 같은 조인트라도 번호는 목록마다 따로 매겨집니다. 목록이 겉면의 것이지 뼈대의 것이 아니기 때문입니다.
그림의 손잡이는 칼을 매달 점으로 둔 조인트입니다. 이 조인트는 어느 목록에도 없습니다. 까닭은 아래 「목록에 안 오른 조인트」에서 봅니다.
칸마다 붙는 역바인드 행렬
꼭짓점 위치는 뼈대에 겉면을 처음 붙인 때의 자세를 기준으로 적혀 있습니다. 팔을 양옆으로 벌린 T 자 자세가 흔합니다. 이 처음 자세를 바인드 포즈라고 합니다.
조인트 하나가 꼭짓점을 옮기려면 바인드 포즈에서 지금까지 얼마나 움직였는지를 알아야 합니다. 먼저 바인드 포즈 때 그 조인트의 자세를 되돌려, 꼭짓점을 그 조인트를 기준으로 한 위치로 가져옵니다. 그다음 조인트의 지금 자세를 입히면 조인트가 움직인 만큼만 꼭짓점이 옮겨집니다.
자세는 옮기기·돌리기·키우기를 한 덩이에 담은 변환 행렬로 적습니다. 행렬로 적으면 자세 둘을 곱셈 한 번으로 이어 붙일 수 있습니다.
어떤 변환을 거꾸로 되돌리는 행렬을 역행렬이라고 합니다. 바인드 포즈를 되돌리는 일은 바인드 포즈 행렬의 역행렬을 곱하는 일입니다.
그래서 스킨은 목록의 칸마다 행렬을 하나씩 붙여 둡니다. 그 칸 조인트의 바인드 포즈를 되돌리는 역행렬입니다. 이것을 역바인드 행렬이라고 합니다. n 번째 역바인드 행렬은 목록 n 번째 칸 조인트의 것입니다.
역바인드 행렬은 캐릭터가 움직여도 바뀌지 않습니다. 바인드 포즈는 모형을 만들 때 한 번 정해지기 때문입니다. 그래서 모형을 불러올 때 한 번 읽어 두고 계속 씁니다.
그래픽 카드로 넘어가는 행렬 배열
화면을 한 장 그릴 때마다 목록을 처음부터 끝까지 한 번 돕니다. 칸마다 그 조인트의 지금 자세 행렬에 역바인드 행렬을 곱합니다. 곱한 결과는 그 조인트가 바인드 포즈에서 움직인 양입니다. 이 결과를 스키닝 행렬이라고 합니다.
스키닝 행렬도 목록과 같은 순서로 배열에 담깁니다. 팔 겉면이라면 아래처럼 세 줄이 같은 번호로 맞물립니다.
| 칸 | 노드 | 역바인드 행렬 | 스키닝 행렬 |
|---|---|---|---|
| 0 | 10 위팔 | 위팔의 역바인드 | 위팔 지금 자세 × 위팔의 역바인드 |
| 1 | 11 아래팔 | 아래팔의 역바인드 | 아래팔 지금 자세 × 아래팔의 역바인드 |
| 2 | 12 손 | 손의 역바인드 | 손 지금 자세 × 손의 역바인드 |
이 배열이 그래픽 카드로 넘어갑니다. 꼭짓점을 옮기는 계산은 정점 셰이더에서 돕니다. 정점 셰이더는 그래픽 카드가 꼭짓점 하나마다 한 번씩 부르는 작은 프로그램입니다.
정점 셰이더는 꼭짓점의 본 인덱스로 이 배열의 칸을 바로 찾습니다. 목록 번호와 배열 번호가 같기 때문에 노드 번호를 다시 찾을 일이 없습니다. 찾은 행렬들을 가중치대로 섞어 꼭짓점의 새 위치를 냅니다.
목록을 따로 두는 까닭
첫째 까닭은 꼭짓점 데이터의 크기입니다. 본 인덱스는 꼭짓점마다 몇 칸씩 붙습니다. 꼭짓점이 수만 개면 번호 한 칸의 크기가 그대로 수만 배가 됩니다. 목록 번호는 목록 길이 안에서만 세면 되므로 1바이트로 담을 수 있는 0~255 안에 드는 일이 많습니다.
둘째 까닭은 그래픽 카드로 넘길 행렬의 수입니다. 그리기 한 번 동안 모든 꼭짓점이 똑같이 읽는 값을 유니폼이라고 합니다. 스키닝 행렬 배열은 유니폼에 실립니다.
유니폼에 담을 수 있는 크기에는 한도가 있습니다. 목록을 두면 뼈대 전체가 아니라 그 겉면이 쓰는 조인트만큼만 넘기면 됩니다.
셋째 까닭은 앞 소절의 그림에서 본 나눠 쓰기입니다. 겉면마다 목록이 따로라서 한 뼈대에 겉면을 얼마든지 붙일 수 있습니다. 겉면 하나를 빼거나 바꿔도 다른 겉면의 번호는 흔들리지 않습니다.
목록에 안 오른 조인트
뼈대에는 겉면을 휘게 하지 않는 조인트도 있습니다. 그림의 손잡이처럼 칼이나 방패를 매달 점으로 두는 조인트가 그렇습니다. 칼은 휘지 않으므로 이 조인트의 자식 노드로 붙여 통째로 따라가게 합니다.
이런 조인트는 스킨 조인트 목록에 넣지 않아도 됩니다. 목록에 없는 조인트는 어느 꼭짓점도 가리킬 수 없으므로 그 겉면의 모양에 영향을 주지 않습니다. 목록에 넣지 않으면 넘길 행렬도 하나 줄어듭니다.
거꾸로 꼭짓점이 따라가야 할 조인트를 목록에서 빠뜨리면 그 조인트를 가리킬 번호가 없습니다. 그 부분의 겉면은 그 조인트가 움직여도 제자리에 남습니다.
파일에 적힌 모습
glTF(GL Transmission Format, 3차원 모형 전송 포맷)는 3차원 모형을 담아 주고받는 파일
포맷입니다. 이 포맷에서는 스킨마다 joints 라는 필드가 있습니다. 이 필드에 노드 번호를 목록 순서대로
적습니다. joints 는 스킨에 꼭 있어야 하는 필드입니다.
역바인드 행렬은 inverseBindMatrices 필드가 가리키는 숫자 덩어리에 담깁니다. 이 덩어리의 행렬
순서는 joints 의 순서와 맞아야 합니다. 이 필드를 생략하면 역바인드 행렬은 전부 아무것도 바꾸지
않는 단위 행렬로 칩니다.
꼭짓점 쪽에는 JOINTS_0 과 WEIGHTS_0 이라는 정점 속성이 붙습니다. JOINTS_0 이 본 인덱스입니다.
그 값은 joints 목록의 칸 번호입니다.
본 인덱스는 넷씩 묶어 한 벌로 적습니다. 꼭짓점 하나가 넷보다 많은 조인트를 따르면 JOINTS_1
처럼 한 벌을 더 붙입니다. 이름 끝 숫자가 몇째 벌인지를 뜻합니다. 번호 한 칸은 1바이트나
2바이트 정수입니다.
관련 항목
스킨 조인트 목록이 가리키는 뼈대 구성 요소
조인트 · 스켈레톤 · 노드 · 뿌리 뼈 · 계층 변환 · 씬 그래프
스킨 조인트와 같은 순서로 맞추는 행렬
역바인드 행렬 · 바인드 포즈 · 스키닝 행렬 · 월드 행렬 · 변환 행렬 · 역행렬 · 단위 행렬
스킨 조인트 번호를 쓰는 꼭짓점 값
본 인덱스 · 스킨 가중치 · 정점 속성 · 정점 · 메시
스킨 조인트를 입력으로 읽는 계산
스키닝 · 선형 블렌드 스키닝 · 듀얼 쿼터니언 스키닝 · 스켈레탈 애니메이션 · 정점 셰이더 · 유니폼
스킨 조인트를 담는 파일 포맷
스킨 조인트를 정하는 작업
리깅 · 스킨 바인딩 · 웨이트 페인팅 · 자동 가중치
다른 이름: skin joint · skin joints · 스킨 조인트 목록 · 스킨의 조인트 목록