DirectX11 - DirectX 초기화

Sungsik Bae·2024년 1월 21일

DirectX11

목록 보기
3/6
post-thumbnail

DirectX 초기화

DirectX11-SDK 튜토리얼를 활용하여 공부 중이다.

Window Rect 가져오기

HRESULT InitDevice()
{
	...
	RECT rc;
	GetClientRect(g_hWnd, &rc);
	const UINT width = rc.right - rc.left;
	const UINT height = rc.bottom - rc.top;
    ...
}

Window의 Width, Height값을 가져옵니다.
그 때 사용하는 함수가 바로 GetClientRect 이다.

Right로 갈수록 x값이 늘어나고, Bottom으로 갈수록 y값이 늘어난다고 보면 되는듯


DirectX Device Flag 설정

	UINT createDeviceFlags = 0;
#ifdef _DEBUG
	createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

DirectX Device를 생성할 때의 플래그 설정


DirectX를 초기화 하기 위해서는 Driver랑 Context를 만드는 게 시작이라고 볼 수 있다.

DirectX DriverType

D3D_DRIVER_TYPE driverTypes[] =
{
	D3D_DRIVER_TYPE_HARDWARE,
	D3D_DRIVER_TYPE_WARP,
	D3D_DRIVER_TYPE_REFERENCE,
};
constexpr UINT numDriverTypes = ARRAYSIZE(driverTypes);

D3D_DRIVER_TYPE을 열거형으로 해서 0,1,2 순서대로 찾아보고 Device를 생성할 때 사용함.

  • Hardware : 하드웨어 드라이버
  • Warp : 고속 퍼포먼스 소프트웨어 레스터라이저
  • Reference : 레퍼런스 레스터라이저

레스터라이저라는 용어가 무슨 뜻인지 모르겠어서 찾아보았다.

정확한 뜻은 모르겠지만 하드웨어가 돌아가지 못할 때, Software상에서 렌더링을 돌려주는 역할을 하는 거 같다.
[참고자료1] [참고자료2]


DirectX Feature Level

	D3D_FEATURE_LEVEL featureLevels[] =
	{
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
	};
	constexpr UINT numFeatureLevels = ARRAYSIZE(featureLevels);

이 부분은 DirectX의 버전을 정해주는 단계이다.
이 또한 위와 같이 0,1,2 index순서대로 찾아보고 결정된다.


DirectX Swapchain 설정

	DXGI_SWAP_CHAIN_DESC sd;
	ZeroMemory(&sd, sizeof(DXGI_SWAP_CHAIN_DESC));
	sd.BufferCount = 1;
	sd.BufferDesc.Width = width;
	sd.BufferDesc.Height = height;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = g_hWnd;
	sd.SampleDesc.Count = 1;
	sd.SampleDesc.Quality = 0;
	sd.Windowed = TRUE;

이 부분은 Swapchain 구조체에 대한 정보를 설정하는 단계이다. [공식문서]

Swapchain이 뭐냐하면은 화면을 그릴 때, front buffer와 back buffer 두 가지 buffer를 생성해두고, 두 buffer를 계속 바꿔주면서 화면에 출력하는 방법을 의미한다.

Swapchain 구조체를 알아보자 (대충 보자마자 알 거 같은 것을 제외하고)

  • BufferCount : Back buffer 개수

  • BufferDesc : display mode

    • Format : display format
    • RefreshRate : 화면 주사율(지금은 1/60)
    • BufferUsage : 버퍼의 사용 용도(지금은 출력용)
    • OutputWindow : 출력될 윈도우
    • SampleDesc : 멀티 샘플링 설정(안티 앨리어싱)
      • Count : 픽셀 당 얼마나 샘플링할지
        • Quality : 멀티 샘플링 퀄리티

    BufferCount가 1로 설정되어 있는데 하나의 백 버퍼를 사용한다는 뜻
    SampleDesc의 Count = 1 Quality = 0으로 설정되어 있으니 멀티 샘플링이 되지 않는다는 것을 의미한다.


    DirectX Device & Swapchain 생성

    		for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++)
    		{
    			g_driverType = driverTypes[driverTypeIndex];
    			hr = D3D11CreateDeviceAndSwapChain(
    				nullptr,						// device를 생성하기 위한 video adapter(nullptr = default adapter)
    				g_driverType,					// driver type
    				nullptr,						// software handle
    				createDeviceFlags,				// device flag
    				featureLevels,					// feature level array
    				numFeatureLevels,				// feature level array length
    				D3D11_SDK_VERSION,				// sdk version
    				&sd,							// swap chain structure
    				&g_pSwapChain,					// 생성된 swap chain 
    				&g_pDevice,						// 생성된 device
    				&g_featureLevel,				// 생성된 feature level
    				&g_pImmediateContext);			// 생성된 device context
    
    			if (SUCCEEDED(hr))
    			{
    				break;
    			}
    		}

지정된 driver type들을 바탕으로 device와 swapchain이 성공적으로 생성될 때까지 순차적으로 반복한다.


