<DirectX11> 초기화 (기본 틀 마련?)

MwG·2024년 11월 12일

DirectX11

목록 보기
3/7

Device, Immediate Context, Swap Chain

DX11을 사용하기 위해 기본적으로 세 가지 오브젝트가 필요한데.
Device, Immediate Context, Swap Chain이다.

1. ID3D11Device (Device)

3D 그래픽 카드와 연결되는 기본 디바이스 객체이다.

ID3D11DeviceContext (레스터라이제이션 파이프라인 객체)(Immediate Context)

DX11에선 디바이스 객체에 직접적으로 접근하지 않고 , Context객체를 사용하여 디바이스에 명령을 내린다. 또한 멀티코어/멀티 스레드를 최적화하기 위하여 Device의 하위에 context를 분리하여 사용한다.

IDXGISwapChain(swap chain)

화면에 렌더링할 백버퍼를 관리하며 실제로 화면에 렌더링하는 역할도 담당한다. 더블 버퍼와 같이 프론트 버퍼와 백버퍼를 번갈아가며(스왑) 화면에 렌더링한다.

초기화순서

  1. ID3D11Device 와 SwapChain을 생성한다.
  2. 백버퍼에 실제 렌더링할 '렌더 타겟 뷰'를 생성
  3. 뷰포트를 생성한다.
  4. 매 프레임마다 위에서 생성한 렌더 타겟 뷰에 게임화면을 렌더링한다.
  5. SwapChain을 이용하여 디바이스에 화면을 그린다.

RenderTargetView

이 렌더타겟뷰가 실제로 렌더링할 렌더타겟(Frame Buffer)에 바인딩되어 렌더링을 진행하게 됩니다.
렌더타겟뷰는 리소스뷰의 일종으로 디바이스의 백버퍼에 바운딩되면서 생성됩니다. 그래서 렌더 타겟뷰에 게임화면을 렌더링 한다는 것은 백버퍼에 렌더링 하는 것과 같습니다. 그리고 백버퍼에 모든 화면을
렌더링 하고 난 다음에는 SwapChain을 이용해서 백버퍼를 화면에 그려줍니다.
정확히는 백버퍼를 프론트 버퍼와 바꾸는 스와핑이 일어나겠죠.
SwapChain 생성 -> RenderTarget (백버퍼 또는 FrameBuffer)를 꺼내오고, 꺼내온 백버퍼를 가지고 렌더타겟뷰라는 오브젝트에 바운딩 합니다.

그리고 실제 내부에서는 SwapChain이 백버퍼라는 리소스를 가지고 있고, 개발자는 그 백버퍼를 렌더 타겟뷰에 바인딩하여 사용합니다.

초기화 과정 코드

1.디바이스 생성

  UINT DeviceFlag = D3D11_CREATE_DEVICE_DEBUG;
        D3D_FEATURE_LEVEL FeatureLevel = (D3D_FEATURE_LEVEL)0;

        ID3D11Device* pDevice = nullptr;
        HRESULT hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr
                                        , DeviceFlag, nullptr, 0
                                        , D3D11_SDK_VERSION
                                        , mDevice.GetAddressOf()
                                        , &FeatureLevel
                                        , mContext.GetAddressOf());

이때 여기서 쓰는 피쳐레벨이란?
피처레벨은 GPU가 서포트 하는 기능셋의 엄밀한 정의로 기본적으로는 상위의 피처레벨은 하위의 피처레벨의 기능을 포함하고 있다.

여러 레벨이 존재하고 각 레벨에 따른 차이가 있다.

2.Swap Chain 생성

