Wednesday, January 9, 2013

DirectX11 렌더링 기법

현재 팀 Project Fate 소속원 중 Hyeon님이 나한테 나름 괜찮은 질문을 하나 주셔서 여기에도 올려본다. 사실 요즘 이런 저런일이 많이 생기다 보니깐 Project Fate과 그 팀원들에 대해 많이 소홀하게 여겼는데 가장 나이가 어린 멤버가 이렇게 열심히 공부하는 모습보고 감탄하고 동시에 미안한 마음이 많이 들기도 했다. 많이 반성해야 할지도..

아무튼 메일로 주신 질문 내용:


일반적으로 렌더링을 할때 가지고있는 object의 vertices가 갖는 위치벡터를 
object space에서 world space로 옮기기 위해 world transform 한번
world space에서 camera space로 옮기기 위해 view transform 한번
camera space에서 canonical view volume으로 perspective projection transform 시키잖아요?  
여기서 궁금한건... 일반적인 경우에 카메라는 하나고, 화면도 하나니까
한 프레임을 렌더링할때 사용할 view transform matrix와 projection transform matrix는 하나잖아요? 
렌더링을 시작하기전에 어디 constant buffer에 집어넣고있으면 만사 오케이인데...
world transform matrix는 물체 하나별로 하나씩 있어야 하잖아요???? 호옹이
vertex shader에서 vertices를 transform 할때 물체별로 다른 world matrix를 적용시켜야 하는건데 
실제로 작업할때엔 저걸 어떻게 구현하나요?? ??_??지금 콬님이 주신 튜토리얼에선 world matrix를 constant buffer에 저장하고있더라구요 
그런데 제가 CUDA 쓰면서 알게된 비디오카드의 메모리 구조에 대한 제 지식이 맞다면 constant buffer은 dynamic allocation이 불가능한걸로 알고잇어요 ㄷㄷ
실제 상황에서 렌더링할 object의 수가 dynamic하게 변할텐데 말이져

제가 찾으면서 스스로 답을 좀 내봤는데요.. 
1. 튜토리얼에서 알려준 방법이 이건데요, 모든 object들을 일시에 vertex shader로 넘기지 않고, object 가 A, B, C 있다면
A object의 world matrix를 상수버퍼에 입력 후, draw A object
B object의 world matrix를 상수버퍼에 입력 후, draw B object
C object의 world matrix를 상수버퍼에 입력 후, draw C object
이렇게 하더라구요
제대로 작동이야 하겠지만, 렌더링 한번하는데 저렇게 Synchronize를 자주해서야 그리는 시간이 물체의 갯수에 비례해서 O(n) 이 되어버리고
작업을 병렬로 처리하고싶은 셰이더의 철학에도 좀 맞지 않는것같아요. 사실 이방법이 조금 아닌거같다는 생각에 이 질문이 나왔어요 허허 
2. 이건 그냥 제스스로 생각해본건데
GPU에 메모리를 sizeof(XMMatrix)*( 물체의 수 ) 만큼 dynamically allocate한후에
vertex shader에서 vertex를 변환할때, 여러 world matrix중에 어느 matrix를 쓸지 선택하는거죠
문제는 제가 이걸 CUDA로는 구현할수있겠는데 HLSL로는 구현을 몬하겟어요 흑흑ㅎ긓ㄱㅎ
왜 구현을 못하겠냐면요
A. CPU 코드에서 dynamic allocate 시킨 메모리를 어떻게 HLSL에서 접근시키는지도 잘 모르겠고요 .............. HLSL문법을 아직 제대로 몰라서 .........
B. vertex shader에다가 이 vertex가 어느 world matrix의 vertex인지 어떻게 알려줄지를 모르겠어요. 이건 방법론적으로 어떻게 해야될지 몰라서 ㅋㅋㅋ 
3. 아니면 object의 vertices들을 애초에 world space에 위치시킨채로 Vertex Shader로 보낸다던가! 
4. 아니면 저따위는 감히 상상할수없는 어떤 사악한 방법이 있어서 잘 할수있다던가!!!
ㅋㅋㅋㅋㅋㅋㅋㅋ 저 혼자선 도저히 방법을 모르겠네요
혹시 아신다면 짧게나마 도움을 부탁드립니다 ㅠㅠ 


사실 본인도 처음에 Constant Buffer에 넣고 하는걸 보고 "그냥 그러려니" 싶었지, 자세하게 왜 그러는지까지는 생각해 본적 없다. 일단 인터넷 검색 조금 해보고 나름 아는것 토대로 답변:

