DX11 공부를 어느정도 한바퀴돈것 같아 이후 다시 처음부터 다시 돌아보아 내가 첫번째 학습때 이해하지 못했던것들을 두번째 학습때는 제대로 이해하고 넘어가도록 할려고한다
이 강의를 통해 DX를 공부하였다
https://www.inflearn.com/course/directx11-%EA%B2%8C%EC%9E%84%EA%B0%9C%EB%B0%9C-%EB%8F%84%EC%95%BD%EB%B0%98
#pragma once
class Game
{
public:
Game();
~Game();
public:
void Init(HWND hwnd);
void Update();
void Render();
private:
void RenderBegin();
void RenderEnd();
private:
void CreateDeviceAndSwapChain();
void CreateRenderTargetView();
void SetViewPort();
private:
HWND _hwnd;
uint32 _width = 0;
uint32 _height = 0;
private:
//create
ComPtr<ID3D11Device>_device = nullptr;
//랜더링파이프라인 연결
ComPtr<ID3D11DeviceContext>_deviceContext = nullptr;
ComPtr<IDXGISwapChain> _swapChain = nullptr;
//RTV
ComPtr<ID3D11RenderTargetView> _renderTargetView;
//MISC
//화면의 크기 묘사
D3D11_VIEWPORT _viewport = { 0 };
float _clearColor[4] = { 0.f};
};
Com(Component Object Model)은 DirectX의 프로그래밍 언어 독립성과 하위호환성을 가능하게 하는 기술이다. c++ 클래스로 간주하고 사용해도 무방하다고 한다.
COM은 New 나 delete 가 아닌 AddRef()와 Release()를 호출하여 사용하나
코드가 길어지면 어디서 Release()를 해주어야하는 지 혼란이 올거다. 마치 raw pointer처럼..
그래서 ComPtr이라는 클래스가 존재한다.
ComPtr 클래스는 #include <wrl.h> 가 필요하며 COM객체를 위한 스마트포인터라고 할수있다.
Get
COM인터페이스를 가리키는 포인터를 돌려준다
GetAddressOf
COM인터페이스를 가리키는 포인터의 주소를 돌려준다
ID3D11Device는 주로 렌더링에 필요한 리소스를 생성 및 렌더링 상태를 관리하는 역할을 한다.
ID3D11DeviceContext는 GPU에 렌더링 명령 전달, 렌더링 파이프라인을 제어하는 역할을 한다.
SwapChain은 프레임 버퍼를 생성 및 관리를 하는역할인데
만약 버퍼가 하나라고 한다면 이미 화면에 보이고 있는 그림과 그다음 화면에 보여지기 위해 그려지고 있는 그림으로 섞여 굉장히 난잡해진다. 이를 해결하기위해 화면에 보여지기위한 전면 버퍼와
그다음 화면에 보여지기 위해 그려지고 있는 후면 버퍼가 존재하게된다. 후면버퍼가 다그려져 전면버퍼와 교체를 해줄 필요가 생기는데이를 위함이 IDXGISwapChain이다.
RenderTargetView: 렌더 타겟 뷰(Render Target View)는 GPU가 그림을 출력할 최종 버퍼(백 버퍼)를 설정하는 역할을 한다
#include "pch.h"
#include "Game.h"
Game::Game()
{
}
Game::~Game()
{
}
void Game::Init(HWND hwnd)
{
_hwnd = hwnd; //윈도우 handle
_width = GWinSizeX;//800
_height = GWinSizeY; //600
CreateDeviceAndSwapChain();
CreateRenderTargetView();
SetViewPort();
}
void Game::Update()
{
}
void Game::Render()
{
RenderBegin();
//TODO
RenderEnd();
}
void Game::RenderBegin()
{
_deviceContext->OMSetRenderTargets(1, _renderTargetView.GetAddressOf(),nullptr);
_deviceContext->ClearRenderTargetView(_renderTargetView.Get(), _clearColor);
_deviceContext->RSSetViewports(1, &_viewport);
}
void Game::RenderEnd()
{
HRESULT hr= _swapChain->Present(1, 0);
CHECK(hr);
}
void Game::CreateDeviceAndSwapChain()
{
DXGI_SWAP_CHAIN_DESC desc;
ZeroMemory(&desc, sizeof(desc));
{
desc.BufferDesc.Width = _width; //픽셀단위로 정보채우기
desc.BufferDesc.Height = _height;
desc.BufferDesc.RefreshRate.Numerator = 60;
desc.BufferDesc.RefreshRate.Denominator = 1;
desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = 1;
desc.OutputWindow = _hwnd;
desc.Windowed = true;
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
}
HRESULT hr = ::D3D11CreateDeviceAndSwapChain
(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
0,
nullptr,
0,
D3D11_SDK_VERSION,
&desc,
_swapChain.GetAddressOf(),
_device.GetAddressOf(),
nullptr,
_deviceContext.GetAddressOf()
);
CHECK(hr);
}
void Game::CreateRenderTargetView()
{
HRESULT hr;
ComPtr<ID3D11Texture2D> backBuffer = nullptr;
hr = _swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuffer.GetAddressOf());
CHECK(hr);
_device->CreateRenderTargetView(backBuffer.Get(), nullptr, _renderTargetView.GetAddressOf());
CHECK(hr);
}
void Game::SetViewPort()
{
_viewport.TopLeftX = 0.f;
_viewport.TopLeftY = 0.f;
_viewport.Width = static_cast<float>(_width);
_viewport.Height = static_cast<float>(_height);
_viewport.MinDepth = 0.f; //최소깊이 0
_viewport.MaxDepth = 1.f; //최대 깊이 1
}
void Game::CreateDeviceAndSwapChain()
{
DXGI_SWAP_CHAIN_DESC desc;
ZeroMemory(&desc, sizeof(desc));
{
desc.BufferDesc.Width = _width; //픽셀단위로 정보채우기
desc.BufferDesc.Height = _height;
desc.BufferDesc.RefreshRate.Numerator = 60;
desc.BufferDesc.RefreshRate.Denominator = 1;
desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = 1;
desc.OutputWindow = _hwnd;
desc.Windowed = true;
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
}
HRESULT hr = ::D3D11CreateDeviceAndSwapChain
(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
0,
nullptr,
0,
D3D11_SDK_VERSION,
&desc,
_swapChain.GetAddressOf(),
_device.GetAddressOf(),
nullptr,
_deviceContext.GetAddressOf()
);
CHECK(hr);
}
//
DXGI_SWAP_CHAIN_DESC desc;
ZeroMemory(&desc, sizeof(desc));
{
desc.BufferDesc.Width = _width; //픽셀단위로 정보채우기
desc.BufferDesc.Height = _height;
desc.BufferDesc.RefreshRate.Numerator = 60;
desc.BufferDesc.RefreshRate.Denominator = 1;
desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = 1;
desc.OutputWindow = _hwnd;
desc.Windowed = true;
desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
}
BufferDesc :이 구조체는 생성하고자 하는 후면 버퍼의 속성들을 서술한다.
SampleDesc : 다중표본화 표본 개수와 품질수준을 서술하는 구조체이다.
BufferUsage: 후면 버퍼에 렌더링 할것이므로 DXGI_USAGE_RENDER_TARGET_OUTPUT을 설정해준다.
BufferCount: SwapChain 이 사용할 버퍼개수이다.
OutputWindow 는 렌더링 결과가 표시될 창의 handle이다
Windowed : 창모드 할건지 안할건지 true, false 값으로 받는다
SwapEffect:디스플레이 화면에서 픽셀을 처리하는 옵션으로
DXGI_SWAP_EFFECT_DISCARD로 설정해주었다.
추가로 DXGI_SWAP_EFFECT_DISCARD는 DX12에서는 지원이 안되어 DX12에서는
DXGI_SWAP_EFFECT_FLIP_DISCARD로 사용하여야한다
HRESULT hr = ::D3D11CreateDeviceAndSwapChain
(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
0,
nullptr,
0,
D3D11_SDK_VERSION,
&desc,
_swapChain.GetAddressOf(),
_device.GetAddressOf(),
nullptr,
_deviceContext.GetAddressOf()
);
CHECK(hr);
}
이 부분은 이전에 한번 다룬적이있다. DirectX 11 api 에서 그래픽장치를 생성하고 이를 통해 응용프로그램이 화면에 이미지를 랜더링 할 수 있도록 swap chain을 생성하는데 사용된다는 점만 알아두도록 하겠다
https://velog.io/@hallow0312/DirectX-11-D3D11CreateDeviceAndSwapChain
{
HRESULT hr;
ComPtr backBuffer = nullptr;
hr = _swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuffer.GetAddressOf());
CHECK(hr);
_device->CreateRenderTargetView(backBuffer.Get(), nullptr, _renderTargetView.GetAddressOf());
CHECK(hr);
}
이 함수의 역할은 swapchain에서 backbuffer를 가져오고, backbuffer를 사용하여 RTV를 생성한다.
이후 이제 backbuffer에 그림을 그릴수있게된다.
void Game::RenderBegin()
{
_deviceContext->OMSetRenderTargets(1, _renderTargetView.GetAddressOf(),nullptr);
_deviceContext->ClearRenderTargetView(_renderTargetView.Get(), _clearColor);
_deviceContext->RSSetViewports(1, &_viewport);
}
void Game::RenderEnd()
{
HRESULT hr= _swapChain->Present(1, 0);
CHECK(hr);
}
RenderBegin
GPU가 그림을 그릴 백 버퍼(RTV)를 설정
이전 프레임의 화면을 _clearColor로 지워 초기화
GPU가 그릴 영역(뷰포트) 설정
RenderEnd
BackBuffer에서 다음 프레임에 보여질 이미지가 다그려지면 Present를 사용하여 전면버퍼와 후면버퍼를 서로 바꾸는 역할을 해준다