bool GraphicsDevice_DX11::CreateSwapChain(const SwapChainDesc* desc, HWND hWnd, SwapChain* swapchain)
    {
        DXGI_SWAP_CHAIN_DESC dxgiDesc = {};

        dxgiDesc.OutputWindow = hWnd;// 버퍼를 출력할 윈도우
        dxgiDesc.Windowed = true;// 윈도우, 전체화면 모드
        dxgiDesc.BufferCount = desc->buffer_count; //백버퍼 개수
        dxgiDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;// 이전 프레임 장면을 유지하지 않는다.

        dxgiDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; //백버퍼를 어떤 용도로사용할 것인지..
        dxgiDesc.BufferDesc.Width = desc->width; //백버퍼 가로 세로
        dxgiDesc.BufferDesc.Height = desc->height;
        dxgiDesc.BufferDesc.Format = (DXGI_FORMAT)desc->format; //백버퍼 포맷 : RGBA 8 비트이며 값의 범위0.0~1.0
        dxgiDesc.BufferDesc.RefreshRate.Numerator = 144; //화면의 주사율 
        dxgiDesc.BufferDesc.RefreshRate.Denominator = 1;
        dxgiDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
        dxgiDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;

        dxgiDesc.SampleDesc.Count = 1; //멀티 샘플링 수
        dxgiDesc.SampleDesc.Quality = 0; //멀티 샘플링 퀄리티

        Microsoft::WRL::ComPtr<IDXGIDevice> pDXGIDevice = nullptr;
        Microsoft::WRL::ComPtr<IDXGIAdapter> pAdapter = nullptr;
        Microsoft::WRL::ComPtr<IDXGIFactory> pFactory = nullptr;

        if (FAILED(mDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)pDXGIDevice.GetAddressOf())))
            return false;

        if (FAILED(pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), (void**)pAdapter.GetAddressOf())))
            return false;

        if (FAILED(pAdapter->GetParent(__uuidof(IDXGIFactory), (void**)pFactory.GetAddressOf())))
            return false;

        if (FAILED(pFactory->CreateSwapChain(mDevice.Get(), &dxgiDesc, mSwapChain.GetAddressOf())))
            return false;

        return true;
    }

3. swap chain 으로 부터 FrameBuffer 가져오기

// Get render target by Swapchain
        hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)mFrameBuffer.GetAddressOf());

디바이스, 디바이스 컨텍스트, 스왑체인을 생성하였지만
스왑체인의 백버퍼는 Direct3D의 렌더타겟에 설정되어 있지 않다.
그래서 스왑체인으로부터 백버퍼를 취하여 디바이스의 렌더타겟으로 설정해야 한다.
IDXGISwapChain::GetBuffer 함수로 백버퍼의 포인터를 얻어 낼수 있다.

4. Render Target View 생성

// Create Rendertarget view
        hr = mDevice->CreateRenderTargetView(mFrameBuffer.Get(), nullptr, mRenderTargetView.GetAddressOf());

텍스처는 파이프라인으로부터 뷰를 통하여 액세스 할수 있으며
렌더타겟에는 렌더타겟 뷰를 사용한다 따라서 백버퍼를 얻어왔으면 해당 백버퍼에 접근할수 있게 렌더 타겟 뷰를 생성한다
렌더타겟 뷰는 D3D11_RENDER_TARGET_VIEW_DESC 구조체로 설정하는데 디폴트 설정으로는 NULL을 넘긴다

5. 깊이 버퍼 생성과 깊이버퍼 뷰 생성

// Create Depth Stencil Bufferif 
if(FAILED(mDevice->CreateTexture2D(&dxgiDesc, nullptr, mDepthStencilBuffer.GetAddressOf())))
            return false;

// Create Depth Stencil Buffer Viewif 
if (FAILED(mDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), nullptr, mDepthStencilView.GetAddressOf())))
            return false;

깊이/스텐실 버퍼 설정
3D 그래픽스 기능을 사용하는 어플리케이션을 만들때 대부분은 보이지 않는 부분을 숨기는 컬링 을 수행하는데에
깊이 버퍼(Z버퍼, 뎁스 버퍼)를 사용한다.
3D 그래픽스의 컬링
3D를 올바르게 렌더링하려면 앞쪽에 있는 물체가 뒤에 있는 물체를 가릴수있는 처리가 필요한데
이 처리에는 Z 소팅 방식과 Z 버퍼 기법이 있다.
Z-Sort

Z-bufffer

Z 소트 기법은 간단하고 심플하지만 물체끼리가 교차하는 전후관계를 간단하게 결정하기 어려운 물체가있을때 제대로 렌더링되지 않는 다는 점과 소팅하는데 시간이 걸린다라는 단점이 있다.

Z 버퍼 기법은 픽셀 단위로 거리를 기록해두기 위한 [ 깊이 버퍼 ]가 필요하지만 전후관계를 픽셀단위로 판단할 수 있어 일반적으로는 Z 버퍼 기법을 사용하지만 렌더링하는 물체중간의 반투명인 물체가 있으면 깊이 버퍼만으로는 제대로 렌더링할수 없다.
이경우에는 불투명 물체만을 먼저 렌더링해두고 반투명 물체를 뒤에서 부터 소팅하여 렌더링한다 (알파 소팅)

