반응형

 

2012/05/30 13:28

복사http://blog.naver.com/sorkelf/40160183951


인스턴싱 (Instancing)

 : 동일한 객체(오브젝트)를 한 화면에 여러개를 렌더링 시킬 경우에 한번의 DP Call로 처리 하는 방식을 말한다.
   (나무, 풀, 스프라이트, 파티클 등등..)








즉 50개 메시 -> Dp Call 50번 이 아닌 
    50개 메시 -> DP Call 1번으로 처리한다는 것이며 이는 DpCall에 따른 오버헤드를 줄여준다



DirectX SDK에서는 4가지 방식의 인스턴싱 방식을 제시하고 있다
(이 외에 XBox에서는 VFetch 인스턴싱을 사용한다 )

1. 하드웨어 인스턴싱
 : VS_3_0이 필요하며 인스턴싱에 필요한 데이터를
세컨드 버텍스 버퍼에 저장하는 형식이다.

2. 셰이더 인스턴싱
 : 하드웨어인스턴싱이 불가능할 경우 사용하는 가장 효율적인 방식이다
인스턴싱 데이터는 시스템 메모리 배열에 저장된 후 렌더링시 버텍스 셰이더의
Constant에 카피 한다. VS_2_0일 경우 최대가 256개 vs_1_1에서는 최대 96개이다
그러므로 많은 수의 인스턴싱 데이터의 배열 전체를 한번에 처리하는것은 불가능하다

3. Contant 인스턴싱
  셰이더인스턴싱처리로 하지 않을 경우(배치사이즈=1)와 유사하다

4. 스트림 인스턴싱
 : 하드웨어 인스턴싱과 마찬가지로 세컨드 버텍스 버퍼를 사용한다
단, 버텍스 셰이더의 Constant를 설정해서 각 박스의 위치와 색을 갱신하는 것이 아닌
인스턴스 스트림 버퍼 내에 오프셋을 갱신해서 박스를 하나씩 렌더링한다



이 중에서 하드웨어 인스턴싱을 다룬다.. 
( 요샌 3.0 지원하는 카드 많으니까...ㅠㅠ 나중에 시간이 또 되면 셰이더 인스턴싱 설명도 넣어야지..-_-)


하드웨어 인스턴싱은 작업은 전적으로 GPU상에서 수행되며 CPU 사용량은 거의 없다

하드웨어 인스턴싱은 현재 데이터 구조를 변경하지 않는 대신
세컨드 버텍스 버퍼라고 불리는 또 다른 버텍스 버퍼를 추가한다



세컨드 버텍스 버퍼에는 변환행렬 정보등이 저장되어 있으며 인스턴스 하나당 하나씩 포함된다
또한 원래의 메인 버텍스 버퍼와는 다른 녀석이므로 버텍스버퍼 사이즈를 둘다 동일하게 유지할 필요도 없으며
한번에 DP Call로 얼마나 많은 인스턴싱을 할 것인가에 따라 최대 사이즈가 커지게 된다



소스 설명 주저리 주저리...

우선 메인 버텍스 버퍼와 세컨드 버텍스 버퍼에 들어갈 버텍스를 선언한다

 //메인 버텍스 버퍼에 들어갈 버텍스 선언

