이번에 물리 엔진을 메모리 최적화하면서 성능이 확 올라갔는데, 이게 오히려 새로운 문제를 만들었다.
성능이 좋아지다 보니 duration(물리 연산에 사용하는 시간 단위)을 크게 설정했는데, 7 x 7 x 7의 박스 탑에서 이상한 현상이 벌어졌다.
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
duration이 커서 중력 영향을 크게 받게 되니까, 박스들이 서로 요동치면서 건드리지도 않았는데 스스로 무너져버렸다.
나는 이 문제를 정지 접촉(Rest Contact) 쪽 이슈라고 판단했고, 그래서 Sleep 상태와 Awake 상태를 추가해서 해결을 시도했다.
먼저 setSleep() 함수를 살펴보면:
void Rigidbody::setSleep(float duration)
{
if (m_canSleep)
{
m_sleepTime += duration;
if (m_sleepTime > START_SLEEP_TIME)
{
m_isAwake = false;
}
}
}
이렇게 setSleep()이 호출될 때마다 sleepTime이 누적되고, START_SLEEP_TIME을 넘어서면 Sleep 상태로 전환된다. Sleep 상태가 된 물체는 물리 연산을 전혀 하지 않고, 충돌의 주체로도 간주되지 않는다. (단, 다른 오브젝트가 이 오브젝트에 충돌해 들어오는 건 인정)
그렇다면 setSleep()은 언제 호출될까? 조건은 아래와 같다.
이 세 가지 상태가 START_SLEEP_TIME 동안 유지되면 Sleep 상태가 되는 것이다.
Awake 상태는 말 그대로 Sleep을 하지 않는 상태다. 물리 연산 대상이 되고, 충돌의 주체가 될 수 있다. 위에서 말한 setSleep() 조건을 하나라도 만족하지 못하면 setAwake()가 호출된다.
void Rigidbody::setAwake()
{
m_sleepTime = 0.0f;
m_isAwake = true;
}
호출되면 sleepTime은 0으로 초기화되고 Awake 상태가 된다.
정지 접촉이란, 두 물체의 접촉을 충돌이 아니라 단순히 붙어 있는 정지 상태로 보는 것이다. 조건은 다음과 같다.
3번이 약간 헷갈릴 수 있는데, 이 조건이 없다면 한 줄로 쌓여 있는 상자들 중 맨 위에 있는 상자 옆에 정지 접촉을 이용하여 다른 상자를 붙일 수 있게 된다. 그러면 추가한 상자 아래엔 아무것도 없는데도 "옆의 상자와 정지 접촉을 유지한다"는 이유만으로 물리 연산에서 빠져서 중력도 안 받게 되는 결과가 발생한다. 그렇기 때문에 3번 조건을 둬서, 중력 방향으로 충분히 지지받는 상태인지 판별해야 한다.
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
이렇게 Sleep과 Awake를 추가해주면, 초기에는 Sleep 상태라서 박스들이 서로 안정적으로 보인다. 그러나 공으로 박스들을 건드리는 순간 박스들이 요동치기 시작한다.
처음엔 "Sleep 처리를 했는데도 박스가 이상하게 무너지는 걸 보면, 다른 데 문제가 있나?" 싶어서 이것저것 테스트해봤더니, velocity constraint solve와 position constraint solve의 반복 처리를 잘못하고 있었다.
충돌을 N번 solve로 반복 처리할 때, 속도나 위치를 어느 타이밍에 업데이트하는 게 맞을까?
내 생각엔 2번이 올바른 방법이다.
물리 엔진은 자연스럽게 수렴하는 게 핵심이기 때문에, 2번 타이밍마다 속도/위치를 업데이트해서 충돌 내 접촉점끼리는 같은 정보를 쓰고, 하나의 충돌 처리가 끝날 때마다 결과를 다른 충돌에도 반영되도록 만들어줘야 된다.
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
속도, 위치 업데이트 타이밍을 바꿔주니까 확실히 충돌 처리가 자연스러워졌다.
결국 Sleep 상태와 Awake 상태 도입 + Constraint Solve 반복 처리 시점 수정 이 두 가지로 문제가 해결됐다. 사실 Sleep 처리를 추가했는데도 문제가 계속 나타났을 때 정말 당황했고, 해결책을 찾기까지 삽질을 많이 하면서 많이 고생했다. 그래도 어떻게든 해결한 것을 보니, 무엇이든 열심히 하다 보면 어떻게든 해결이 되는게 맞는 것 같다.
다음엔 좀 더 빠르게 문제 해결에 도달할 수 있도록, 물리 엔진의 내부 로직과 수렴 방식에 대해 더 깊이 공부해야겠다.
| ALEngine: Frustum Culling 구현 - (1) (0) | 2025.02.17 |
|---|---|
| ALEngine: 게임 엔진 제작 합류 - (0) (0) | 2025.02.17 |
| FT_Newton: 메모리 풀 구현 - (12) (0) | 2025.02.07 |
| FT_Newton: 최적화를 통해 FPS 방어하기 - (11) (0) | 2025.02.04 |
| FT_Newton: Cylinder 와 Capsule 충돌 - (10) (0) | 2025.01.16 |