프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by cagetu
예전에 개인 블로그에 적은 적이 있기는 한데, 다시 한번 정리해서 올려보겠습니다. 
이전에 Nebula3 라는 공개용 엔진을 참 많이 봤었는데, 그 엔진 기능 중에 Vertex Component Packing 이라는 제목의 관심 있는 포스팅이 하나 있었습니다. 말 그대로 버텍스의 사이즈를 줄여보자는 것입니다. 버텍스 사이즈를 줄임으로써 얻는 이득이 몇 가지가 있는데요.
- 첫째, 게임에서 mesh 리소스의 사이즈를 줄일 수 있습니다. 이게 별거 아닌 것 처럼 느껴지겠지만, 게임을 릴리즈 할 때 정도 되면, 엄청난 양의 리소스 데이터를 줄일 수 있다는 이야기가 됩니다.
- 두번째, 전체적이 로딩 시간을 줄일 수 있습니다.
- 세번재, 전체적인 버텍스 버퍼의 크기를 줄여주기 때문에, 그래픽 카드로 버텍스 데이터를 전송할 때, 버텍스 처리량(vertex though-put)이 좋아집니다.

물론, 추가적인 shader에서 연산이 들어갑니다. 하지만, 얻어지는 이득과는 Trade-Off 관계이기 때문에, 상황에 맞게 선택을 하면 되겠습니다.

Vertex 압축

패킹된 버텍스 구조의 최종 모습은 이렇습니다.

[Nebula3]

각 Comonent이 가지는 특성을 이용해서, 값의 범위에 따라 적절하게 값을 압축하는 형태입니다.


* Position
Position의 경우, 비교적 높은 정밀도와 표현 가능한 값의 범위가 굉장히 광범위 하게 때문에, 사실 압축을 하기란 쉽지가 않습니다. 따라서, 일반적으로 포지션 압축은 사용하지 않습니다.
 
*Texture Coord
텍스쳐 좌표의 경우, [0, 1] 사이의 범위를 가지고, 일반적인 텍스쳐의 경우에는 정밀도가 매우 중요한 편이 아닙니다. 따라서, Texture 좌표의 경우에는 압축을 시도해볼 만 합니다. float을 고정 소수점(Fixed Point)로 변환하여, float2 -> short2로 변환을 합니다. 이렇게 되면, 8Byte의 값을 4Byte로 변환을 할 수 있습니다. 4.12 Fixed Point 변환을 주로 이용하는데, 16비트 중 12비트를 왼쪽으로 이동시키는 것입니다. 혹은 4096으로 나누어 준다고 하면 빠르겠군요. 즉, c++에서 4096으로 나누어 주고, shader에서 4096을 곱해주어서 복원해주는 형태입니다.

- Normal/Tangent/Binormal :
노말은 [-1, 1]의 범위를 가지고 있고, 정밀도 또한 그렇게 중요한 편은 아닙니다. 이는 UBYTE4를 이용하면, 좋은데요. UBYTE4 계열의 특성에 대해서 먼저 알아보자면,

- UBYTE4 : 0 ~ 255 범위의 unsigned char형 4개 (BGRA)
- UBYTE4N : 0 ~ 1 범위의 unsigned char형 4개 (BGRA)
(geforce 7xxx 이상의 그래픽 카드에서부터 지원!!!) 
 

Normal의 경우에는 UBYTE4N을 사용합니다. 이 결과, 12Byte(float*3) -> 4Byte(ubyte4n)으로 줄일 수 있습니다.  http://www.gamedev.net/topic/491408-how-to-design-a-mesh-class-/ 를 참고하시면, 어떻게 압축을 하는지 코드를 보실 수 있습니다.

- Skin Weights
Skin Weight의 경우, [0~1] 사이 범위의 값을 가지기 때문에, 이는 UBYTE4N 형을 사용해서 압축을 하면, 그대로 이용이 가능한 범위입니다. 이를 통해서 float4(16byte)를 UBYTE4N(4byte)로 줄일 수 있군요. 하지만, 이 경우, 정밀도가 영향을 줄 수 있기 때문에, 셰이더에서 다시 한번 정규화를 해주어서, 전체적인 weight를 보정해줍니다.
기본적으로 normal과 동일하게 처리가 되지만, [0, 1]의 범위이기 때문에,  노말과 같이 [-1, 1] -> [0, 1]로 변환해주는 과정 (normal * 0.5f + 0.5f)을 할 필요가 없이 바로 UBYTE4N로 변환을 할 수 있습니다.

