Tutorial2_버텍스버퍼


<사진출저: 한국콘텐츠 진흥원>



/-----------------------------------------------------------------------------

// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{

  
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc,

                      0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      "D3D Tutorial", NULL };
    RegisterClassEx( &wc );
 

    HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 02: Vertices",
                              WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
                              GetDesktopWindow(), NULL, wc.hInstance, NULL );


    if( SUCCEEDED( InitD3D( hWnd ) ) )
    {

        // Create the vertex buffer
        // 정점 버퍼 초기화

        if( SUCCEEDED( InitVB() ) )
        {

            // Show the window
            ShowWindow( hWnd, SW_SHOWDEFAULT );
            UpdateWindow( hWnd );

            // Enter the message loop
            MSG msg;
            ZeroMemory( &msg, sizeof(msg) );
            while( msg.message!=WM_QUIT )
            {

                   // 메시지큐에 메시지가 있으면 메시지 처리
                   if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
                   {
                       TranslateMessage( &msg );
                       DispatchMessage( &msg );
                   }
                   else
                   {
                       
//처리할 메시지가 없으면 Render() 함수 호출
                       Render();
                   }
             }
        }
    }

 // 등록된 클래스 소거
    UnregisterClass( "D3D Tutorial", wc.hInstance );
    return 0;
}


Render()함수는 후면버퍼에 그린 화면을 전면버퍼로 보내주는 함수이다. 위에서 보면 알수있듯이특이하게 메시지루프안에서 화면을 그려주고 있다. 이는 게임 혹은 3D 프로그램의 특성상 사용자의 시각적인 면을 고려한 것으로 메시지대기중 여유분의 CPU를 활용하여 보다 빠르고 자연스럽게 화면에 보여주기 위한 처리방법이다.
 

* 정점버퍼를 초기화 하는 부분의 InitVB()함수를 살펴보자.

함수를 살펴보기 전에 먼저 정점 버퍼의 구조체 선언을 보기로 하자.

 

// A structure for our custom vertex type
struct CUSTOMVERTEX
{

     // The transformed position for the vertex
     // 정점의 변환된 좌표(rhw 값이 있으면 변환이 완료된 정점이다.)

    FLOAT x, y, z, rhw; 
    DWORD color; 
       // The vertex color (정점의 색깔)
};

// Our custom FVF, which describes our custom vertex structure
// 사용자 정점 구조체에 관한 정보를 나타내는 FVF 값
// 구조체는 X, Y, Z, RHW 값과 Diffuse(흩뿌리는, 퍼지는) 색깔 값으로 이루어져 
// 있음을 알 수 있다.

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)

 

구조체와 함께 반듯이 플래그도 선언해 주어야만 한다.
여기서 중요한것은 구조체의 선언시 
들어간 항목 그대로를 플래그에서도 똑같이 선언해주어야 한다는 것이다.

 

//-----------------------------------------------------------------------------
// Name: InitVB()
// 정점 버퍼를 생성하고 정점값을 채워넣는다.
// 정점 버퍼란 기본적으로 정점 정보를 갖고 있는 메모리 블록이다.
// 정점 버퍼를 생성한 다음에는 반드시 Lock()과 Unlock()으로 포인터를 얻어내서
// 정점 정보를 정점 버퍼에 써넣어야 한다.
// D3D는 인덱스 버퍼도 사용 가능하다.
// 정점 버퍼나 인덱스 버퍼는 기본 시스템 메모리 외에 디바이스 메모리(비디오카드 메모리)
// 에 생성될 수 있는데, 대부분의 비디오카드에서는 이렇게 할 경우 엄청난 속도의 향상을 
// 얻을 수 있다.
//-----------------------------------------------------------------------------

