상세 컨텐츠

본문 제목

[포니 게임 개발] 29. 텍스쳐 기반 버텍스 모핑 외 기타

프로그래밍/포니게임개발

by ∫2tdt=t²+c 2013. 7. 17. 03:11

본문




한동안 작업내역을 업로드하지 못했는데, 작업내역이 없어서가 아니라... 한일은 참 많은데 눈에 보이지가 않아서 묵혀두고 있었던거에요!


작업내역은 다음과 같아요.

- 버텍스 모핑이 가능하도록 엔진 개량

- 모핑이 적용된 모델을 Export할수 있도록 맥스스크립트 개량

- 스크립트 엔진을 루아에서 PGLight로 교체

- 메인6 캐릭터 모델링 최적화

- 모델용 텍스쳐 최적화를 위한 어도비 포토샵용 JSX 스크립트 작성


먼저 요 근래 작업 중 가장 눈에 띄는건 (제목에 나와있듯이) 텍스쳐 기반 버텍스 모핑을 이용하여 표정 애니메이션을 구현한 것입니다. 원래 표정과 같이 미묘한 부분은 뼈대를 심어서 애니메이션하기에 적당하지 않기에 (원시적이지만 강력한) 버텍스 블렌딩(Vertex Blending; Blending 대신 Morphing이라고 부르거나 Tweening이라고 하기도 합니다.)을 이용할 수 밖에 없는데요, 기존 엔진에서는 이 기능을 전혀 구현해두지 않았던 것입니다.


DX10 이상에서는 지오메트리 셰이더 등을 통해서 Vertex Blending을 아주 쉽고 강력하게 구현할 수 있는데요 (DX10 예제도 있죠), 불행히도 DX9에서는 지오메트리 셰이더 따위는 없어서 버텍스 모핑 구현하기가 조금 까다롭습니다.

처음에는 아무 고민도 안 해보고, 예전에 Vertex Tweening 구현할 때 마냥, 버텍스 스트림으로 모든 Shape을 넘겨주고, 버텍스 셰이더 쪽에서 넘겨받은 버텍스를 이용해 적당히 보간하는 식으로 구현하려고 했습니다. 그런데


- 일단 버텍스 스트림으로 넘겨줄수 있는 Shape의 수에 한계가 있고(보통 표정 애니메이션을 위해서 적어도 40개 이상의 Shape을 사용한다고 하는데, 이 방법으로는 적으면 2개, 많아봤자 4개..?밖에 안됩니다)

- 그 많은 버텍스 세트를 스트림으로 넘겨주다 보니 병목이 걸릴 수 밖에 없다


는 치명적인 문제가 있더군요.


그래서 검색을 해본 결과 http://www.robg3d.com/?page_id=33 이렇게 좋은 글이! 있더군요.


테크닉을 요약해서 정리하자면,

1. 버텍스마다 차례로 인덱스를 준다.

2. Shape들은 기본 모양으로부터의 차이값을 구하여 (16비트 부동소수점 포맷을 지원하는) 텍스쳐에 저장한다.

3. 렌더 타겟을 새로 설정하고, 블렌딩할 Shape들이 저장된 텍스쳐에 가중치를 주어 차례로 렌더링한다. 이때 가산혼합이 되도록 RenderState를 설정하는게 필수

4. 렌더 타겟에 렌더된 결과물이 블렌딩으로 인해 최종적으로 버텍스가 움직이는 값이 된다.

5. 이제 버텍스 셰이더에서 3,4번에서 구한 텍스쳐를 참조하여, 버텍스를 움직여준다.(버텍스 셰이더에서 텍스쳐를 참조하기 위해서는 적어도 vs3.0 이상의 셰이더모델이 요구됩니다. 요즘 그래픽 카드에선 대부분 지원!!)