이 외에 여러가지 효과를 표현하는데 쓰이는 스텐실 버퍼를 사용하는 경우도 있다
DirectX 11에 깊이 버퍼와 스텐실 버퍼는 1개의 리소스를 공유하므로 프로그램에서는 깊이/스텐실 버퍼 라고 묶어서 취급한다
또 깊이/스텐실 버퍼는 백버퍼나 렌더가능한 텍스처 등인 렌더 타겟 과조합해서 사용한다
렌더타겟은 파이프라인에서 최대 8개까지 설정 할수 있지만 깊이/스텐실 버퍼는 하나만을 설정 가능하다.

DirectX 11 에서 깊이/스텐실 버퍼는 텍스처 리소스의 한종류이다.
따라서 깊이/스텐실 텍스처로써의 텍스처 구조체를 생성한다.
예시

// Create depth stencil texture

D3D11_TEXTURE2D_DESC descDepth;
ZeroMemory( &descDepth, sizeof(descDepth) );                   //초기화
descDepth.Width = width;
descDepth.Height = height;
descDepth.MipLevels = 1;                                                   //밉맵 레벨 개수
descDepth.ArraySize = 1;                                                   // 배열 사이즈
descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; // 포맷 (깊이버퍼 24bit, 스텐실 8 bit)
descDepth.SampleDesc.Count = 1;                                      //멀티 샘플링 수
descDepth.SampleDesc.Quality = 0;                                    //멀티 샘플링 퀄리티
descDepth.Usage = D3D11_USAGE_DEFAULT;                      //디폴트 사용법
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;          //깊이/스텐실 버퍼로써 사용
descDepth.CPUAccessFlags = 0;                                          //CPU로부터는 액세스 하지 않음
descDepth.MiscFlags = 0;                                                   //그 외 설정 없음

hr = g_pd3dDevice->CreateTexture2D( &descDepth, NULL, &g_pDepthStencil ); //텍스처 생성

위에서 생성한 텍스처는 텍스처 리소스의 한 종류 이므로 파이프라인에 설정하려면
일반 텍스처와 마찬가지로 뷰 를 사용한다
예시

ID3D11DepthStencilView* g_pDepthStencilView = NULL;


// Create the depth stencil view
D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
ZeroMemory( &descDSV, sizeof(descDSV) );
descDSV.Format = descDepth.Format;                                      //깊이/스텐실텍스처에서 사용한 포맷 그대로 사용
descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;          //2D 텍스처
descDSV.Texture2D.MipSlice = 0;
hr = g_pd3dDevice->CreateDepthStencilView( g_pDepthStencil, &descDSV, &g_pDepthStencilView );

6. RenderTargetView 와 DepthStencilView 설정

mContext->OMSetRenderTargets(1, mRenderTargetView.GetAddressOf(), mDepthStencilView.Get());

깊이/스텐실 뷰를 설정했으면 렌더타겟 뷰와 깊이스텐실 뷰를 파이프라인의 Output Merger 스테이지에 설정한다

7. 렌더링

모니터에 그린물체를 그려주기전에 화면을 지워주는 역할도 진행한다.

void GraphicsDevice_DX11::Draw()
 {
     FLOAT backgroundColor[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
				mContext->ClearRenderTargetView(mRenderTargetView.Get(), backgroundColor);
				mContext->ClearDepthStencilView(mDepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.f, 0);
				
				// draw object
				
     mSwapChain->Present(1, 0);
 }

위 소스에서는 깊이값만을 클리어 하고 있으며 스텐실 값도 클리어 하고 싶을때는
D3D11_CLEAR_STENCIL플래그를 OR 연산하여 플래그 인수로 넘겨준다
클리어 처리는 뷰 (렌더타겟뷰,깊이/스텐실뷰)를 대상으로 수행되며, Output Merger 스테이지에 렌더타겟으로써 등록한 버퍼가 클리어 되는 것은 아니다.
렌더링 처리가 끝나면 스왑체인의 있는 백버퍼에 렌더링 처리를 수행한 후 렌더링 결과를 화면에 표시하려면
IDXGISwapChain::Present 함수를 호출한다

<출처>
얌얌코딩 수업노트

0개의 댓글