3 분 소요

GUI 크기 변환에 따른 ViewPort 크기 바꾸기 1

D3D11_VIEWPORT m_screenViewport;

// Set the viewport
ZeroMemory(&m_screenViewport, sizeof(D3D11_VIEWPORT));
m_screenViewport.TopLeftX = 0;
m_screenViewport.TopLeftY = 0;
m_screenViewport.Width = float(m_screenWidth);
m_screenViewport.Height = float(m_screenHeight);
// m_screenViewport.Width = static_cast<float>(m_screenHeight);
m_screenViewport.MinDepth = 0.0f;
m_screenViewport.MaxDepth = 1.0f; // Note: important for depth buffering

m_context->RSSetViewports(1, &m_screenViewport);

TopLeftX와 TopLeftY,
Width, Height를 수정함으로서
Viewport의 크기를 조절 가능하다

ex) 화면의 오른쪽 절반에만 출력하기

// Set the viewport
ZeroMemory(&m_screenViewport, sizeof(D3D11_VIEWPORT));
m_screenViewport.TopLeftX = m_screenWidth / 2;
m_screenViewport.TopLeftY = 0;
m_screenViewport.Width = float(m_screenWidth / 2);
m_screenViewport.Height = float(m_screenHeight);
// m_screenViewport.Width = static_cast<float>(m_screenHeight);
m_screenViewport.MinDepth = 0.0f;
m_screenViewport.MaxDepth = 1.0f; // Note: important for depth buffering

m_context->RSSetViewports(1, &m_screenViewport);

Image

물론 이 경우 aspect ratio는 맞지 않기에 다소 어색한 편
그렇기에 AspectRatio도 Width의 비율에 맞게 수정해주면
보다 자연스러워 진다

float AppBase::GetAspectRatio() const {
    return float(m_screenWidth / 2) / m_screenHeight;
}

Image

ImGui 유틸 함수들

  • ImGui 창의 위치와 크기를 얻는 방법
ImVec2 pos =ImGui::GetWindowPos(); // 위치
ImVec2 size =ImGui::GetWindowSize(); // 크기
  • ImGui 창의 위치와 크기를 넣는 방법
// loop 에 넣어 사실상 반쯤 고정시킬순 있음
ImGui::SetWindowPos(ImVec2(0,0));
ImGui::SetWindowSize(ImVec2(400, 400));

GUI 크기 변환에 따른 ViewPort 크기 바꾸기 2 - 동적으로 변환

먼저 Init 시점에선 어차피 ImGui와 연동할 수 없으므로
다시 원래대로 초기화한다

이후 새로 그릴때부터 GUI의 위치와 크기를 고려해 ViewPort 조정

먼저 이전의 ViewPort 설정 부분을 하나의 함수로 설정하도록 뺀 후

void SetViewport() {
    // Set the viewport
    ZeroMemory(&m_screenViewport, sizeof(D3D11_VIEWPORT));
    m_screenViewport.TopLeftX = 0;
    m_screenViewport.TopLeftY = 0;
    m_screenViewport.Width = float(m_screenWidth);
    m_screenViewport.Height = float(m_screenHeight);
    // m_screenViewport.Width = static_cast<float>(m_screenHeight);
    m_screenViewport.MinDepth = 0.0f;
    m_screenViewport.MaxDepth = 1.0f; // Note: important for depth buffering

    m_context->RSSetViewports(1, &m_screenViewport);
}

GUI width 관련한 변수를 생성한 후
다음과 같이 수정한다

void SetViewport() {
    // Set the viewport
    ZeroMemory(&m_screenViewport, sizeof(D3D11_VIEWPORT));
    m_screenViewport.TopLeftX = m_guiWidth;
    m_screenViewport.TopLeftY = 0;
    m_screenViewport.Width = float(m_screenWidth - m_guiWidth);
    m_screenViewport.Height = float(m_screenHeight);
    // m_screenViewport.Width = static_cast<float>(m_screenHeight);
    m_screenViewport.MinDepth = 0.0f;
    m_screenViewport.MaxDepth = 1.0f; // Note: important for depth buffering

    m_context->RSSetViewports(1, &m_screenViewport);
}

float AppBase::GetAspectRatio() const {
    return float(m_screenWidth - m_guiWidth) / m_screenHeight;
}

UpdateGUI()
{
  while()
  {
    ...
    
    m_guiWidth = ImGui::GetWindowSize().x;
  }
}

