Quick Syntax
// GL 3.3 Core / GLSL 330 core의 정점 변형 구간
// ids: ivec4, weights: vec4, bones: mat4[32], position: vec3
mat4 skin = weights.x * bones[ids.x]
+ weights.y * bones[ids.y]
+ weights.z * bones[ids.z]
+ weights.w * bones[ids.w];
vec4 skinnedPosition = skin * vec4(position, 1.0);CPU에서 인덱스가 모두 0~31이고 가중치가 음수가 아니며 합이 1이 되도록 준비합니다. 0 가중치 항목도 배열 접근 인덱스는 유효하게 둡니다. 본의 현재 행렬만이 아니라 바인드 포즈를 되돌리는 변환과 기준 공간이 함께 맞아야 합니다.
팔레트의 의미
선형 블렌드 스키닝은 여러 본 변환의 영향을 가중합합니다. 각 팔레트 행렬이 메시의 바인드 포즈 위치를 현재 변형 공간으로 옮기도록 구성합니다. 자산 형식에 따라 메시 노드·스켈레톤 루트 변환이 포함되는 위치가 다르므로 현재 본 행렬 × inverse bind라는 이름만 보고 모든 파일에 그대로 적용하지 않습니다.
변형된 위치가 모델 공간인지 world 공간인지 정하고 이후 model·view·projection을 한 번씩 적용합니다. 팔레트에 이미 모델 변환이 있는데 model을 다시 곱하면 두 번 이동합니다.
정점 입력과 업로드
본 인덱스는 정수 속성 경로 glVertexAttribIPointer, 가중치는 float 또는 정규화된 저장 형식을 사용합니다. 행렬 배열은 링크된 프로그램의 uniform 위치에 glUniformMatrix4fv로 전달할 수 있지만 구현의 정점 uniform 한도와 다른 uniform 사용량을 함께 봅니다.
| 저장소 | 사용할 때 | 제한과 비용 |
|---|---|---|
| 일반 uniform 배열 | 작은 팔레트의 시작 예제 | 정점 단계 성분 한도·재링크 위치 |
| UBO | 구조화된 팔레트 공유 | block 최대 크기·std140 stride |
| SSBO | 더 큰 팔레트·GPU 처리 연동 | 4.3 이상, 레이아웃·동기화 조건 |
인덱스·가중치와 팔레트 연결
GL 3.3에서 정점의 위치는 location 0, 본 인덱스는 2, 가중치는 3을 사용한다고 정합니다. 다음 구조체는 이 세 속성을 한 버퍼에 저장하는 예이며 <cstddef>·<cstdint>가 필요합니다.
struct SkinVertex {
GLfloat position[3];
std::int32_t ids[4];
GLfloat weights[4];
};
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, skinVertexBuffer);
glVertexAttribIPointer(2, 4, GL_INT, sizeof(SkinVertex),
reinterpret_cast<void*>(offsetof(SkinVertex, ids)));
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(SkinVertex),
reinterpret_cast<void*>(offsetof(SkinVertex, weights)));
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);위치 속성도 같은 구조체의 position offset과 stride로 연결합니다. 셰이더에는 layout(location=2) in ivec4 ids;, layout(location=3) in vec4 weights;, uniform mat4 bones[32];를 선언합니다. 버퍼에는 유효한 SkinVertex 배열이 실제로 업로드되어 있어야 합니다.
CPU의 palette를 열 우선 float 32 * 16개로 준비했다면 링크된 프로그램을 선택한 뒤 glUniformMatrix4fv(glGetUniformLocation(program, "bones[0]"), 32, GL_FALSE, palette)로 올립니다. 코드의 32는 행렬 개수이고, 다른 uniform까지 포함한 정점 단계 한도를 넘지 않아야 합니다.
본 하나만 영향을 주는 정점은 ids를 (0,0,0,0), weights를 (1,0,0,0)으로 둘 수 있습니다. 팔레트의 첫 본 행렬(처음 float 16개)이 단위행렬이면 원래 위치가 유지됩니다. 본 0·1의 영향이 절반씩이면 유효한 두 인덱스와 (0.5,0.5,0,0)을 사용합니다. 입력 검증·파일 파싱은 CPU 자산 준비 단계에서 수행하며, 셰이더가 범위 밖 본 인덱스를 자동으로 교정하지 않습니다.
법선과 실패 조건
위 예제는 위치 변형입니다. 법선에 평행이동을 적용하지 않고, 비균일 스케일이 포함되면 역전치 관계를 고려합니다. 여러 회전의 가중합은 다시 완전한 회전 행렬이 된다고 보장되지 않습니다. 단순 mat3 가중합 뒤 정규화하는 근사와 정확한 변형의 법선 처리를 구분합니다. 퇴화한 행렬의 inverse를 무조건 계산하지 않습니다.
가중치 합이 0이면 위 식은 0 행렬이 됩니다. 데이터 준비 단계에서 유효한 본에 가중치 1을 주는 등 정책을 정합니다. 범위 밖 본 인덱스, 다른 포즈의 inverse bind, 프레임별 팔레트 갱신 누락은 메시가 늘어나거나 원점으로 모이는 원인입니다.
버전과 범위
1.x 고정 기능의 기본 행렬 스택만으로 위의 임의 본 팔레트 셰이더를 표현하지 않습니다. 기본 예제는 3.3의 정수 속성·GLSL·uniform 경로를 사용하며 UBO는 3.1, SSBO는 4.3 경로입니다. 애니메이션 파일 파서·키프레임 보간·IK 시스템은 이 카드의 범위 밖입니다.
참고 링크
4 sources