텍스쳐에 Shape별 버텍스 변위를 차곡차곡 쌓아두었다가, 실제로 개체를 렌더링할때는 이 텍스쳐를 참조하는 것입니다. Vertex Texture Fetch(http://www.slideshare.net/henjeon/ndc2010-2 이 기술은 왼쪽의 슬라이드가 좋은 예라고 할 수 있겠네요)와 유사한 아이디어가 돋보이는 테크닉이죠. 물론 렌더링 패스가 하나 늘어나지만, 그로 인한 손실이 엄청난 버텍스 스트림을 주고받는 병목현상에서 발생하는 손실보다 훨씬 적기 때문에 더 빨라집니다.


이 방법을 약간 손봐서 적용시켰습니다.

- 블렌딩에 사용되지 않는 버텍스들에게는 인덱스를 -1로 주었습니다. 버텍스 셰이더에서는 먼저 인덱스가 음수인지를 검사하여 텍스쳐를 참조해야하는지를 알아냅니다.

- 텍스쳐는 4096x1 사이즈의 1D A16B16G16R16 텍스쳐를 2장(1장은 Position용, 다른 1장은 Normal용 : 실은 Normal을 패킹하여 텍스쳐 하나에 모두 구겨넣는것도 가능하지만, 정밀도를 위해서 그냥 두 장 썼어요.) 이용했습니다.

- 멀티렌더타켓을 이용하면 Position과 Normal 텍스쳐를 동시에 렌더링할 수 있습니다. 렌더링 횟수는 최대한 줄여야죠.

- 픽셀 셰이더를 잘 이용하면 동시에 여러 개의 Shape 텍스쳐를 렌더링할 수 있습니다. 저는 2개 Shape을 동시에 렌더링하는 걸로 했어요.

- 블렌딩 Shape이 저장된 텍스쳐 역시 1D 텍스쳐. 블렌딩에 사용되는 버텍스만 추려내서 만들어보니 보통 크기가 512 미만으로 나옵니다.

- 모핑을 사용하는 개체가 하나뿐이라는 보장이 없기때문에, 전체 오프셋 변수를 줬습니다. 렌더링 직전에 모핑을 사용하는 오브젝트를 추려내 인덱스 오프셋을 부여받습니다. 이 오프셋을 적용하여 블렌딩 텍스쳐를 사용하기 때문에, 동일한 텍스쳐를 사용하면서도 다른 객체의 블렌딩에 전혀 영향을 받지 않을 수 있습니다.


이 구현대로라면 동시에 많으면 10개 정도의 모핑 개체를 렌더링할 수 있습니다. 저거보다 많은 수를 모핑할일은 없다고 과감하게 가정했습니다ㅋ




짠! 모핑 10개 제한은 넉넉해 보입니다.



또 다른 중대한 변화는 스크립트 엔진을 교체했다는 것이죠. (사용자 문제일테지만) Lua의 가비지 컬렉터가 작동하면 자꾸 무한루프에 빠져서 게임을 중단시켜버리는 고질적인 버그가 있어서 수정도 못하고 쩔쩔매다가, 아예 PGLight를 개발하게 된거죠. PGLight 관련 이야기는 여기서 더 살펴보시면 될듯합니다.


모델링 최적화 역시 중요한 작업이었습니다. 포니 몸매를 보면, 모두 단색에 허벅지쪽에 큐티마크가 유일한 무늬인데, 이 몸통 전부가 텍스쳐링 되어있어서, 텍스쳐 낭비가 심했습니다. 그래서 허벅지부분 큐티마크쪽만 텍스쳐를 사용하고, 나머지 부분은 Diffuse색상을 이용해 렌더링 할 수 있도록 모델을 개량했어요. 그리고 부착지점 기능을 이용해 머리카락과 꼬리를 몸통 모델에서 분리해냈습니다. 즉, 이제는 머리카락 모양, 꼬리 모양, 몸통 색, 큐티마크를 커스터마이징하는것이 가능해졌다는 것이지요. (하지만 게임에 사용할 계획은 아직 없어요.)


이런 저런 기술들을 자꾸 적용하다 보니, 점점 프로젝트가 커져만 가네요. 이거 끝은 낼 수 있을지..?ㅋ

아직도 할 일이 참 많습니다.

- 모션블러 버그 수정

- 물리 버그 수정 (아예 PhysX를 이용할까 고민중)

- 레벨 에디터 제작

- 게임 디자인

관련글 더보기

댓글 영역