이후 마지막으로 이렇게 변경한 m_guiWidth가 적용된
ViewPort를 다시 RSSetViewport() 하도록 SetViewport()를 호출해준다!

void ExampleApp::Update(){
  ...
  m_aspect = AppBase::GetAspectRatio(); // <- GUI에서 조절
  ...(Projection)
}

void ExampleApp::Render() {
  ...
  SetViewport();
}

결과

Image

Image

GUI 넓이에 따라 ViewPort가 동적으로 수정된다

SetViewport() 값이 같은데 바꿀 필요가 없지 않나?

그렇기에 함수에 지역 정적 변수를 사용하여
같은 경우는 따로 RSSetViewports를 호출하지 않도록 할 수 있음
(사소(매우)한 팁)

void SetViewport(){
  static int Previous = m_guiWidth;

  if (Previous == m_guiWidth)
      return;

  ...
}

Window 크기 변환에 따른 Viewport 바꾸기

Windows 창 크기 변환 시
WM_SIZE 메시지가 발생
(처음 실행하거나, 사이즈 변경시 발생하는 메시지)

LRESULT AppBase::MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {

    if (ImGui_ImplWin32_WndProcHandler(hwnd, msg, wParam, lParam))
        return true;

    switch (msg) {
    case WM_SIZE:
        // Reset and resize swapchain
        // std::cout << (UINT)LOWORD(lParam) << " " << (UINT)HIWORD(lParam)
        //          << std::endl;

        if (m_swapChain) { // 처음 실행이 아닌지 확인 (Swapchain 존재 확인)

            m_screenWidth = int(LOWORD(lParam));
            m_screenHeight = int(HIWORD(lParam));
            m_guiWidth = 0;

            m_renderTargetView.Reset();
            m_swapChain->ResizeBuffers(0, // 현재 개수 유지
                                       (UINT)LOWORD(lParam), // 해상도 변경
                                       (UINT)HIWORD(lParam),
                                       DXGI_FORMAT_UNKNOWN, // 현재 포맷 유지
                                       0);
            CreateRenderTargetView();
            CreateDepthBuffer();
            SetViewport();
        }
      ...
    }

따라서 그 중 ‘첫 실행’을 제외한 경우
RTV와 Depth/Stencil 버퍼를 재 생성(화면 크기를 따름)
이후 Viewport를 다시 세팅해준다

bool AppBase::CreateRenderTargetView() {

    ComPtr<ID3D11Texture2D> backBuffer;
    m_swapChain->GetBuffer(0, IID_PPV_ARGS(backBuffer.GetAddressOf()));
    if (backBuffer) {
        m_device->CreateRenderTargetView(backBuffer.Get(), NULL,
                                         m_renderTargetView.GetAddressOf());
    } else {
        std::cout << "CreateRenderTargetView() failed." << std::endl;
        return false;
    }

    return true;
}

bool AppBase::CreateDepthBuffer() {
    D3D11_TEXTURE2D_DESC depthStencilBufferDesc;
    depthStencilBufferDesc.Width = m_screenWidth;
    depthStencilBufferDesc.Height = m_screenHeight;
    depthStencilBufferDesc.MipLevels = 1;
    depthStencilBufferDesc.ArraySize = 1;
    depthStencilBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    if (numQualityLevels > 0) {
        depthStencilBufferDesc.SampleDesc.Count = 4; // how many multisamples
        depthStencilBufferDesc.SampleDesc.Quality = numQualityLevels - 1;
    } else {
        depthStencilBufferDesc.SampleDesc.Count = 1; // how many multisamples
        depthStencilBufferDesc.SampleDesc.Quality = 0;
    }
    depthStencilBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    depthStencilBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    depthStencilBufferDesc.CPUAccessFlags = 0;
    depthStencilBufferDesc.MiscFlags = 0;

    if (FAILED(m_device->CreateTexture2D(
            &depthStencilBufferDesc, 0, m_depthStencilBuffer.GetAddressOf()))) {
        std::cout << "CreateTexture2D() failed." << std::endl;
    }
    if (FAILED(m_device->CreateDepthStencilView(m_depthStencilBuffer.Get(), 0,
                                                &m_depthStencilView))) {
        std::cout << "CreateDepthStencilView() failed." << std::endl;
    }
    return true;
}

결과

Image
Image
Image

Window의 크기에 따라 ViewPort가 동적으로 수정된다

댓글남기기