'프로그래밍 팁/3D'에 해당되는 글 3건

  1. 2012.02.26 [Direct3D] SkinnedMesh 대략의 구조 (5)
  2. 2008.11.16 RIM LIGHT
  3. 2008.05.13 비균일 스플라인 spline (1)

예전에 x파일을 대신할 custom 포맷을 만드는 작업을 한 적이 있다. 그 때는 SkinnedMesh의 구조를 직접 소스코드 보면서 전부 분석해서 알고 있었는데, 시간이 지나니 금방 까먹더라. 클라이언트가 메인잡이 아니어서 그렇기도 하고, 구조가 쉽게 외우고 있을 만큼 간단한 편은 아니니 잊어먹을 수밖에.

이번에 다시 x파일을 파싱할 일이 생겼는데, 전에 한 번 했던 작업이니 어렵지 않을 거라고 생각했지만 SkinnedMesh구조에 대해 거의 생각나는 게 없었다. 이제 다시 조금씩 분석하는 중인데 이번엔 까먹지 않기 위해서 대략의 구조를 우선 메모해둔다. x파일은 이제 d3d10부터는 정식으로 지원하지도 않지만, SkinnedMesh를 표현하는 방법에 대한 구조는 이후에도 분명 참조할 일이 있을 듯.

x파일에서 메쉬를 읽어 들이려면 D3DXLoadMeshHierarchyFromX(…) 함수를 사용한다. 함수를 통해 돌려받는 인자는 LPD3DXFRAME* ppFrameHierarchy와 LPD3DXANIMATIONCONTROLLER* ppAnimController 두 개다. 이걸 하나씩 살펴보자.

 

LPD3DXFRAME* ppFrameHierarchy

이건 이름에서 알 수 있듯이 계층을 가지는 – 트리 방식의 – 데이터다. sibling과 child를 가리키는 포인터를 가지고 있어서 트리를 이루고, 이중에 root node인 프레임의 포인터가 반환된다. 프레임 구조체 하나가 캐릭터의 본 하나를 나타낸다. 프레임 구조체에는 크게 세 가지 정보가 있는데, 이 중에 세 번째 MeshContainer가 복잡하다.

  1. LPSTR Name
  2. D3DXMATRIX TransformationMatrix
  3. LPD3DXMESHCONTAINER pMeshContainer

메쉬 컨테이너는 하나의 메쉬 정보를 가진다. 대신 링크드 리스트로 여러 개의 메쉬 컨테이너가 연결될 수 있기 때문에, 프레임당 메쉬 컨테이너는 여러 개가 달릴 수 있다. 실제로 하나의 메쉬 데이터는 ID3DXMesh 인터페이스 객체가 가지고 있다. (버텍스 정보, 인덱스 정보, 속성 정보) 이 메쉬가 사용하는 다수의 Material 정보를 D3DXMATERIAL 타입의 배열 형식으로 가지고 있고, 각각의 D3DXMATERIAL에는 텍스처 파일 이름과 D3DMATERIAL9 구조체가 들어있다.

ID3DXSkinInfo 인터페이스에는 어느 버텍스가 어느 본에 얼마나 영향을 받는지를 나타내는 Weight값이 들어있다. 각 본을 기준으로 데이터가 들어있으므로, 본의 개수만큼의 하위 정보로 나뉜다. 각 본마다 이 본에 영향을 받는 버텍스의 개수 (dwNumBoneInfluences)와, 그 개수만큼의 버텍스 인덱스 및 float타입의 weight를 가진다.

 

LPD3DXANIMATIONCONTROLLER* ppAnimController

AnimController는 분석하기가 힘들어서 그랬지 내부의 값은 좀 더 간단한 형태인듯. 프레임 정보는 트리 방식으로, 메쉬 컨테이너는 링크드 리스트 방식으로 연결되는 부분이 있는데 애니메이션 정보는 그런 자료구조는 없다. 그냥 애니메이션의 수 만큼 배열형식으로 접근 가능하다.

하나의 AnimController는 여러 개의 AnimationSet을 가진다. AnimSet이 캐릭터의 하나의 모션을 표현한다. 예를 들어 걷기, 달리기, 숨쉬기(?) 세 개의 모션을 가진 캐릭터 데이터라면 AnimSet이 세 개다.

하나의 AnimSet은 다시 본 개수만큼의 하위정보를 가진다. 애니메이션 키는 이동, 확대, 회전 세가지 종류가 있고, 각각 개수 정보와 그만큼의 데이터 정보가 들어있다. 본 개수만큼 돌면서 모든 본의 애니메이션 데이터를 참조할 수 있다.

 

위에 정리한 내용은 디테일은 모두 생략한 대략의 큰 크림이다. 실제로 custom 파일 포맷을 만들어 데이터를 저장하려면 위에서 드러나지 않은 디테일한 값들이 더 필요하다. 데이터를 일일이 다 표현하자니 표현방식도 애매하고.. 어차피 대략의 데이터 구조를 잊어먹지 않으려고 정리하는 글이어서 다소 생략된 부분이 많다. 위에 있는 도식들도 처음에는 UML로 그렸었는데.. 왠지 눈에 썩 들어오지 않는 느낌이어서 그냥 손으로 러프하게 그렸음.

