STUDY/DirectX11

DirectX11 3D - 3D 맵 지형 렌더링(Terrain) 높이맵(Height Map) 적용

최디디 2022. 3. 22. 02:29

Terrian(지형) 렌더링

지형 렌더링에서는 높이 맵을 사용 한다.

 

1. 높이 맵

높이 맵이란 지형의 높이를 나타내는 y값을 텍스쳐로 저장한 텍스쳐를 뜻한다.

Byte로 저장된 이미지는 0~255로 표현하는데,

그림판 RGB

그림판에서 흰색을 표현할 때 최댓값 255를 가지는 것을 

볼 수가 있다. 흰색일수록 높은 높이값을 갖는다는 의미다. 

Height Map

그렇다면 위 높이 맵을 보면, 가운데는 평지고,

바깥쪽은 높은 고지를 이룬다는 점을 미루어 짐작할 수 있겠다. 

하지만 이런 높이 맵은 지형의 기울기에 따라, 폴리곤의 증가나 감소가 어렵다.

이는 쿼트 트리, 옥트리, BSP 공간분할 기법을 이용해 LOD 같은 기술을

사용해 보완하기도 한다.

 

2. 텍스쳐 접근 및 제한

CPU나 GPU가 리소스에 접근할 수 있는지는 개발자가 직접 D3D11_USAGE

사용해서 지정해야 한다. 

typedef 
enum D3D11_USAGE
    {
        D3D11_USAGE_DEFAULT	= 0,
        D3D11_USAGE_IMMUTABLE	= 1,
        D3D11_USAGE_DYNAMIC	= 2,
        D3D11_USAGE_STAGING	= 3
    } 	D3D11_USAGE;

 

일반적으로 GPU가 접근할 수 있다. (D3D11_USAGE_DEFAULT)

높이 맵 텍스쳐를 읽어와서 높이 맵 정보를 토대로

높이 맵을 적용하기 위해(Vertex생성) CPU가 접근해야 한다.

 

리소스 사용 방법 DEFAULT DYNAMIC IMMUTABLE STAGING
GPU 읽기 가능 허용1 가능 허용 1,2
GPU 쓰기 허용1     허용 1,2
CPU 읽기       허용 1,2
CPU 쓰기   가능   허용 1,2

허용은 예외 사항을 갖고 있다는 뜻이다. 

 

CPU는 ID3D11DeviceContext::Map으로 접근할 수 있다.

GPU는 CopySubresourceResource나 CopyResource, UpdateSubresource로 접근 가능하다.

 

3. 높이 맵 적용

높이 맵 텍스쳐를 로드해 STAGING 권한을 갖고

ID3D11DeviceContext::Map API를 통해 높이 정보를 받아 오면 된다.

Map은 크리티컬 섹션처럼 공유 자원의 독점을 보장해준다.

Unmap 하기 전까지 아무도 접근을 못한다. 

 

HRESULT hr;
	ID3D11ShaderResourceView* pSRV = nullptr;
	wrl::ComPtr<ID3D11Resource> pTexture;
	size_t maxsize = 0;
	if (FAILED(hr = CreateWICTextureFromFileEx(g_pd3dDevice,
		heightmap.c_str(),
		maxsize, 
		D3D11_USAGE_STAGING,
		NULL,
		D3D11_CPU_ACCESS_WRITE|D3D11_CPU_ACCESS_READ,
		NULL,
		WIC_LOADER_DEFAULT,
		pTexture.GetAddressOf(), nullptr)))
	{
		return false;
	}

WICTextureLoader DXToolkit으로 텍스쳐를 로드한다.

CPU Access Flag를 D3D11_USAGE_STAGING

misc Flags를 D3D11_CPU_ACCESS_WRITE|D3D11_CPU_ACCESS_READ로

CPU가 접근해서 쓰고 읽기 가능하게 한다.

 

ID3D11Texture2D* pTexture2D = NULL;
	if (FAILED(pTexture->QueryInterface(__uuidof(ID3D11Texture2D), (LPVOID*)&pTexture2D)))
	{
		return false;
	}