HRESULT InitVB()
{

    // Initialize three vertices for rendering a triangle
 // 삼각형을 렌더링하기 위해 3개의 정점 선언

    CUSTOMVERTEX vertices[] =
    {
        { 150.0f,  50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color
        { 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },
        {  50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, },
    };
 

    // 정점 버퍼를 생성한다.
    // 3개의 사용자 정점을 보관할 메모리를 할당한다.
    // FVF(Flexible Vertex Format)를 지정하여 보관할 데이터 형식을 지정한다.

    if( FAILED( g_pd3dDevice->CreateVertexBuffer( 3*sizeof(CUSTOMVERTEX),
                                                  0, D3DFVF_CUSTOMVERTEX,
                                                  D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
    {
        return E_FAIL;
    }


    // 정점 버퍼를 값으로 채운다.
    // 정점 버퍼의 Lock() 함수를 호출하여 포인터를 얻어온다.

    VOID* pVertices;
    if( FAILED( g_pVB->Lock( 0, sizeof(vertices), (void**)&pVertices, 0 ) ) )
        return E_FAIL;

    memcpy( pVertices, vertices, sizeof(vertices) );

    g_pVB->Unlock();

    return S_OK;
}

 

실제 정점의 값을 할당하는 부분을 살펴보자.

CUSTOMVERTEX vertices[] =
{
    { 150.0f,  50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color
    { 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },
    {  50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, },
};

 

3차원 공간의 x, y, z값과 rhw값, color을 할당해주고 있다. 
color의 경우 0xAARRGGBB의 순서대로 값을 주는데, A는 Alpha값으로 0xff~0xff의 값을 갖는다. R, G, B는 Red, Green, Blue의 값으로0x00~0xff의 값을 갖는다.

 

 

CreateVertexBuffer()함수 설명

 

HRESULT CreateVertexBuffer

(      
    UINT Length,
    DWORD Usage,
    DWORD FVF,
    D3DPOOL Pool,
    IDirect3DVertexBuffer9** ppVertexBuffer,
    HANDLE* pHandle
);

 

파라메타 설명

 

Length : 생성할 정점 버퍼의 바이트 단위 크기

 

Usage : 정점 버퍼의 종류 혹은 처리 방식(SW, HW)지정

 

FVF : 버퍼내의 정점의 정점 포맷을 기술하는 사용 방법의 지정자. 이 파라미터를 유효한 FVF 코

         드로 설정 하면, 생성 되는 정점 버퍼는 FVF 정점 버퍼가 된다

 

Pool : 정점 버퍼가 저장될 메모리의 위치(비디오카드, 시스템 메모리)와 관리 방식 지정(열거형)

 

ppVertexBuffer : 반환될 정점 버퍼의 인터페이스의 포인터 주소.

 

pHandle : 예약이 끝난 상태. 이 파라미터는 NULL 로 설정한다.

 
 

위 소스의 경우 3개의 정점 처리 버퍼를 비디오카드에 생성하여 g_pVB에 보관한다.

CreateVertexBuffer() 로 생성된 버퍼는 쓰레기 값으로 가득차 있기 떄문에 값을 넣어줘야함.

CreateVertexBuffer() 로 생성한 점점버퍼는 정확히 말해서 정점버퍼의 인터페이스이다.

그러기 위해서 Lock()을 사용해서 메모리 포인터를 얻어내야함!!!!!!!!!!!!!!!!!

Lock() 으로 얻어진 포인터는 일반 메모리처럼 마음대로 사용가능함!!!!!! 오호


 

HRESULT Lock(      
    UINT OffsetToLock,
    UINT SizeToLock,
    VOID **ppbData,
    DWORD Flags
);

 

파라미터 설명

 

OffsetToLock  : 잠그는 정점 데이터에의 오프셋(offset) (바이트 단위). 정점 버퍼 전체를 잠그려

                          면 ,SizeToLock 와 OffsetToLock 의 양쪽 모두의 파라미터에 을 지정한

                          다.

 

SizeToLock  : 잠그는 정점 데이터의 사이즈 (바이트 단위). 정점 버퍼 전체를 잠그려면,

                       SizeToLock 와 OffsetToLock 의 양쪽 모두의 파라미터에 0 을 지정한다.

 

ppbData  : 돌려주어진 정점 데이터를 포함한 메모리 버퍼에의 VOID* 포인터. (읽고 쓸 수 있게

                  된 메모리 영역의 포인터)

 

Flags  : 실행하는 잠금의 종류를 기술하는, 0 개 이상의 잠금 플래그의 편성. 이 메서드에 사용할

              수 있는 플래그는 아래와 같다.(Lock을 수행할 때 함께 사용하는 플래그) 

    - D3DLOCK_DISCARD

    - D3DLOCK_NO_DIRTY_UPDATE

    - D3DLOCK_NO_SYSLOCK

    - D3DLOCK_READONLY

    - D3DLOCK_NOOVERWRITE

 

#define설명
D3DLOCK_DISCARD

애플리케이션은, 잠금 되고 있는 영역내의 모든 장소를, 쓰기

전용 처리로 덧쓰기한다. 이것은 동적 텍스처, 동적인 정점 버

퍼, 및 동적인 인덱스 버퍼를 사용할 때에 유효한 옵션이다.

정점 버퍼 및 인덱스 버퍼의 경우, 애플리케이션은 버퍼 전체

를 파기한다. 새로운 메모리 영역의 포인터가 돌려주어지므로,

다이나믹 메모리 액세스 (DMA)와 낡은 영역으로부터의 렌더링

이 기능 정지할 것은 없다.

텍스처에 대해서는, 애플리케이션은 잠금 되고 있는 영역내의

모든 장소를 쓰기 전용 처리로 덧쓰기한다.

 

D3DLOCK_DONOTWAIT

드라이버가 즉시 표면을 잠글 수 없는 경우에, 애플리케이션이

CPU 사이클을 다시 취득할 수가 있다. 이 플래그가 설정되어

는 경우, 드라이버가 즉시 표면을 잠글 수 없을 때는, 잠금 호

출은 D3DERRR_WASSTILLDRAWING 를 돌려준다. 이 플래그는,

IDirect3DDevice9::CreateOffscreenPlainSurface,

IDirect3DDevice9::CreateRenderTarget , 또는

IDirect3DDevice9::CreateDepthStencilSurface

사용해 생성한 표면의 LockRect 를 호출하는 경우에만 사용

할 수 있다. 이 플래그는 백 버퍼와 함께 사용할 수도 있다.

 

D3DLOCK_NO_DIRTY_UPDATE

디폴트에서는, 리소스의 잠금은 더티 영역을 그 리소스에 추

가한다. 이 옵션을 지정 하면, 리소스의 더티 상태가 변경되

지 않게 된다. 애플리케이션에서는, 잠금 처리동안으로 변경

되는 것 영역 세트에 대한 추가 정보를 가지고 있을 때는, 이

옵션을 사용할 필요가 있다.

 

D3DLOCK_NOOVERWRITE

애플리케이션이 정점 버퍼 및 인덱스 버퍼내의 데이터를 덧

쓰기하지 않는 것을 보증한다. 이 플래그를 지정 하면, 드라

이버가 즉석에서 종료해, 이 버퍼를 사용해 렌더링을 속행할

수 있다. 이 플래그를 사용하지 않는 경우, 드라이버는 잠금

으로부터 돌아오기 전에 렌더링을 종료할 필요가 있다.

 

D3DLOCK_NOSYSLOCK

비디오 메모리 잠금의 디폴트의 동작은, 시스템의 크리티컬

섹션을 확보하는 것으로, 잠금중에 디스플레이 모드의 변경

을 하지 않는 것을 보증한다. 이 옵션은, 시스템 와이드인 크

리티컬 섹션이 잠금의 사이 보관 유지되지 않게 한다.

락 처리는 시간이 걸리지만, 마우스 커서의 이동 등, 시스템

으로 다른 처리를 실행하는 것이 가능하게 된다. 이 옵션은,

소프트웨어 렌더링의 백 버퍼의 잠금과 같이, 잠금이 장시간

에 이르러, 시스템의 응답성에 악영향을 주어 버리는 것 같

은 잠금에 대해서 유효하다.

 

D3DLOCK_READONLY

애플리케이션은 버퍼에 기입하지 않는다. 이것에 의해, 비네

이티브 포맷으로 저장 되고 있는 리소스는, 언락시에 재압축

스텝을 생략 할 수 있다.

 



CreateVertexBuffer()
로 생성한 정점 버퍼는 인터페이스이며, 이 인터페이스의 Lock()이라는 함수를 사용하여 실제 정점을 읽고 쓸 수 있는 메모리 포인터를 얻어낸다. 얻어진 ppbData포인터는 일반 메모리처럼 마음대로 사용할 수 있다.

 

memcpy( pVertices, vertices, sizeof(vertices) );
g_pVB->Unlock();

 

Lock()을 사용한 정점 버퍼는 반드시 Unlock()을 호출해서 해제해주어야 한다.





추가적으로 버택스버퍼와 메모리 관계에 대해서~
 

정점버퍼(Vertex Buffer)는 D3D에서 가장 중요한 개념중의 하나이며, 하드웨어 가속을 위해서는 반드시 사용해야 하는것


일단 정점버퍼란 정점을 모아 두는 일종의 메모리라 보면된다!

이 메모리가 단순간 배열이나 new, malloc() 들의 의한 메모리와 구별되는것은

정점 처리만을 위해 만들어진 특수한 메모리이기떄문에 훨씬 효율적이라는 것!


정점버퍼는 크게 2가지의 메모리를 사용한다. 비디오 메모리시스템 메모리


비디오메모리: 비디오카드의 GPU(Grapghic Processing Unit)에 의해서 정점 쉐이더, T&L등의 강력한 하드웨어 가속을 사용할 수 있지만,비디오 카드 자체의 메모리 용량을 벗어날 수 는 없음


시스템 메모리: T&L 등의 강력한 하드웨어 가속은 사용할 수 없지만, 풀부한 용량으로 상대적으로 많은 정점버퍼를 관리할 수 있음.


*질문: 정점은 정점버퍼에만 관리가능한가??

-> NO 배열을 사용해서 직접 정점을 관리하는것도 가능하다! 하지만 웬만하면 정점버퍼씀 

이 글을 공유하기

댓글

Designed by JB FACTORY