const D3DVERTEXELEMENT9 g_VBDecl_Geometry[] =
{
    {0,  0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
    {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,   0},
    {0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT,  0},
    {0, 36, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0},
    {0, 48, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
    D3DDECL_END()
};

//세컨드 버텍스 버퍼에 들어갈 버텍스 선언
const D3DVERTEXELEMENT9 g_VBDecl_InstanceData[] =
{
    {1, 0,  D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1},
    {1, 16, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2},
    {1, 32, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 3},
    {1, 48, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 4},
    {1, 64, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR,    0},
    D3DDECL_END()
};

첫번째 선언은 위치 법선, 탄젠트, 종법선, 및 텍스처 좌표 데이터들로 지오메트리 데이터 를 정의하며
2번째 선언은 오브젝트 인스턴스 별 데이터를 정의한다

2번째 선언에서를 보면 여기서는 각 인스턴스가 4개의 float4형 부동소수점 값 및 4성분 컬러에 의해 정의되고 있다.

처음의 4개의 값은 4x4 행렬의 초기화에 사용할 수 있으며 이 데이터는 각 지오메트리의 
인스턴스 사이즈, 위치, 방향등을 한번에 처리한다

렌더링 하기 전에 IDirect3DDevice9::SetStreamSourceFreq 를 호출하여 버텍스 버퍼의 스트림을 디바이스의 바인드해야 한다
바인드 하는 방법은 아래와 같다

//스트림 0으로 세팅 되어있는 메인 버텍스 버퍼 바인드

//g_NumBoxes 는 출력할 인스턴싱 데이터 개수
pd3dDevice->SetStreamSourceFreq( 0, D3DSTREAMSOURCE_INDEXEDDATA | g_NumBoxes );
pd3dDevice->SetStreamSource( 0, g_pVBBox, 0, sizeof( BOX_VERTEX ) );

//스트림 1로 세팅 되어있는 세컨드 버텍스 버퍼 바인드
//버텍스당 하나의 인스턴싱 데이터를 가지므로 1과 바인드한다
pd3dDevice->SetStreamSourceFreq( 1, D3DSTREAMSOURCE_INSTANCEDATA | 1ul );
pd3dDevice->SetStreamSource( 1, g_pVBInstanceData, 0, sizeof( BOX_INSTANCEDATA_POS ) );

여기서 BOX_VERTEX 는 기존의 버텍스 구조이며 는 BOX_INSTANCEDATA_POS 는 인스턴싱데이터를 가진 구조체이다

IDirect3DDevice9::SetStreamSourceFreq 는 D3DSTREAMSOURCE_INDEXEDDATA를 가지고
인덱스에 따른 지오메트리 데이터를 식별한다

우선 첫번째로 스트림0에 설정된 버텍스 버퍼(메인 버텍스 버퍼)를 바인드 하는데
위와 같이 인스턴싱 데이터 개수만큼 D3DSTREAMSOURCE_INDEXEDDATA와 or 연산하여 설정하면 
렌더링하는 지오메트리의 인스턴스값이 논리적으로 결합된다

다음 스트림1에 설정된 버텍스 버퍼(세컨드 버텍스 버퍼)를 바인드 하는데
이번에는 IDirect3DDevice9::SetStreamSourceFreq 가 D3DSTREAMSOURCE_INSTANCEDATA를 가지고 
인스턴싱 데이터를 포함한 스트림을 식별한다.
이때에 각 버텍스는 하나의 인스턴스 데이터를 포함하므로 D3DSTREAMSOURCE_INSTANCEDATA  1과 결합한다

그리고 IDirect3DDevice9::SetStreamSource를 호출하여 버텍스버퍼 포인터를 디바이스에 바인드 후 렌더링한다


렌더링 종료후...

인스턴싱 데이터 렌더링이 종료되면 버텍스 스트림의 Frequency를 원래대로 돌려주어야 한다

pd3dDevice->SetStreamSourceFreq( 0, 1 );

pd3dDevice->SetStreamSourceFreq( 1, 1 );


즉 렌더의 로직은 아래와 같다

 void OnRenderHWInstancing( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime )

{
    HRESULT hr;
    UINT iPass, cPasses;

    //버텍스 선언 세팅
    V( pd3dDevice->SetVertexDeclaration( g_pVertexDeclHardware ) );

    //스트림 0으로 세팅 되어있는 메인 버텍스 버퍼 바인드
    //g_NumBoxes 는 출력할 인스턴싱 데이터 개수
    V( pd3dDevice->SetStreamSourceFreq( 0, D3DSTREAMSOURCE_INDEXEDDATA | g_NumBoxes ) );
    V( pd3dDevice->SetStreamSource( 0, g_pVBBox, 0, sizeof( BOX_VERTEX ) ) );

    //스트림 1로 세팅 되어있는 세컨드 버텍스 버퍼 바인드 
    //버텍스당 하나의 인스턴싱 데이터를 가지므로 1과 바인드한다
    V( pd3dDevice->SetStreamSourceFreq( 1, D3DSTREAMSOURCE_INSTANCEDATA | 1ul ) );
    V( pd3dDevice->SetStreamSource( 1, g_pVBInstanceData, 0, sizeof( BOX_INSTANCEDATA_POS ) ) );

    //인덱스 버퍼 설정
    V( pd3dDevice->SetIndices( g_pIBBox ) );

    // 테크닉 설정
    V( g_pEffect->SetTechnique( g_HandleTechnique ) );

    V( g_pEffect->Begin( &cPasses, 0 ) );
    for( iPass = 0; iPass < cPasses; iPass++ )
    {
        V( g_pEffect->BeginPass( iPass ) );

        V( g_pEffect->SetTexture( g_HandleTexture, g_pBoxTexture ) );

        //BeginPass 와 EndPass 사이에서 호출 필요!
        V( g_pEffect->CommitChanges() );

        V( pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, 4 * 6, 0, 6 * 2 ) );

        V( g_pEffect->EndPass() );
    }


    V( g_pEffect->End() );

    //스트림 설정 원래대로
    V( pd3dDevice->SetStreamSourceFreq( 0, 1 ) );
    V( pd3dDevice->SetStreamSourceFreq( 1, 1 ) );
}


이 때 BeginPass와 EndPass 사이에 연속적으로 SetTexture를 하거나 할 경우
CommitChanges()를 부르지 않으면 이펙트에 바로 적용되지 않는다

BeginPass 와 EndPass 하수 특성상 EndPass가 실행되기 전 까지
다음 스테이트로 넘어가지 않고 계속 호출하게 되어 
제일 마지막에 설정한 부분에 모두 출력하게 된다.

CommitChanges()는 렌더링함수 (DrawSubSetDrawPrimitive 계열등)을 부르기 전에 호출해야 한다

또한 위에서도 설명했듯이 버텍스버퍼의 사이즈가 커지면 더 느려질 수도 있으니
노가다를 통해 적절한 버텍스버퍼의 크기를 찾는것이 중요하다-..-



반응형

'그래픽스(Graphics) > DirectX9~12' 카테고리의 다른 글

Effect::CommitChanges()  (0) 2013.01.25
모션 블러(Motion Blur)  (0) 2013.01.25
D3DXCreateTextureFromFile 함수  (0) 2013.01.22
다중스트림 셰이더  (0) 2013.01.21
D3DVERTEXELEMENT9 사용법  (0) 2013.01.21

+ Recent posts