아 그리고 custom file에서 프레임 트리구조와 애니메이션 컨트롤러를 따로 로딩, 생성한 다음에 D3DX의 함수처럼 동시에 내어놓는 라이브러리를 만들려면 호출자에게 데이터를 돌려주기 전에 D3DXFrameRegisterNamedMatrices 함수로 이 두 데이터를 연결한 후 돌려주어야 한다.

ID3DXMesh 인터페이스의 내부 데이터에 대한 설명은 정보문화사 DirectX 9를 이용한 3D Game 프로그래밍 입문 책에, SkinnedMesh의 스키닝 정보 및 프레임 계층 구조에 대한 설명은 에이콘 출판사 초보 개발자를 위한 DirectX 게임 데모 프로그래밍 책에 잘 설명되어 있다. Animation 데이터에 대한 설명이 잘 나와있는 책은 아직 본 적이 없음. 누구 알고 있으면 좀 알려주세요.

'프로그래밍 팁 > 3D' 카테고리의 다른 글

[Direct3D] SkinnedMesh 대략의 구조  (5) 2012.02.26
RIM LIGHT  (0) 2008.11.16
비균일 스플라인 spline  (1) 2008.05.13
Posted by leafbird 트랙백 0 : 댓글 5

댓글을 달아 주세요

  1. addr | edit/del | reply Favicon of http://www.facebook.com/RoyTheGame BlogIcon 강윤식 2012.06.14 20:48

    글 너무 잘 보고 갑니다. 이것에 대한 자료 찾기가 쉽지 않아서 애먹었는데 많이 이해하게 되었네요. 감사합니다.
    그런데 한 가지 이해 안되는 부분이 있는데, ID3DXSkinInfo에서 dwNumBoneInfluences가 Bone0안에 들어있는데, Bone0가 아닌 ID3DXSkinInfo에 들어가 있어야 하는건 아닌지요...? 저렇게 보면 Bone0안에 VertexIndex와 Weight가 다수 들어있어야 하는 것 같은데 그건 아닌 것 같아서 조금 헷갈립니다. 답변해주시면 너무 감사드리겠습니다.

    • addr | edit/del Favicon of https://devnote.tistory.com BlogIcon leafbird 2012.06.17 22:47 신고

      답글이 달린줄 이제 알았네요. 도움이 되셨다니 다행입니다.
      ID3DXSkinInfo 인터페이스에는 적어놓은 대로 본을 기준으로 한 데이터가 들어 있습니다. http://msdn.microsoft.com/en-us/library/windows/desktop/bb174206(v=vs.85).aspx 페이지의 인터페이스를 살펴보세요.
      GetNumBones()를 통해 본의 개수를 얻을 수 있고
      GetNumBoneInfluences( 본의 번호 )를 통해 해당 본에 영향을 받는 버텍스의 수를 얻을 수 있고
      GetBoneInfluence( 본 번호, 버텍스 인덱스 아웃풋, 버텍스 가중치 아웃풋 )을 통해 해당 본의 버텍스 인덱스와 가중치를 얻을 수 있습니다.
      제가 적어놓은 글을 오해없이 제대로 이해한 것 같으신데요. 혹시 제가 틀리게 적은 부분이 있다면 알려주세요 :)

  2. addr | edit/del | reply Favicon of https://alleysark.tistory.com BlogIcon 앨리삵 2013.05.06 23:14 신고

    dx입문한지 얼마안된 학생입니다. 샘플보고 구현은 했지만 속에서 도대체 어떻게 돌아가고있을까 너무 궁금했는데.. 정말 많은 도움됬습니다 ㅎㅎ

  3. addr | edit/del | reply 깐다삐아 2013.07.09 00:30

    이해가 아주 쏙 잘 되네요. 좋은 글 감사합니다.

RIM LIGHT

2008. 11. 16. 14:32 from 프로그래밍 팁/3D

http://narew.net/blog/49


요즘 라이팅 기법중에 "대세"라고 까지는 모르겠습니다만, 구현도 쉽고
느낌도 괜찮기 때문에 많이 쓰이는 것 같습니다. ( 요즘 캐쥬얼 게임을 만들다 보니
카툰쪽 라이팅 말고는 구현해본게 오랜만인거 같네요. )

그래서 저도 한번 구현해 보았습니다. 

RIM LIGHT란, ( LIM LIGHT라고 하는 곳도 있음 ) 후광효과를 내기 위한
LIGHT ( 혹은 조명 )이라고 보시면 될 것 같습니다.
사실 이런건 직접 눈으로 보는 것이 이해가 빠른데, 간단하게 말로 표현하자면,
예수나 석가모니 뒤에 흔히 하는 후광처럼 과장된 것이 아니라 은은하게 물체를
더욱 두드러져 보이게 하는 빛입니다.