return (packedWeights / dot(packedWeights, float4(1, 1, 1, 1)));


- Skin Index
Skin 의 본 인덱스의 경우, [0, n]의 값 범위를 가지게 되는데, bone의 개수가 255를 넘지 않는다며, UBYTE4의 특성을 이용해서, 압축이 가능합니다. 이 결과로 float4(16byte)를 ubyte4(4byte)로 압축이 가능합니다.

이 결과 버텍스 하나가 position(float4), normal(float3), uv(float2), skinweights(float4), skinindices(float4)로 구성되어 있을 때, 압축을 하면, 64Byte -> 28Byte로 버텍스의 사이즈를 줄여줄 수 있습니다. 

결론
 최근 온라인 게임의 리소스 데이터의 용량이 1G가 넘어가는 경우는 이미 일반적입니다. 이러한 상황에서, 리소스 데이터를 다운로딩하는 시간을 줄이는 것 또한 많은 개발사에서 고민을 하고 있습니다. 특히, 해외 서비스를 하는 경우에는 더욱이 이러한 필요성을 더욱 많이 느끼고 있습니다.
이런 고민의 해결책 중 하나가 vertex 압축이 될 수도 있습니다. 약간의 vertex shader 추가적인 연산을 통해서, 데이터 사이즈를 1/3 정도로 줄일 수 있다면, 충분히 시도해볼만한 작업이라고 생각합니다.
 

참고자료
- http://cagetu.egloos.com/4941476
- http://zho.pe.kr/view.html?file_name=doc/vtxprg.txt
http://flohofwoe.blogspot.com/2008/03/vertex-component-packing.html
TAG

댓글을 달아 주세요

  1. Favicon of https://gamedevforever.com 김포프 2011.12.12 05:29 신고  댓글주소  수정/삭제  댓글쓰기

    좋은글 감사합니다. cagetu님 말대로 요즘 vertex 압축 많이 합니다. 특히 메모리가 딸리는 콘솔에선 더더욱 그렇지요. 저흰 PS3에서 메모리 아끼려고 vertex들 다 압축시켜서 메모리에 올려놓은뒤 매 프레임마다 SPU돌려서 uncompress합니다. ㅋ

    위에서 드신 예에서 4바이트 더 줄일 수 있는 방법으로 binormal을 저장 안하는 법도 있습니다. normal과 tangent만 있으면 쉐이더 안에서 외적을 통해 binormal을 쉽게 구할 수 있거든요. 속도도 그리 느리지 않습니다. 어차피 외적이 쉐이더 어셈블리로 하면 MADD(multiply + add) 하나일테니까요. 물론 방향에 따라 -1을 다시 곱해줘야할수도 있지만 +1인지 -1인지는 tangent의 4번째 컴포넌트(w)에 슬쩍 인코딩 해도 되니까요. ^^

    • Favicon of http://cagetu.egloos.com cagetu 2011.12.12 09:43  댓글주소  수정/삭제

      ㅎㅎ.. 온라인에서는 생각보다 버텍스 압축을 많이 사용하지 않는 편인 것 같아요. 하지만, 해외 서비스를 하는 곳들 중에서는 이런 다운로드 용량 때문에 고민이 많은 것 같더라구요. 극단적으로 길드워처럼 스트리밍이 적용되어야 한다면, 더욱 이런 처리가 빛을 낼 수 있을 것 같은데... ㅎ

      물론, 리소스 파이프라인을 구축할 때, 데이터를 압축된 포멧으로 뽑느냐 마느냐에 따라서, 또 다른 고민이 시작되긴 하겠네요. ^^

    • Favicon of https://gamedevforever.com 김포프 2011.12.12 11:49 신고  댓글주소  수정/삭제

      MMO같은 경우에는 메모리에 올릴때 압축을 푸는 경우도 있더라구요. procedural texture generation 하는 Substance도 그런식으로 하더라는~

  2. Favicon of https://gamedevforever.com 귀거리 2011.12.12 14:30 신고  댓글주소  수정/삭제  댓글쓰기

    잘은 모르지만 잘읽었습니다..나중에 서비스할때 도움이 될만한 글인것 같네요 저희 프로그래머들에게 공유하겟습니다 ^^

  3. 감사함. 2012.01.18 11:42  댓글주소  수정/삭제  댓글쓰기

    퍼갈께요.