그런 후에 인터페이스를 얻기 위해 사용하는 QueryInterface 함수를 사용한다.

위에서 얻는 텍스쳐 리소스를 ID3D11Texture2D* 포인터 변수에 가상 포인터를 넣어준다.

 

D3D11_TEXTURE2D_DESC desc;
	pTexture2D->GetDesc(&desc);

이제 이 pTexture2D 변수로 파일의 정보를 얻을 수 있다.

 

	m_HeightList.resize(desc.Height * desc.Width);

	if (pTexture2D)
	{
		D3D11_MAPPED_SUBRESOURCE MappedFaceDest;
		//크리티칼 섹션처럼 unmap 하기전까지 접근 못함
		if (SUCCEEDED(m_pContext->Map((ID3D11Resource*)pTexture2D, 
			D3D11CalcSubresource(0, 0, 1), D3D11_MAP_READ, 0, &MappedFaceDest)))
		{
			UCHAR* pTexels = (UCHAR*)MappedFaceDest.pData;
			PNCT_VERTEX	v;
			for (UINT row = 0; row < desc.Height; row++)
			{
				UINT rowStart = row * MappedFaceDest.RowPitch;
				for (UINT col = 0; col < desc.Width; col++)
				{
					UINT colStart = col * 4;
					UINT byte_height = pTexels[rowStart + colStart + 0];
					//byte에 저장할수있는 최대값은 0~255
					//따라서 높이를 조절하려면 나눗셈
					m_HeightList[row * desc.Width + col] = (static_cast<float>(byte_height)/8.0f)-4.0f;	/// DWORD이므로 pitch/4	
				}
			}
			m_pContext->Unmap(pTexture2D, D3D11CalcSubresource(0, 0, 1)); 
		}
	}

	m_num_row = desc.Height;
	m_num_col = desc.Width;

	if (pTexture2D) pTexture2D->Release();

 

HRESULT Map(
  [in]            ID3D11Resource           *pResource,//읽을 리소스 포인터
  [in]            UINT                     Subresource,//인덱스 번호
  [in]            D3D11_MAP                MapType,//읽기 쓰기 권한
  [in]            UINT                     MapFlags,//CPU 수행 작업 플래그
  [out, optional] D3D11_MAPPED_SUBRESOURCE *pMappedResource//깊이,크기,사이즈 조회 가능
);


Map함수를 호출하여 텍스쳐 정보에 액세스 할 수 있는

D3D11_MAPPED_SUBRESOURCE 포인터를 받아온다.

참고로 2번째 인자는 D3D11SubCalcResource 함수로 계산되는데

인덱스 번호를 지정하는 것으로 [리소스의 밉맵 레벨 X 리소스 번호 + 서브 리소스 번호]

로 지정이 된다.

UINT rowStart = row * MappedFaceDest.RowPitch;
for (UINT col = 0; col < desc.Width; col++)
{
	UINT colStart = col * 4;
	UINT byte_height = pTexels[rowStart + colStart + 0];
//byte에 저장할수있는 최대값은 0~255
//따라서 높이를 조절하려면 나눗셈
	m_HeightList[row * desc.Width + col] = (static_cast<float>(byte_height)/8.0f)-4.0f;
}

나머지는 파싱 해서 높이 값 정보 m_HeightList에 저장을 한다. 

오브젝트 위치에 y축 0에 맞추고 싶어서 적당히 나눠준 모습이다.

 

https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_mapped_subresource

 

D3D11_MAPPED_SUBRESOURCE (d3d11.h) - Win32 apps

Provides access to subresource data.

docs.microsoft.com

 

이제 버텍스를 찍는 함수에서 Y값을 높이맵 정보를 저장한 HeightLIst로 만들어 낸다. 

 

나머지는 저번에 노말 맵 적용하면서 퐁 셰이딩 적용했기 때문에, 

오브젝트와 마찬가지로 노말, 바이 노말, 탄젠트 값을 버텍스 정보에 넘겨주었다.

나중에 Flat Shading, Gouraud Shading, Phong Shading에 대해서도 정리해야겠다.

 

4. 결과