이제 사람 모델에 애니메이션 기능을 적용했다. 각 파트의 움직임에 맞춰 회전 축(피벗)을 설정하고, 애니메이션 동작을 적용한 것이 주요 변경 사항이다.
피벗 설정 및 Transform 계산 변경
사람의 몸을 그리는 과정에서 파츠마다 회전할 때 피벗 위치가 달라야 했다. 그래서 각 파트에 맞는 회전 중심을 계산해 적용했다.
DrawNode 함수 변경
void Human::DrawNode(const Node& node, const Mesh* mesh, const Program* program, const sglm::mat4& transform) {
sglm::vec3 rotationPivot;
if (node.part->name == "body") {
rotationPivot = sglm::vec3(0.0f, 0.0f, 0.0f);
} else if (node.part->name == "head") {
rotationPivot = sglm::vec3(0.0f, -node.part->scale.y * node.part->size.y * 0.5f, 0.0f);
} else if (node.part->name == "leftUpperArm" || node.part->name == "rightUpperArm") {
rotationPivot = sglm::vec3(0.0f, node.part->scale.y * node.part->size.y * 0.9f * 0.5f, 0.0f);
} else {
rotationPivot = sglm::vec3(0.0f, node.part->scale.y * node.part->size.y * 0.5f, 0.0f);
}
sglm::mat4 pivotTransform = sglm::translate(sglm::mat4(1.0f), -1.0f * rotationPivot);
sglm::mat4 pivotTransformBack = sglm::translate(sglm::mat4(1.0f), rotationPivot);
sglm::mat4 translateModel = sglm::translate(sglm::mat4(1.0f), node.part->translate);
sglm::mat4 rotateModel = sglm::rotate(sglm::mat4(1.0f), sglm::radians(node.part->rotate.x), sglm::vec3(1.0f, 0.0f, 0.0f));
rotateModel = sglm::rotate(rotateModel, sglm::radians(node.part->rotate.y), sglm::vec3(0.0f, 1.0f, 0.0f));
rotateModel = sglm::rotate(rotateModel, sglm::radians(node.part->rotate.z), sglm::vec3(0.0f, 0.0f, 1.0f));
sglm::mat4 scaleModel = sglm::scale(sglm::mat4(1.0f), node.part->scale);
sglm::mat4 sizeModel = sglm::scale(sglm::mat4(1.0f), node.part->size);
sglm::mat4 childTransform = transform * translateModel * pivotTransformBack * rotateModel * pivotTransform * scaleModel;
sglm::mat4 newTransform = childTransform * sizeModel;
program->Use();
program->SetUniform("objectColor", sglm::vec4(node.part->color.x, node.part->color.y, node.part->color.z, 1.0f));
program->SetUniform("transform", newTransform);
mesh->Draw(program);
for (const auto& child : node.children) {
DrawNode(child, mesh, program, childTransform);
}
}
각 파츠의 회전 중심을 지정하기 위해 rotationPivot
변수를 사용했다. 예를 들어, 몸통(body
)은 회전 중심이 (0.0f, 0.0f, 0.0f)
로 설정되지만, 팔이나 머리 등은 상대적인 위치에 맞춰 피벗이 설정된다. 이렇게 피벗을 설정한 후, pivotTransform
과 pivotTransformBack
을 이용해 회전 변환을 적절히 적용했다.
회전은 피벗을 기준으로 하며, 회전 후 원래 위치로 되돌리는 작업을 통해 각 파츠의 회전이 다른 파츠에 영향을 주지 않도록 하였다.
애니메이션 동작 추가
Idle 애니메이션
Idle
함수는 사람 모델이 서 있는 상태에서 간단한 움직임을 주기 위해 작성되었다. 머리가 살짝 흔들리거나 팔이 가볍게 이동하는 정도로 구현했다.
void Human::Idle() {
if (m_state != 1 && m_idleTime == 0) {
return;
}
if (m_state == 1 && m_walkTime != 0) {
return;
}
m_idleTime++;
if (m_idleTime > 100) {
m_idleTime = 0;
}
float headAngle = 5.0f * sin(sglm::radians(m_idleTime * 1.8f));
m_head.rotate = sglm::vec3(headAngle - 1.0f, m_head.rotate.y, m_head.rotate.z);
if (m_idleTime < 50) {
m_head.translate = sglm::vec3(m_head.translate.x, 0.75f + m_idleTime * 0.0006f, m_head.translate.z);
m_body.size = sglm::vec3((1.0f + m_idleTime * 0.001f), (1.0f + m_idleTime * 0.001f), m_body.size.z);
m_leftUpperArm.translate = sglm::vec3(-0.65f - m_idleTime * 0.0006f, 0.15f + m_idleTime * 0.0001f, m_leftUpperArm.translate.z);
m_rightUpperArm.translate = sglm::vec3(0.65f + m_idleTime * 0.0006f, 0.15f + m_idleTime * 0.0001f, m_rightUpperArm.translate.z);
} else {
m_head.translate = sglm::vec3(m_head.translate.x, 0.75f + (100 - m_idleTime) * 0.0006f, m_head.translate.z);
m_body.size = sglm::vec3((1.0f + (100 - m_idleTime) * 0.001f), (1.0f + (100 - m_idleTime) * 0.001f), m_body.size.z);
m_leftUpperArm.translate = sglm::vec3(-0.65f - (100 - m_idleTime) * 0.0006f, 0.15f + (100 - m_idleTime) * 0.0001f, m_leftUpperArm.translate.z);
m_rightUpperArm.translate = sglm::vec3(0.65f + (100 - m_idleTime) * 0.0006f, 0.15f + (100 - m_idleTime) * 0.0001f, m_rightUpperArm.translate.z);
}
}
Idle
함수는 사람의 머리를 살짝 흔들리게 하고, 팔과 몸통을 가볍게 움직인다. sin
함수를 이용해 부드러운 움직임을 구현했으며, 애니메이션이 자연스럽게 반복되도록 m_idleTime
을 이용해 시간에 따른 변화량을 계산했다.
Walk 애니메이션
void Human::Walk() {
if (m_state != 2 && m_walkTime == 0) {
return;
}
m_walkTime++;
if (m_walkTime > 200) {
m_walkTime = 0;
}
float frontLegAngle = 30.0f * sin(sglm::radians(m_walkTime * 1.8f));
float backLegAngle = 15.0f * sin(sglm::radians(m_walkTime * 1.8f));
float bodyTranslate = sin(sglm::radians(m_walkTime * 3.6f)) * 0.05f;
m_body.translate = sglm::vec3(m_body.translate.x, 0 + bodyTranslate, m_body.translate.z);
float armAngle = 30.0f * sin(sglm::radians(m_walkTime * 1.8f));
m_leftUpperArm.rotate = sglm::vec3(-armAngle, m_leftUpperArm.rotate.y, m_leftUpperArm.rotate.z);
m_rightUpperArm.rotate = sglm::vec3(armAngle, m_rightUpperArm.rotate.y, m_rightUpperArm.rotate.z);
if (m_walkTime < 100) {
m_leftUpperLeg.rotate = sglm::vec3(frontLegAngle, m_leftUpperLeg.rotate.y, m_leftUpperLeg.rotate.z);
m_rightUpperLeg.rotate = sglm::vec3(-backLegAngle, m_rightUpperLeg.rotate.y, m_rightUpperLeg.rotate.z);
} else {
m_leftUpperLeg.rotate = sglm::vec3(backLegAngle, m_leftUpperLeg.rotate.y, m_leftUpperLeg.rotate.z);
m_rightUpperLeg.rotate = sglm::vec3(-frontLegAngle, m_rightUpperLeg.rotate.y, m_rightUpperLeg.rotate.z);
}
}
설명
Walk
함수는 사람의 팔과 다리를 반대 방향으로 움직이며, 몸이 앞뒤로 약간 흔들리도록 했다. 이때도 sin
함수를 사용해 자연스러운 동작을 구현했다. 상하 흔들림을 추가하여 걷는 동작이 조금 더 현실적으로 보이도록 했다.
Jump 애니메이션
void Human::Jump() {
if (m_state != 3 && m_jumpTime == 0) {
return ;
}
if (m_walkTime != 0 || m_idleTime != 0) {
return ;
}
m_jumpTime++;
if (m_jumpTime > 400) {
m_jumpTime = 0;
m_state = 1;
return ;
}
float velocity = 0.06f;
float gravity = -0.0006f;
float time = m_jumpTime - 100.0f;
float jumpHeight = velocity * time + 0.5f * gravity * time * time;
if (m_jumpTime < 100) {
m_body.translate = sglm::vec3(m_body.translate.x, 0.0f - sin(sglm::radians(m_jumpTime * 0.9f)) * 0.5f, m_body.translate.z);
m_body.rotate = sglm::vec3(- sin(sglm::radians(m_jumpTime * 0.9f)) * 45.0f, m_body.rotate.y, m_body.rotate.z);
m_leftUpperArm.rotate = sglm::vec3(- sin(sglm::radians(m_jumpTime * 0.9f)) * 30.0f, m_leftUpperArm.rotate.y, m_leftUpperArm.rotate.z);
m_rightUpperArm.rotate = sglm::vec3(- sin(sglm::radians(m_jumpTime * 0.9f)) * 30.0f, m_rightUpperArm.rotate.y, m_rightUpperArm.rotate.z);
m_leftLowerArm.rotate = sglm::vec3(sin(sglm::radians(m_jumpTime * 0.9f)) * 20.0f, m_leftLowerArm.rotate.y, m_leftLowerArm.rotate.z);
m_rightLowerArm.rotate = sglm::vec3(sin(sglm::radians(m_jumpTime * 0.9f)) * 20.0f, m_rightLowerArm.rotate.y, m_rightLowerArm.rotate.z);
m_leftUpperLeg.rotate = sglm::vec3(sin(sglm::radians(m_jumpTime * 0.9f)) * 100.0f, m_leftUpperLeg.rotate.y, m_leftUpperLeg.rotate.z);
m_rightUpperLeg.rotate = sglm::vec3(sin(sglm::radians(m_jumpTime * 0.9f)) * 100.0f, m_rightUpperLeg.rotate.y, m_rightUpperLeg.rotate.z);
m_leftLowerLeg.rotate = sglm::vec3(- sin(sglm::radians(m_jumpTime * 0.9f)) * 90.0f, m_leftLowerLeg.rotate.y, m_leftLowerLeg.rotate.z);
m_rightLowerLeg.rotate = sglm::vec3(- sin(sglm::radians(m_jumpTime * 0.9f)) * 90.0f, m_rightLowerLeg.rotate.y, m_rightLowerLeg.rotate.z);
}
else if (m_jumpTime < 200) {
m_body.translate = sglm::vec3(m_body.translate.x, -0.5f + jumpHeight, m_body.translate.z);
m_body.rotate = sglm::vec3(-45.0f + sin(sglm::radians((m_jumpTime - 100) * 0.9f)) * 45.0f, m_body.rotate.y, m_body.rotate.z);
m_leftUpperArm.rotate = sglm::vec3(-30.0f + sin(sglm::radians((m_jumpTime - 100) * 0.9f)) * 210.0f, m_leftUpperArm.rotate.y, m_leftUpperArm.rotate.z);
m_rightUpperArm.rotate = sglm::vec3(-30.0f + sin(sglm::radians((m_jumpTime - 100) * 0.9f)) * 210.0f, m_rightUpperArm.rotate.y, m_rightUpperArm.rotate.z);
m_leftUpperLeg.rotate = sglm::vec3(100.0f - sin(sglm::radians((m_jumpTime - 100) * 0.9f)) * 100.0f, m_leftUpperLeg.rotate.y, m_leftUpperLeg.rotate.z);
m_rightUpperLeg.rotate = sglm::vec3(100.0f - sin(sglm::radians((m_jumpTime - 100) * 0.9f)) * 100.0f, m_rightUpperLeg.rotate.y, m_rightUpperLeg.rotate.z);
m_leftLowerLeg.rotate = sglm::vec3(-90.0f + sin(sglm::radians((m_jumpTime - 100) * 0.9f)) * 90.0f, m_leftLowerLeg.rotate.y, m_leftLowerLeg.rotate.z);
m_rightLowerLeg.rotate = sglm::vec3(-90.0f + sin(sglm::radians((m_jumpTime - 100) * 0.9f)) * 90.0f, m_rightLowerLeg.rotate.y, m_rightLowerLeg.rotate.z);
}
else if (m_jumpTime < 300) {
m_body.translate = sglm::vec3(m_body.translate.x, -0.5f + jumpHeight, m_body.translate.z);
m_body.rotate = sglm::vec3(0.0f - sin(sglm::radians((m_jumpTime - 200) * 0.9f)) * 45.0f, m_body.rotate.y, m_body.rotate.z);
m_leftUpperArm.rotate = sglm::vec3(180.0f - sin(sglm::radians((m_jumpTime - 200) * 0.9f)) * 210.0f, m_leftUpperArm.rotate.y, m_leftUpperArm.rotate.z);
m_rightUpperArm.rotate = sglm::vec3(180.0f - sin(sglm::radians((m_jumpTime - 200) * 0.9f)) * 210.0f, m_rightUpperArm.rotate.y, m_rightUpperArm.rotate.z);
m_leftUpperLeg.rotate = sglm::vec3(0.0f + sin(sglm::radians((m_jumpTime - 200) * 0.9f)) * 100.0f, m_leftUpperLeg.rotate.y, m_leftUpperLeg.rotate.z);
m_rightUpperLeg.rotate = sglm::vec3(0.0f + sin(sglm::radians((m_jumpTime - 200) * 0.9f)) * 100.0f, m_rightUpperLeg.rotate.y, m_rightUpperLeg.rotate.z);
m_leftLowerLeg.rotate = sglm::vec3(0.0f - sin(sglm::radians((m_jumpTime - 200) * 0.9f)) * 90.0f, m_leftLowerLeg.rotate.y, m_leftLowerLeg.rotate.z);
m_rightLowerLeg.rotate = sglm::vec3(0.0f - sin(sglm::radians((m_jumpTime - 200) * 0.9f)) * 90.0f, m_rightLowerLeg.rotate.y, m_rightLowerLeg.rotate.z);
}
else {
m_body.translate = sglm::vec3(m_body.translate.x, -0.5f + sin(sglm::radians((m_jumpTime - 300) * 0.9f)) * 0.5f, m_body.translate.z);
m_body.rotate = sglm::vec3(-45.0f + sin(sglm::radians((m_jumpTime - 300) * 0.9f)) * 45.0f, m_body.rotate.y, m_body.rotate.z);
m_leftUpperArm.rotate = sglm::vec3(-30.0f + sin(sglm::radians((m_jumpTime - 300) * 0.9f)) * 30.0f, m_leftUpperArm.rotate.y, m_leftUpperArm.rotate.z);
m_rightUpperArm.rotate = sglm::vec3(-30.0f + sin(sglm::radians((m_jumpTime - 300) * 0.9f)) * 30.0f, m_rightUpperArm.rotate.y, m_rightUpperArm.rotate.z);
m_leftLowerArm.rotate = sglm::vec3(20 - sin(sglm::radians((m_jumpTime - 300) * 0.9f)) * 20.0f, m_leftLowerArm.rotate.y, m_leftLowerArm.rotate.z);
m_rightLowerArm.rotate = sglm::vec3(20 - sin(sglm::radians((m_jumpTime - 300) * 0.9f)) * 20.0f, m_rightLowerArm.rotate.y, m_rightLowerArm.rotate.z);
m_leftUpperLeg.rotate = sglm::vec3(100.0f - sin(sglm::radians((m_jumpTime - 300) * 0.9f)) * 100.0f, m_leftUpperLeg.rotate.y, m_leftUpperLeg.rotate.z);
m_rightUpperLeg.rotate = sglm::vec3(100.0f - sin(sglm::radians((m_jumpTime - 300) * 0.9f)) * 100.0f, m_rightUpperLeg.rotate.y, m_rightUpperLeg.rotate.z);
m_leftLowerLeg.rotate = sglm::vec3(-90.0f + sin(sglm::radians((m_jumpTime - 300) * 0.9f)) * 90.0f, m_leftLowerLeg.rotate.y, m_leftLowerLeg.rotate.z);
m_rightLowerLeg.rotate = sglm::vec3(-90.0f + sin(sglm::radians((m_jumpTime - 300) * 0.9f)) * 90.0f, m_rightLowerLeg.rotate.y, m_rightLowerLeg.rotate.z);
}
}
Jump
함수는 점프하는 동작을 시간에 따라 단계적으로 구현했다.
- 도약 단계(0-100 프레임): 처음 100 프레임 동안 몸이 아래로 살짝 숙여진 후, 도약하면서 위로 상승한다. 이 과정에서는
sin
함수를 사용해 부드러운 움직임을 주었다. - 공중 단계(100-300 프레임): 점프 후 공중에 떠 있는 동안 중력과 가속도를 반영한 수식으로 높이를 조절한다.
velocity
와gravity
변수를 통해 시간 경과에 따른 위치 변화를 계산해 자연스러운 체공을 구현했다.
- 착지 단계(300-400 프레임): 착지할 때는 다시
sin
함수를 사용해 부드럽게 지면에 닿도록 하였고, 착지 시 몸의 회전 각도나 팔, 다리의 자세 변화도 추가하여 더 사실적인 효과를 구현했다.
'Computer Graphics > HumanGL' 카테고리의 다른 글
HumanGL - 7. ImGui 추가 (1) | 2024.10.23 |
---|---|
HumanGL - 5. transform 행렬 계산식 변경 (0) | 2024.10.18 |
HumanGL - 4. 문제 발견 (0) | 2024.10.17 |
HumanGL - 3. 사람 그리기 (0) | 2024.10.17 |
HumanGL - 2. Scale 적용 해보기 (1) | 2024.10.17 |