화재, 피, 폭발 - <프로토타입 2>의 과장된 효과 기술
작성자: 케이스 오코너(Keith O’Conor) 작성일: 2012년 10월 23일
<게임 개발자 잡지> 2012년 4월호에 실린 이 기사에서, 래디컬 엔터테인먼트(Radical Entertainment)의 시니어 렌더링 코더(senior rendering coder)인 케이스 오코너(Keith O’Conor)는 게임의 파티클 시스템(particle system) 요소에 대해 설명한다. 특히 오픈 월드 어드벤처 게임에서 잘 실행되는, 멋진 효과를 만드는 방법에 대해 상세히 살펴볼 것이다.
<프로토타입(Prototype)> 세계의 특징 중 하나는 지나치게 혼란한 오픈 월드다. 우리는 혼란을 만들기 위해 대단히 많은 양의 파티클 효과를 이용했다. 화재, 피, 폭발, 그리고 무기 충격 효과로 환경을 채운 것이다.
제임스 헬러 병장(Sgt.James Heller/ 메인 캐릭터)은 환경 내부 어디라도 갈 수 있다. 빌딩 측면을 달려 올라갈 수도 있고, 옥상을 가로질러 활공하거나, 심지어 납치한 헬리콥터로 도시를 가로지를 수도 있다. 이 때문에 우리는 어느 때든지 알아볼 수 있는 수백 개의 복잡한 효과들과 수천 개의 파티클을 지원하기 위한 이펙트 시스템이 필요했다.
이 글에서는 <프로토타입>이나 <프로토타입 2>에 필요한 수준의 이펙트를 밀어넣을 수 있게 하는 기능들을 더하고 향상시킴으로써, 래디컬 엔터테인먼트에서 <스카페이스(Scarface)>와 <헐크: 최후의 파괴(Hulk: Ultimate Destruction)>를 위해 개발한 시스템을 우리가 어떻게 구축했는지 설명하려고 한다.
파티클 모의 실험과 제작하기
우리의 파티클 시스템들은 전적으로 컴포넌트 기반의 특징 세트로 구성되어 있다. 각 특징은 파티클을 처리하는 방식의 한 측면을 의미한다. 예를 들어, 중력이나 다른 어떤 힘에 의해 위치가 바뀌고, 중심점 주위를 돌고, UV에 생기를 불어넣고(animating UV), 계속해서 크기를 바꾸는 것과 같이 말이다.
이펙트 아티스트(effect artist)는 개별 파티클 시스템을 만드는 특징을 뭐든 선택할 수 있으며, 각각의 선택된 특징은 그가 수정하거나 생기를 불어넣을 수 있는 (속도, 중량, 색상 같은) 관련 속성을 드러낸다. 이는 마야의 표준 애니메이션 툴셋으로 이루어지는데, 같은 시뮬레이션 코드를 이용해 마야 플러그인(Maya plug-in)으로 엮음으로써 마야와 게임이 모두 끊임없이 작동하게 했기 때문이다.
아티스트가 마야에서 파티클 시스템의 스타일과 작동에 만족하면, 게임에 실을 수 있는 효과로 내보내진다. 그러면 이 효과는 우리의 인게임 에디터 “더 짐(The Gym)”을 이용해 게임플레이에 쓰이는데, 이 복잡한 상태 기계 에디터는 디자이너들이 게임의 모든 측면을 통제할 수 있게 해준다.(자세한 사항은 우리의 <GDC2006> 프레젠테이션1을 보라.)
특정 상황에서 플레이하기 위한 효과 스크립트를 쓸 때, 이펙트 아티스트는 추가 컨트롤 설정, 가령 오버라이드(override)와 편향(biase)에 접근한다. 각 기능에 대한 추가 속성으로, 이펙트 아티스트는 활발한 가치에 편향(증폭)되거나 그것을 완전히 무시할 수 있다. 이는 하나의 연관된 효과가 다양한 상황에서 쓰일 수 있도록 했다. 예를 들어, 아티스트가 표준 연기 효과를 이용해 작고 가볍고 빠르게 움직이게 하거나, 크고 짙고 검은 연기를 선택할 수 있다. 그저 배출량, 색상, 속도 같은 속성을 무시하거나 증폭시킴으로써 말이다. (그림 1의 예제 참조)
그림 1. 원래 효과(왼쪽)와 편향, 오버라이드를 다르게 준 세 가지 변형
아티스트들은 다수의 상황에서 동일한 포괄적인 효과를 사용한다거나 같은 효과의 비슷한 버전을 많이 싣고 제작하는 대신에 ‘더 짐’을 이용해 게임 내에서 사용되는 상황에 따라 각 효과를 조정할 수 있다. 이는 게임 내 조명과 애니메이션으로 효과를 살릴 수 있게 함으로써 메모리 사용량을 줄이고 아티스트의 작업 흐름을 향상시킨다. 편향과 오버라이드는 또한 이 글의 후반부에 설명할, 지속적인 LOD 시스템(level-of-detail system)의 주요 부분이다.
각 파티클 시스템의 속성은 모든 파티클의 위치, 수명, 속도 등등을 각각의 배열에 밀집해 저장한다. 이렇게 데이터 지향적인 디자인을 통해 모든 프레임의 시뮬레이션 상태를 업데이트할 때 캐시 효율성이 높은 방식으로 데이터에 접근할 수 있게 해준다. 이 부분은 파티클 시뮬레이션을 할 때 CPU 실행에 큰 충격을 줄 수도 있다.
이 방식으로, 우리는 복잡한 작용으로 수천 개의 파티클들을 시뮬레이션 할 때 CPU 시간을 조금밖에 사용하지 않는다. 또한 비교적 간단하게 PS3의 비동기 SPU를 실행할 수도 있다. 각 기능을 업데이트할 때, 관련 없는 데이터는 하나도 없이 그 기능에 필요한 속성만 기억장치에 직접 접근해 가져온다는 뜻이다.
파티클 위치 분리는 다른 실행과 같은 이점을 가진다. 빨라지고, 정확한 알파 혼합 렌더링을 위한 캐시 효율성 높은 카메라 관련 분류처럼. 이는 또한 다른 기능들도 가능하게 하는데, 말하자면 다른 시스템의 파티클 처리 프로세스에 넣음으로써, 시뮬레이션 업데이트의 위치 출력 기능 구분을 이용해 다른 파티클들을 내보낼 수 있다.
메모리 사용과 조각화 줄이기
가용 메모리를 쪼개면, 많은 단기 파티클 효과를 언제나(예를 들면 심각한 전투 상황에서) 진행할 수 있게 된다. 그래서 인접한 미사용 메모리의 양을 제한하는 “스위스 치즈(Swiss cheese)” 효과로 이어지며 메모리의 많은 작은 조각들이 본질적으로 무작위로 할당되고 마련될 때 분열이 일어난다.
다시 말해, 힙(heap) 내 미사용 메모리의 총량은 효과를 내는 데는 충분하겠지만, 메모리는 실제 사용하기엔 너무 작은 양의 힙으로 흩어질 것이다.(분열과 할당을 위한 가이드는 스티븐 토비(Steven Tovey)의 훌륭한 글 #AltDevBlogADay 2 를 읽어보라.) 비록 우리가 분할의 위치를 알기 위해 파티클 할당량을 분할해 사용하고 있지만, 이는 여전히 문제가 된다. 다행히도 우리는 분할을 제한할 수 있는, 그리고 문제가 될 때 다룰 수 있는 작은 트릭이 있다.
우리는 분열과 동적 할당 비용을 피하기 위해 가능한 한 언제나 (처음부터 할당된) 고정된 세그먼트화 메모리 풀을 사용한다. 그 부분은 파티클 시스템 할당에 가장 흔히 사용되는 구조에 크기가 맞춰져 있다. 이 풀이 가득 찰 때만 동적 할당 비용을 실행하면 된다. 이러한 일은 특별히 심각한 전투의 순간이나 많은 파티클 효과가 한번에 실행되는 순간에만 일어난다.
우리의 효과 시스템은 하나의 파티클 시스템이 만들어질 때 메모리 할당을 여러 개 해둔다. 만약 이중 어떤 것이 (분열, 혹은 힙이 다 차서) 실패하면, 효과는 만들어지지 않는다. 효과를 반만 만들거나 이미 만들어진 어떤 할당을 비우거나 (아마 힙을 더 분열시키거나) 하는 대신에 우리는 효과 힙에서 크게 하나를 할당한다.
이게 성공하면 작업을 진행하고 이 메모리를 모든 할당에 사용한다. 만약 실패하면, 효과를 초기화하려는 시도조차 하지 않고, 그냥 플레이를 안 한다. 플레이어의 관점에선 분명히 바람직하지 않다. 폭발 효과가 실행되지 않으면 폭발하는 차가 매우 이상하게 보일 것이기 때문에, 이는 마지막 방책이다. 대신에, 우리는 힙이 절대 꽉 차지 않게 하거나 처음부터 과하게 분열되지 않도록 한다.
이것이 끝날 때쯤에, 우리가 하는 것 중 하나는 대략 효과의 분류에 따라 “저장소(stores)”에 효과를 분할하는 것이다. 우리는 폭발, 잔잔한 효과, 총알 폭죽, 그밖의 다양한 다른 효과 유형의 저장소를 가지고 있다. 이렇게 효과를 구분함으로써, 언제든지 존재하는 특정 유형의 효과 수를 제한할 수 있다.
우리의 효과 힙은 이와 같이 다른 유형의 효과에 메모리를 허용하지 않기 때문에, 수백 개의 피 튀기는 효과 같은 것들로 가득 차지 않는다. 저장소는 배열로 구조화된다. 저장소가 가득 차고 새로운 효과가 실행될 때, 저장소에서 가장 오래된 효과는 “폐기장(graveyard)” 저장소(오래된 효과가 사라지는 곳)로 옮겨진다. 그 효과들의 배출률은 0으로 정해져 있어서 새로운 파티클들이 배출될 수 없고, 특정 시간(보통 단 몇 초)이 지나면 사라지고 죽게 되며, 그 결과 삭제된다.
효과를 저장소에 분할하는 것은 또한 우리가 효과 유형에 따라 다른 최적화를 실행할 수 있도록 해준다. 예를 들어, 우리는 “폭죽(squib)”에 있는 효과는 모두 불꽃이나 담배연기처럼 작은 단명 효과라는 것을 추측할 수 있다. 따라서, 카메라 바깥 혹은 특정 거리보다 훨씬 멀리에서 이런 효과가 실행될 때, 우리는 그냥 효과를 전부 실행하지 않으며 누구도 이를 알아채지 못한다.
※ 자세한 내용은 첨부(PDF)화일을 참고하시기 바랍니다.
|