이렇게 후광을 구현하기 위해선, 단 두가지만 알아 두면 됩니다.

1. 피사체 ( 물체 )의 외곽-실루엣. ( fresnel항 )
2. 빛을 등지고 있는지의 여부 ( backlight )

후광의 경우에는 물체의 외곽이 되는 라인에 빛이 먹기 때문에 구해야 하며,
역광을 표현하기 위해서는, 빛을 정면으로 받고 있을땐 RIM LIGHT이 먹지 않게 해야 합니다.

1. 피사체 ( 물체 )의 외곽-실루엣. ( fresnel항 )

사용자 삽입 이미지

위의 스크린샷을 보다시피, 외곽라인에 많은 빛이 먹는 것을 볼 수가 있습니다.
현재 모델은 normal map을 이용한 per pixel lighting으로 처리되는 normal값을
사용하였습니다.

fresnel을 구하기 위해서는, 정점(vertex)의 방향벡터(normal)값과 카메라와
정점(vertex)의 방향값의 내적을 구하면 됩니다.

간단하게 HLSL코드로 설명하자면,


// 카메라에서 정점을 바라보는 방향
vEye = normalize( vPS.xyz - g_vCameraPos.xyz );

// 프레넬항을 구합니다. ( 카메라에서
fresnel = 1.0 - abs( dot( vNormal, vEye ) );
 


이렇게 됩니다. 위의 스크린샷은 이것을 그대로 색으로 치환한 경우입니다.

2. 빛을 등지고 있는지의 여부 ( backlight )

역광이기 때문에, 빛을 정면으로 받고 있을 때가 아닌 빛을 등지고 있을때만 먹도록 해야
합니다.

사용자 삽입 이미지


이처럼, 모델과 라이트의 위치는 그대로 유지한채, 카메라만 캐릭터의 뒤로 향하게 하면,
후광이 먹는 모습을 볼 수가 있습니다. 빛의 정면에선 후광이 먹지 않는 것이지요.

이렇게 빛을 정면으로 먹는 정도는, "빛-정점 방향"과 "카메라-정점 방향"의 내적을 통해서
구할 수가 있습니다.

이때, 내적의 경우 두 방향 벡터의 각도가 90도가 되면 0이 되며, 일직선이 되면,
1이 되거나 -1이 됩니다.



// 카메라 시점에서 정점을 바라본 방향
vEye = normalize( vPS.xyz - g_vCameraPos.xyz );

// 빛의 시점에서 정점을 바라본 방향
vLight = normalize( vPS.xyz - g_vLightPos.xyz );

// 백라이트 값
float backlight = dot( vLight, vEye ) );
 


HLSL코드로 보았을때 위에처럼 backlight를 구하게 되면, backlight값은
-1~1의 사이로 결과가 나오며, 카메라가 빛을 정면으로 보고 있는 시점
( 즉, 물체 입장에선 후광이 비쳐지는 시점 )에서 -1이 나오므로 이것을
역으로 해줄 필요가 있습니다.


// 백라이트 값
float backlight = -dot( vLight, vEye ) );
 


또한, 우리가 표현하고자 하는 것은, 후광인 경우이며 backlight값이 0이 될때,
vLight와 vEye의 사이각이 90도 정도가 됩니다. 즉, 0 이하의 값은 버리면 간단히
구현이 완료 됩니다.


// 백라이트 값
float backlight = max( 0, -dot( vLight, vEye ) );
 



이렇게 간단한 공식만으로도 구현이 가능합니다.

사용자 삽입 이미지


위의 마지막 스크린샷에서 첫번째 캐릭터는 빛을 정면으로 받고 있는 모습이며,
두번째와 세번째 사진은 각각 빛을 등지고 있는 상황입니다.
두번째는 RIM LIGHT가 적용되지 않았으며, 세번째는 RIM LIGHT가 적용된 상태입니다.


*넥슨 컨퍼런스( NDC )의 동영상 자료를 참고하였습니다.

'프로그래밍 팁 > 3D' 카테고리의 다른 글

[Direct3D] SkinnedMesh 대략의 구조  (5) 2012.02.26
RIM LIGHT  (0) 2008.11.16
비균일 스플라인 spline  (1) 2008.05.13
Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요


gpg 4권에 있는 코드를 d3d 버전으로 수정하고, 최적화 관련 일부 처리한 버전.

'프로그래밍 팁 > 3D' 카테고리의 다른 글

[Direct3D] SkinnedMesh 대략의 구조  (5) 2012.02.26
RIM LIGHT  (0) 2008.11.16
비균일 스플라인 spline  (1) 2008.05.13
Posted by leafbird 트랙백 0 : 댓글 1

댓글을 달아 주세요

  1. addr | edit/del | reply black_H 2008.08.29 17:12

    도움이 되는 소스입니다^^ 고맙습니다~