56일차. DirectX11_2D시작, DirectX11_2D[Graphics]
서론
DirectX11을 구성하기 위해 Framework 와 UnitTest로 두개의 프로젝트로 나누어 관리한다.
Framework는 각 라이브러리를 가져와서 연동시키는 역활을 하고,
UnitTest는 연동된 라이브러리를 활용하는 직접적인 알고리즘 부분을 맡게 된다.
[ 위와같은 방법으로 사용하지 않고 자유롭게 구축하여도 무방하다. ]
DirectX 라이브러리 연동하기
이와같이 두개의 프로젝트를 구성하여, Framework 의 프로젝트를 UnitTest에 연동시키기 위해, VS에서는 다음과 같은 설정을 진행한다.
위 항목에서 포함 디렉터리, 라이브러리 디렉터리 를 수정한다.
상속된 값, 에 해당하는 기록들도 같이 복사 붙여넣기 해준다.
위 경로는, DirectX경로의 Include 를 프로젝트 폴더에 복사 붙여넣기 한 경로이다.
만약, 절대경로로써 로컬디스크 C 에 있는 DirextX를 사용하고 싶다면, 위 경로를 찾아가서 설정해도 좋다.
디버그 폴더 관리하기
그리고, 프로젝트를 컴파일 했을 때 나오는 디버그 폴더를 관리하기 위해, 출력 디렉터리와 중간 디렉터리를 다음과 같이 수정해준다.
DIrextX 의 Graphics에 대한 설명을 작성한다.
그래픽 작업을 다루는 클레스이며, 싱글톤 베이스 로 작업한다.
싱글톤 베이스는 겹쳐지는 것을 피하기 위해 한개만 사용할때 사용한다.
// 그래픽들의 값을 전부 초기화 해주는것.
// 인자값을 전부 초기화 해야한다.
class D3DEnumAdapterInfo;
class D3DEnumOutputInfo;
class Graphics : public SingletonBase<Graphics>
{
// 싱글톤 베이스 = 한개가 존재해야 하는것은 한개만 넣는것.
public:
friend class SingletonBase<Graphics>;
// private에 있는 것을 사용하기 위해 firend 선언해준다.
ID3D11Device* GetDevice() { return device;}
ID3D11DeviceContext* GetDC() { return deviceContext; }
void Resize(const UINT& width, const UINT& height);
// 창 사이즈 바꾸기
void SetViewport(const UINT& width, const UINT& height);
// Viewport = 우리가 보고있는 화면.
void Begin();
void End();
// 생성자와 소멸자의 내용을 줄여서 만든다.
void GUI();
// 그래픽 유저 인터페이스.
// 다른 부가정보를 띄워주기 위해 사용
private:
void CreateSwapchain();
// 우리가 화면을 그려줄떄 뒤쪽에 버퍼를 생성해준다.
// 그리고 화면을 두개 띄워서 교차하면서 표시해준다.
void CreateRenderTargetView();
//
void DeleteSurface();
private:
Graphics();
~Graphics();
private:
void EnumerateAdapters();
bool EnumerateAdapterOutput(D3DEnumAdapterInfo* adapterInfo);
private:
ID3D11Device* device = nullptr;
ID3D11DeviceContext* deviceContext = nullptr;
IDXGISwapChain* swapChain = nullptr;
ID3D11RenderTargetView* rtv = nullptr;
D3D11_VIEWPORT viewport;
D3DXCOLOR clearColor = 0xff555566;
UINT numerator = 0;
UINT denominator = 1;
UINT gpuMemorySize;
wstring gpuName;
vector<D3DEnumAdapterInfo*> adapterInfos;
int selectedAdapterIndex = 0;
bool bVsync = true;
};
class D3DEnumAdapterInfo
// 그래픽카드
{
public:
~D3DEnumAdapterInfo();
UINT adapterOrdinal = 0;
IDXGIAdapter1* adapter = nullptr;
DXGI_ADAPTER_DESC1 adapterDesc = { 0 };
D3DEnumOutputInfo* outputInfo = nullptr;
};
class D3DEnumOutputInfo
// 모니터
{
public:
~D3DEnumOutputInfo();
IDXGIOutput* output = nullptr;
DXGI_OUTPUT_DESC outputDesc = { 0 };
UINT numerator = 0;
UINT denominator = 1;
};
#include "Framework.h"
#include "Graphics.h"
// 스왑체인을 새로 조정하고, 렌더 타겟 뷰와 뷰포트를 업데이트
void Graphics::Resize(const UINT& width, const UINT& height)
{
// 기존에 생성된 RTV 제거
DeleteSurface();
// 버퍼 크기 변경
{
HRESULT hr = swapChain->ResizeBuffers
(
0, // 백 버퍼의 수 (0은 기존과 동일하게)
width, // 백 버퍼의 너비
height, // 백 버퍼의 높이
DXGI_FORMAT_UNKNOWN, // 백 버퍼의 포맷 (기존과 동일하게 유지)
0 // 백 버퍼의 속성 (기존과 동일하게 유지)
);
assert(SUCCEEDED(hr)); // 리사이즈 실패한 경우 프로그램 종료
}
CreateRenderTargetView(); // 새로운 백 버퍼에 대한 RTV 생성
SetViewport(width, height); // 뷰포트를 새로운 크기에 맞게 설정
}
// 뷰포트 설정
void Graphics::SetViewport(const UINT& width, const UINT& height)
{
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
viewport.Width = (float)width;
viewport.Height = (float)height;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
}
// 렌더링을 시작하기 전 필요한 초기화 작업을 수행
void Graphics::Begin()
{
// RTV 설정
deviceContext->OMSetRenderTargets(1, &rtv, nullptr);
// 뷰포트 설정
deviceContext->RSSetViewports(1, &viewport);
// RTV를 지우고 clearColor로 색상을 채움
deviceContext->ClearRenderTargetView(rtv, clearColor);
}
// 렌더링을 끝내고 화면에 결과를 출력
void Graphics::End()
{
// 백 버퍼를 출력하고, 화면 갱신에 대해 대기할지 여부를 bVsync로 전달
HRESULT hr = swapChain->Present(bVsync == true ? 1 : 0, 0);
assert(SUCCEEDED(hr));
}
// Gui 구현
void Graphics::GUI()
{
static bool bOpen = true;
ImGui::SetNextWindowPos({ 0, 15 });
ImGui::SetNextWindowSize(ImVec2(200, 30)); // 위젯 창 크기 설정
ImGui::Begin
(
"Vstnc",
&bOpen,
ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoScrollbar
);
{
ImGui::Checkbox("##Vsync", &bVsync);
}
ImGui::End();
}
// 스왑 체인을 생성
void Graphics::CreateSwapchain()
{
// 이전에 할당된 메모리 해제
{
SAFE_RELEASE(device);
SAFE_RELEASE(deviceContext);
SAFE_RELEASE(swapChain);
}
// 구조체 초기화
DXGI_SWAP_CHAIN_DESC desc;
ZeroMemory(&desc, sizeof(DXGI_SWAP_CHAIN_DESC));
// 너비와 높이 설정
desc.BufferDesc.Width = 0;
desc.BufferDesc.Height = 0;
// 수직 동기화를 사용할 경우 프레임 레이트 설정
{
if (bVsync)
{
desc.BufferDesc.RefreshRate.Numerator = adapterInfos[0]->outputInfo->numerator;
desc.BufferDesc.RefreshRate.Denominator = adapterInfos[0]->outputInfo->denominator;
}
else
{
desc.BufferDesc.RefreshRate.Numerator = 0;
desc.BufferDesc.RefreshRate.Denominator = 1;
}
}
// 버퍼의 쓰임새에 대해서 정의
{
// 버퍼의 색상 형식 설정
desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
// 스케일링과 스캔라인 순서
desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
// 스왑 체인의 버퍼 개수, 용도, MSAA 품질 설정
desc.BufferCount = 1;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
// 출력 창 핸들, 창 모드, 스왑 체인 효과 설정
desc.OutputWindow = handle;
desc.Windowed = true;
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
}
// 사용 가능한 D3D 기능 수준을 나타내는 D3D_FEATURE_LEVEL 배열 생성
vector<D3D_FEATURE_LEVEL> featureLevel
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
};
// 가장 VRAM이 큰 어댑터를 선택하여 selectedAdapterIndex에 할당
UINT maxVRam = 0;
for (UINT i = 0; i < adapterInfos.size(); i++)
{
if (adapterInfos[i]->adapterDesc.DedicatedVideoMemory > maxVRam)
{
selectedAdapterIndex = i;
maxVRam = adapterInfos[i]->adapterDesc.DedicatedVideoMemory;
}
}
// 디바이스와 스왑 체인 생성
HRESULT hr = D3D11CreateDeviceAndSwapChain
(
adapterInfos[selectedAdapterIndex]->adapter, // 최대 VRAM 용량을 가진 그래픽 장치 선택
D3D_DRIVER_TYPE_UNKNOWN, // 드라이버 타입 (자동)
nullptr, // 소프트웨어 렌더러 사용 안함
0, // 렌더링 시 필요한 플래그 설정
featureLevel.data(), // 사용할 Direct3D 기능 레벨
featureLevel.size(), // 사용할 Direct3D 기능 레벨의 개수
D3D11_SDK_VERSION, // DirectX SDK 버전
&desc, // 스왑 체인을 생성할 때 사용할 구조체
&swapChain, // 생성된 스왑 체인 객체 반환
&device, // 생성된 디바이스 객체 반환
nullptr, // 사용하지 않음
&deviceContext // 생성된 디바이스 컨텍스트 객체를 반환
);
assert(SUCCEEDED(hr));
// 사이즈 재설정
Resize(WinMaxWidth, WinMaxHeight);
// cmd 출력
{
for (int i = 0; i < adapterInfos.size(); i++)
{
gpuName = adapterInfos[i]->adapterDesc.Description;
wcout << "GPU Name : " << adapterInfos[i]->adapterDesc.Description << endl;
cout << "VRAM : " << adapterInfos[i]->adapterDesc.DedicatedVideoMemory << endl;
cout << endl;
}
wcout << "Selected GPU Name : "
<< adapterInfos[selectedAdapterIndex]->
adapterDesc.Description << endl;
}
}
// 스왑체인에서 백 버퍼를 가져와 RTV 생성
void Graphics::CreateRenderTargetView()
{
// 백버퍼 가져오기
ID3D11Texture2D* backbuffer = nullptr;
HRESULT hr = swapChain->GetBuffer
(
0,
__uuidof(ID3D11Texture2D),
(void**)&backbuffer
);
assert(SUCCEEDED(hr));
// 백 버퍼로 RTV 생성
hr = device->CreateRenderTargetView
(
backbuffer,
nullptr,
&rtv
);
assert(SUCCEEDED(hr));
// 백 버퍼 해제
SAFE_RELEASE(backbuffer);
}
// RTV 해제
void Graphics::DeleteSurface()
{
SAFE_RELEASE(rtv);
}
// 어댑터를 검색하고 스왑체인 생성
Graphics::Graphics()
{
EnumerateAdapters();
CreateSwapchain();
}
// 생성한 자원을 해제
Graphics::~Graphics()
{
SAFE_RELEASE(rtv);
SAFE_RELEASE(swapChain);
SAFE_RELEASE(deviceContext);
SAFE_RELEASE(device);
}
// 그래픽 어댑터를 찾아 정보를 저장
void Graphics::EnumerateAdapters()
{
// DXGI 객체를 생성하는 함수를 구현한 인터페이스
IDXGIFactory1* factory;
// 팩토리 생성
if (FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory)))
// 실패시 종료
return;
UINT index = 0;
while (true)
{
IDXGIAdapter1* adapter = nullptr;
// 어댑터를 나열
HRESULT hr = factory->EnumAdapters1(index, &adapter);
// 더 이상 어댑터가 없으면 종료
if (hr == DXGI_ERROR_NOT_FOUND)
break;
// 나열에 실패하면 프로그램 종료
assert(SUCCEEDED(hr));
D3DEnumAdapterInfo* adapterInfo = new D3DEnumAdapterInfo();
ZeroMemory(adapterInfo, sizeof(D3DEnumAdapterInfo));
// 어댑터의 인덱스 저장
adapterInfo->adapterOrdinal = index;
// 어댑터의 정보 저장
adapter->GetDesc1(&adapterInfo->adapterDesc);
// 어댑터 정보 저장
adapterInfo->adapter = adapter;
// 어댑터의 출력 장치 나열
EnumerateAdapterOutput(adapterInfo);
// 어댑터 정보 저장
adapterInfos.push_back(adapterInfo);
// 다음 어댑터로 이동
index++;
}
// 팩토리 해제
SAFE_RELEASE(factory);
}
// 어댑터에서 지원하는 출력 모드를 가져오는 역할, 출력 빈도를 저장
bool Graphics::EnumerateAdapterOutput(D3DEnumAdapterInfo* adapterInfo)
{
IDXGIOutput* output = nullptr;
// 어댑터의 출력 장치 나열
HRESULT hr = adapterInfo->adapter->EnumOutputs(0, &output);
// 출력 장치가 없으면 함수 종료
if (DXGI_ERROR_NOT_FOUND == hr)
return false;
// 나열에 실패하면 프로그램 종료
assert(SUCCEEDED(hr));
D3DEnumOutputInfo* outputInfo = new D3DEnumOutputInfo();
ZeroMemory(outputInfo, sizeof(D3DEnumOutputInfo));
output->GetDesc(&outputInfo->outputDesc); // 출력 장치의 정보 저장
outputInfo->output = output; // 출력 장치 객체 저장
UINT numModes = 0;
DXGI_MODE_DESC* displayModes = nullptr;
DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM;
// 출력 장치의 디스플레이 모드 개수 확인
hr = output->GetDisplayModeList(format, DXGI_ENUM_MODES_INTERLACED, &numModes, nullptr);
assert(SUCCEEDED(hr));
displayModes = new DXGI_MODE_DESC[numModes];
// 출력 장치의 디스플레이 모드 정보 저장
hr = output->GetDisplayModeList(format, DXGI_ENUM_MODES_INTERLACED, &numModes, displayModes);
assert(SUCCEEDED(hr));
for (UINT i = 0; i < numModes; i++)
{
bool bCheck = true;
bCheck &= displayModes[i].Width == WinMaxWidth; // 디스플레이 모드의 너비가 최대 너비와 같은지 확인
bCheck &= displayModes[i].Height == WinMaxHeight; // 디스플레이 모드의 높이가 최대 높이와 같은지 확인
if (bCheck == true)
{
// 디스플레이 모드의 리프레시 비율 저장
outputInfo->numerator = displayModes[i].RefreshRate.Numerator;
outputInfo->denominator = displayModes[i].RefreshRate.Denominator;
}
}
// 출력 장치 정보 저장
adapterInfo->outputInfo = outputInfo;
SAFE_DELETE_ARRAY(displayModes);
return true;
}
D3DEnumAdapterInfo::~D3DEnumAdapterInfo()
{
SAFE_RELEASE(adapter);
SAFE_DELETE(outputInfo);
}
D3DEnumOutputInfo::~D3DEnumOutputInfo()
{
SAFE_RELEASE(output);
}
함수
Graphics::GetDevice()
- 현재 DIrectX장치를 반환하는 함수이다.
Graphics::GetDC()
- 현재의 DirectX 장치 컨텍스트를 반환하는 함수 이다.
Graphics::Resize( ... )
- 현재 출력되는 창의 크기를 변경하는 함수이다. 전달된 너비와 높이에 따라 렌더타겟을 재생성하고 크기를 조정한다.
Graphics::SetViewport( ... )
- viewport의 크기를 설정해주는 함수로, 화면에 그려질 영역을 지정해주는 역활을 한다.
Graphics::Begin()
- 렌더링의 루프를 시작하는 함수로, 시작전 값을 초기값으로 초기화해주는 작업을 한다.
Graphics::End()
- 렌더링 루프의 종료를 나타내는 함수로, 렌더링 이후 정리작업을 수행한다.
Graphics::GUI()
- 그래픽 사용자 인터페이스를 처리하는 함수이다. 다른부가정보를 표시한다.
Graphics::CreateSwapchain()
- Swap Chain을 생성하는 함수로, 화면에 그릴후, 그리기전의 버퍼를 생성하고 두개의 화면을 교차하며 표시한다.
Graphics::CreateRenderTargetView()
- Render TargetView를 생성하는 함수. SwapChain의 뷰포트 를 지정해준다.
Graphics::DeleteSurface()
- Surface를 삭제하는 함수. 생성된 SwapChain과 Render Target View와 관련된 자원을 정리한다.
Graphics::EnumerateAdapters()
- 사용가능한 그래픽 어댑터를 열거하는 함수이다. 시스템 설치 그래픽카드를 확인하고 어뎁터 정보를 수집한다.
Graphics::EnumerateAdapterOutput( ... )
- 그래픽 어댑터의 출력(모니터)를 열거하는 함수로 모니터의 정보를 수집한다.
나머지는 소멸자이다.