본문 바로가기
서적 정리/DirectX11을 이용한 3D 게임 프로그래밍 입문

55.동적 정점 버퍼

by 민돌이2 2022. 2. 15.

정적 버퍼의 내용은 초기화 시점에서 고정되지만, 동적 버퍼의 내용은 실행 시점에서 얼마든지 변할 수 있다. 예를 들어 파도 시뮬레이션에서 필요할 것이다. 파도는 시간의 흐름에 따라 높이가 바뀔것이다. 시간은 프레임이 될 것이고 정해진 프레임마다 파도의 정점의 위치는 바꿔야 하낟. 바뀐 정점의 위치들을 반영하기 위해서는 동적 정점 버퍼가 필요하다.

버퍼를 동적으로 만들기 위해서 버퍼의 용도를 D3D11_USAGE_DYNAMIC으로 지정해야 한다. 또한 CPU에서 버퍼에 자료를 기록해야 하므로 CPU 접근 플래그를 반드시 D3D11_CPU_ACCESS_WRITE로 지정해야 한다.

D3D11_BUFFER_DESC desc;
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = sizeof(Vertex) * mWaves.VertexCount();
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
HR(md3dDevice->CreateBuffer(&buffer, 0, &mWavesBuffer);

 

동적 정점 버퍼를 갱신할 때는 ID3D11DeviceContext::Map 메소드를 호출해야 한다.

HRESULT ID3D11DeviceContext::Map(
	ID3D11Resource* pResource,
	UINT Subresource,
	D3D11_MAP MapType,
	UINT MapFlags,
	D3D11_MAPPED_SUBRESOURCE* pMappedResource
);

1.pResource : 접근을 원하는 자원을 가리키는 포인터
2.Subresource : 자원에 담긴 부분자원의 인덱스
3.MapType : 자원에 대한 CPU의 읽기 및 쓰기 권한을 지정(*주 1)
4.MapFlags : GPU가 사용 중일 때 CPU가 수행하는 작업을 지정하는 플래그
5.pMappedResource : 버퍼에 자료를 기록하거나 읽는 구조체를 가리키는 포인터 반환(*주 2)

 

버퍼를 갱신한 후에는 반드시 ID3D11DeviceContext::Unmap 메소드를 호출해야 한다.

void ID3D11DeviceContext::Unmap(ID3D11Resource* pResource, UINT Subresource);

1.pResource : 갱신한 자원을 가리키는 포인터

2.Subresource : 매핑 해체할 자원에 담긴 부분자원의 인덱스

 

아래의 코드는 정점 버퍼를 갱신하는 과정이다.

D3D11_MAPPED_SUBRESOURCE subResource;
Vertex* v = reinterpret_cast<Vertex*>(subResource.pData);

HR(md3dImmediateContext->Map(mWavesBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &subResource));
{
	for (UINT i = 0; i < mWaves.VertexCount(); ++i)
	{
		v[i].Pos = mWaves[i];
		v[i].Color = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
	}
}
md3dImmediateContext->Unmap(mWavesBuffer, 0);

 

동적 버퍼를 사용하면 새 자료를 CPU 메모리에서 GPU 메모리로 전송해야 하므로 추가 비용이 생긴다. 정적 버퍼로 가능한 일이라면 동적 버퍼보단 정적 버퍼를 사용하는 것이 바람직하다. Direct3D는 동적 버퍼의 필요성을 줄여주는 새로운 기능들이 추가되었다.

  1. 간단한 애니메이션은 정점 셰이더에서 수행할 수 있다.
  2. 텍스처로의 렌더링 기능 또는 계산 셰이더, 그리고 정점 텍스처 조회 기능을 사용하면 전적으로 GPU에서 실행하는 것이 가능하다.
  3. 기하 셰이더를 이용하면, 예전에는 CPU에서 수행해야 했던 기본도형의 생성 및 파괴 작업을 GPU에서 수행할 수 있다.

 

 

 

 

(*주 1)D3D11_MAP 열거형

CPU에서 읽고 쓰기 위해서 액세스할 리소스를 식별한다.

typedef enum D3D11_MAP
{
	D3D11_MAP_READ = 1,
	D3D11_MAP_WRITE = 2,
	D3D11_MAP_READ_WRITE = 3,
	D3D11_MAP_WRITE_DISCARD = 4,
	D3D11_MAP_WRITE_NO_OVERWRITE = 5
};

D3D11_MAP_READ : 자원이 읽기용으로 매핑된다. 읽기 액세스 권한으로 생성되어야 한다(D3D11_CPU_ACCESS_READ).
D3D11_MAP_WRITE : 자원이 쓰기용으로 매핑된다. 쓰기 액세스 권한으로 생성되어야 한다(D3D11_CPU_ACCESS_WRITE).
D3D11_MAP_READ_WRITE : 자원이 읽기 및 쓰기용으로 매핑된다. 읽기 및 쓰기 권한으로 생성되어야 한다.(D3D11_CPU_ACCESS_READ 및 D3D11_CPU_ACCESS_WRITE)
D3D11_MAP_WRITE_DISCARD : 자원이 쓰기용으로 매핑된다. 이전 내용은 정의되지 않는다. 쓰기 액세스 및 동적 사용으로 생성되어야 한다(D3D11_CPU_ACCESS_WRITE 및 D3D11_USAGE_DYNAMIC).
D3D11_MAP_WRITE_NO_OVERWRITE : 자원이 쓰기용으로 매핑된다. 기존 내용을 덮어쓸 수 없다. 정점 및 인덱스 버퍼에서만 유요하다. 쓰기 액세스 권한으로 생성되어야 한다(D3D11_CPU_ACCESS_WRITE). D3D11_BIND_CONSTANT_BUFFER 플래그로 생성된 자원에는 사용할 수 없다.

 

(*주 2)D3D11_MAPPED_SUBRESOURCE 구조체

하위 리소스 데이터에 대한 액세스를 제공한다.

typedef struct D3D11_MAPPED_SUBRESOURCE
{
	void *pData;
	UINT RowPitch;
	UINT DepthPitch;
} D3D11_MAPPED_SUBRESOURCE;

1.pData : 읽고 쓸 수 있는 자원의 메모리 블록을 가리키는 포인터. 자원에 담긴 자료에 맞는 형식으로 캐스팅할 필요가 있다.

2.RowPitch : 자원의 자료 한 줄의 byte 단위.

3.DepthPitch : 자원의 자표 한 페이지의 byte 단위

RowPitch와 DepthPitch의 차이는 쉽게 생각해서 3차원 배열의 행과 높이라고 생각하면 된다.

 

728x90

댓글