안녕하세요~ 질문 잘 받았어요
항상 열심히 하는 모습 보고 감명 받네요~ 동시에 질문을 읽으면서 "신님아 밸런스 패치 제데로좀"이라는 생각도 들고요
어흑헉ㅇ흐ㅓㄱ흐ㅓ 난 고등학교때 대체 뭘한거지
ㅋㅋㅋ 암튼 질문으로 넘어가자면요.. 우선 저도 DirectX를 거의 책에서 배운거라 실제 AAA퀄리티 게임을 만들때 사용하는 정확한 기법은 저도 잘 몰라요 ㅠㅠㅠ
일단 제가 알고 있는 내용 바탕으로 최대한 정확하게 대답을 할건데요, 저도 아직 배우는 입장이라 모르는 부분이 많을거라 예상되서요; 너그럽게 용서해주세용 ㅎㅎ
또 아시겠지만 제 국어 실력이 좀 병맛이라.. 이해하기 쉽게 설명을 할 수 있을련지도 걱정이 되네요; 조금 이해하기 어렵다라는 부분이 있으면 알려주세요

1. 각각 다른 World Transforms를 가지고 있는 오브젝트 처리를 어떻게 하느냐에 대해서 물어봐 주셨는데요, 제가 알기로는 적으신 방법이 가장 적절하고 무난한 방법이며 실제로 만들어지는 게임에서 가장 자주 채택되는 방법이에요. 즉, 각 오브젝트 마다 Draw가 호출되기 전에 알맞는 World Matrix를 설정하는거죠. 예전에는 World Matrix자체를 Shader Uniform으로 지정해서 썼지만, DirectX 10부터 적으신 Constant Buffer라는게 나오고 나서 부턴 어떤 변수가 외부 소스 (External Source)로 통해 변해지지 않는 이상 쓰기 더 간편하기 때문에 Constant Buffer를 많이 채택해서 쓰는걸로 알고있어요. 
그리고 Synchronization이랑 O(n) Time Complexity에 대해서 얘기해주셨는데요, 사실 프로그램 상에서 강제적으로 GPU에 Synchronization 명령을 던지지 않는다면 실질적으로 일어나는 Synchronization의 횟수는 극히 적어요.. 왜냐하면 GPU 명령들은 다 파이프라인에 축적된 후 때가되면 한번에 모아서 처리해 주기 때문이죠!! (DirectX 책들 보면 아시겠지만 괜히 파이프라인 설명하는데에 두 챕터씩 소모하던게 아니더군요;) 
즉, 아무로 많은 Draw를 호출하셔도 어짜피 다 Overlapping 시키죠.
렌더링은 안타깝지만 병렬 처리가 이 부분을 감쳐주는것 처럼 보이지만 무슨 짓을 하더라도 O(n)보다 더 좋을수는 없다네요.. 정확한건 저도 잘 모르겠지만 Predicate Calculus를 통해 수학적으로 증명이 가능하다라고 들은적이 있어요... 아 물론 저는 논리 해석학 같은거는 잼병이라 증명같은거 기대하시면 안 됩니다 ㅎㅎㅎㅎㅎㅎㅎ 
그래서 Static Geometry같은거 그릴때는 사전에 Transform 시킨 World Coordinate들을 준비시킨 후 미리 존내 큰 Vertex Buffer에다가 갖다 집어넣은 후 한꺼번에 렌더링 시키는 기법도 많이 쓰곤 합니당

2. Instancing에 대해서 설명해 주셨네요. 설명 끝.
ㅋㅋㅋㅋ 농담이고 일단 이 페이지에서 아주 명확하게 설명해 주니깐 혹시라도 Instancing에 대해서 자세하게 알고 계시지 않는다면 여기서 읽으시면 되고요 http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter03.html (영문이에요; 죄송해요) 
간단히 요약하자면.. 여러개의 Vertex Buffer를 이용하는데요, 하나는 Baseline Geometry Data를 가지고, 다른 하나는 오브젝트 마다 Per-Instance data를 지정해줍니다. DrawInstancedIndexed라는 API가 DirectX9에 추가됬지만.. 사실 그 이후 버젼 부터는 거의 추가된 부분이 없어요. 그것도 그럴만한게, Instancing의 주 목적이 GPU에서 하는 일을 CPU로 덜어준다는건데 (XMMatrix가 CPU측 Matrix이에요).. 요새 GPU가 워낙 빨라서 그럴 필요가 점차 줄어들게 되다보니 지금은 거의 안 쓰는 기법이에요. 아무튼 Direct3D에서 하시는 방법을 보고 싶으면 http://www.rastertek.com/dx10tut37.html 여기 참조해주세요

3. 이건 제가 실제로 본 사례가 없어서 실질적으로 사용되는 기법인지는 잘 모르겠지만 아마 안 하는게 좋을거라고 판단되요. 이론적으로 따지자면, 유동적이지 않는 오브젝트거 정말로 많고  Shader내의 Single Matrix Multiplication으로 처리하기 정말로 느린 경우에는 괜찮을지 몰라도 이런 케이스가 실질적인 상황에서 나올지요;

4. 저도 그런 방법이 있으면 듣고싶네욬ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

아무튼 뭘 먹고 그렇게 열심히 공부하시는지는 잘 모르겠는데 그 먹는거 저도 좀 주세요. 저도 님 처럼 똑똑해지게욬ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
그럼 수고하세요~~

결론: 자네는 고등학교때 뭐했나.jpg

No comments:

Post a Comment