Back buffer 설정

ID3D11Texture2D* pBackBuffer = nullptr;
	hr = g_pSwapChain->GetBuffer(
		0,													// 백버퍼 index
		__uuidof(ID3D11Texture2D),							// back buffer를 다루기 위한 interface
		reinterpret_cast<LPVOID*>(&pBackBuffer)				// back buffer interface output
	);
if (FAILED(hr))
{
	return hr;
}

[공식문서]
Swap chain으로부터 첫번째 buffer를 받아온다.


Render target view 생성

Render target view(RTV)는 뭘까?
그 이전에 DirectX에서 View라는 것은 무엇인가?

MVP패턴에서 사용되던 View라는 의미인가?
그건 당연히 아니였다.

DirectX에서는 렌더링 파이프라인를 통해 그려진다.
렌더링에 리소스들을 아래의 View를 통해 파이프라인에 Bind한다. (이를 통해 읽거나 쓸 수 있음)

여러가지 리소스 View들이 존재한다.

  • Renter target view
  • Depth stencil view
  • Shader resource view
  • Unordered access view

그 중 지금 사용할 View는 RTV이다.
RTV는 2차원 Texture를 파이프라인으로 부터 받을 때 주로 사용된다.

hr = g_pDevice->CreateRenderTargetView(
	pBackBuffer,			// View에서 접근할 리소스
	nullptr,				// RTV 정의
	&g_pRenterTargetView);	// RTV를 받아올 변수
pBackBuffer->Release();
if (FAILED(hr))
{
	return hr;
}

Back buffer를 Release를 한 이유는, RTV를 생성했으므로 그 이후로는 back buffer에 직접 접근하지 않고 RTV를 사용하기 때문임


Render target view 설정

	g_pImmediateContext->OMSetRenderTargets(
		1,						// Render target의 개수(최대 8개)
		&g_pRenterTargetView,	// Render target view의 배열
		nullptr					// Depth stencil view 포인터
	);

OM(output-merger-stage) : 픽셀 셰이더에서 출력된 값을 Render target에 작성하는 스테이지이다.


Viewport 설정

	D3D11_VIEWPORT vp;
	vp.Width = static_cast<FLOAT>(width);		
	vp.Height = static_cast<FLOAT>(height);
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	g_pImmediateContext->RSSetViewports(
    1,		// 설정할 Viewport 개수
    &vp		// Viewport 구조체 배열
    );

Viewport는 Renter target에서 그려질 영역을 의미한다.
RS(rasterizer-stage) : Geometry 셰이더로부터 출력된 primitive data를 RS에 보내서 Rasterizer에 의해 렌더링 할 픽셀 단위로 분해됨.

레스터라이징 과정 중 하나인 Viewport에 따라 그려질 영역이 설정된다.


Render

	// RTV를 Clear할 색상 설정
	constexpr float clearColor[4] = { 0.0f, 0.125f,0.3f,1.0f };
	g_pImmediateContext->ClearRenderTargetView(g_pRenterTargetView, clearColor);
    // 화면에 출력하기
	g_pSwapChain->Present(0, 0);

ClearRenderTargetView : Render target를 RGBA color로 초기화
Present : back buffer에 그려진 이미지를 출력


Release

	if (g_pImmediateContext)
	{
    	// Resets any device context to the default settings
		g_pImmediateContext->ClearState();
	}
	if (g_pRenterTargetView)
	{
		g_pRenterTargetView->Release();
	}
	if (g_pSwapChain)
	{
		g_pSwapChain->Release();
	}
	if (g_pImmediateContext)
	{
		g_pImmediateContext->Release();
	}
	if (g_pDevice)
	{
		g_pDevice->Release();
	}

사용한 DirectX 객체들을 해제한다.


전체 코드

#include <Windows.h>
#include <d3d11.h>

//--------------------------------------------------------------------------------------
// Global Variables
//--------------------------------------------------------------------------------------
HINSTANCE g_hInstance;
HWND g_hWnd;
D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL;
D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
ID3D11Device* g_pDevice = nullptr;
ID3D11DeviceContext* g_pImmediateContext = nullptr;
IDXGISwapChain* g_pSwapChain = nullptr;
ID3D11RenderTargetView* g_pRenterTargetView = nullptr;


//--------------------------------------------------------------------------------------
// Forward declarations
//--------------------------------------------------------------------------------------
HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow);
HRESULT InitDevice();
void CleanupDevice();
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Render();

//--------------------------------------------------------------------------------------
// Entry point to the program. Initializes everything and goes into a message processing 
// loop. Idle time is used to render the scene.
//--------------------------------------------------------------------------------------
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
	if (FAILED(InitWindow(hInstance, nCmdShow)))
	{
		return 0;
	}

	if (FAILED(InitDevice()))
	{
		CleanupDevice();
		return 0;
	}

	MSG msg = { nullptr };
	while (WM_QUIT != msg.message)
	{
		if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			Render();
		}
	}

	CleanupDevice();

	return static_cast<int>(msg.wParam);
}

HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow)
{
	// Register Window class
	WNDCLASSEX wcex;
	ZeroMemory(&wcex, sizeof(wcex));

	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
	wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wcex.hbrBackground = reinterpret_cast<HBRUSH>((COLOR_WINDOW + 1));
	wcex.lpszMenuName = nullptr;
	wcex.lpszClassName = L"TutorialWindowClass";
	wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);

	if (!RegisterClassEx(&wcex))
	{
		return E_FAIL;
	}

	g_hInstance = hInstance;
	RECT rc = { 0, 0, 640, 480 };
	AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
	g_hWnd = CreateWindowEx(NULL,
		L"TutorialWindowClass",
		L"Direct3D 11 Tutorial 1: Direct3D 11 Basics",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		rc.right - rc.left,
		rc.bottom - rc.top,
		nullptr,
		nullptr,
		hInstance,
		nullptr);

	if (g_hWnd == nullptr)
	{
		return E_FAIL;
	}

	ShowWindow(g_hWnd, nCmdShow);

	return S_OK;
}

HRESULT InitDevice()
{
	HRESULT hr = S_OK;

	RECT rc;
	GetClientRect(g_hWnd, &rc);
	const UINT width = rc.right - rc.left;
	const UINT height = rc.bottom - rc.top;

	UINT createDeviceFlags = 0;
#ifdef _DEBUG
	createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	D3D_DRIVER_TYPE driverTypes[] =
	{
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE,
	};
	constexpr UINT numDriverTypes = ARRAYSIZE(driverTypes);

	D3D_FEATURE_LEVEL featureLevels[] =
	{
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
	};
	constexpr UINT numFeatureLevels = ARRAYSIZE(featureLevels);

	DXGI_SWAP_CHAIN_DESC sd;
	ZeroMemory(&sd, sizeof(DXGI_SWAP_CHAIN_DESC));
	sd.BufferCount = 1;
	sd.BufferDesc.Width = width;
	sd.BufferDesc.Height = height;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = g_hWnd;
	sd.SampleDesc.Count = 1;
	sd.SampleDesc.Quality = 0;
	sd.Windowed = TRUE;

	for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++)
	{
		g_driverType = driverTypes[driverTypeIndex];
		hr = D3D11CreateDeviceAndSwapChain(
			nullptr,						// device를 생성하기 위한 video adapter(nullptr = default adapter)
			g_driverType,					// driver type
			nullptr,						// software handle
			createDeviceFlags,				// device flag
			featureLevels,					// feature level array
			numFeatureLevels,				// feature level array length
			D3D11_SDK_VERSION,				// sdk version
			&sd,							// swap chain structure
			&g_pSwapChain,					// 생성된 swap chain 
			&g_pDevice,						// 생성된 device
			&g_featureLevel,				// 생성된 feature level
			&g_pImmediateContext);			// 생성된 device context

		if (SUCCEEDED(hr))
		{
			break;
		}
	}

	if (FAILED(hr))
	{
		return hr;
	}

	ID3D11Texture2D* pBackBuffer = nullptr;
	hr = g_pSwapChain->GetBuffer(
		0,													// 백버퍼 index
		__uuidof(ID3D11Texture2D),							// back buffer를 다루기 위한 interface
		reinterpret_cast<LPVOID*>(&pBackBuffer)				// back buffer interface output
	);
	if (FAILED(hr))
	{
		return hr;
	}

	hr = g_pDevice->CreateRenderTargetView(
		pBackBuffer,			// View에서 접근할 리소스
		nullptr,				// RTV 정의
		&g_pRenterTargetView);	// RTV를 받아올 변수

	// 사용한 back buffer를 
	pBackBuffer->Release();
	if (FAILED(hr))
	{
		return hr;
	}

	g_pImmediateContext->OMSetRenderTargets(
		1,						// Render target의 개수(최대 8개)
		&g_pRenterTargetView,	// Render target view의 배열
		nullptr					// Depth stencil view 포인터
	);

	D3D11_VIEWPORT vp;
	vp.Width = static_cast<FLOAT>(width);
	vp.Height = static_cast<FLOAT>(height);
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	g_pImmediateContext->RSSetViewports(1, &vp);

	return S_OK;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;

	switch (uMsg)
	{
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}

	return 0;
}

void Render()
{
	constexpr float clearColor[4] = { 0.0f, 0.125f,0.3f,1.0f };
	g_pImmediateContext->ClearRenderTargetView(g_pRenterTargetView, clearColor);
	g_pSwapChain->Present(0, 0);
}

void CleanupDevice()
{
	if (g_pImmediateContext)
	{
		g_pImmediateContext->ClearState();
	}
	if (g_pRenterTargetView)
	{
		g_pRenterTargetView->Release();
	}
	if (g_pSwapChain)
	{
		g_pSwapChain->Release();
	}
	if (g_pImmediateContext)
	{
		g_pImmediateContext->Release();
	}
	if (g_pDevice)
	{
		g_pDevice->Release();
	}
}
profile
Game Developer

0개의 댓글