본문 바로가기

그래픽스

[그래픽스] 후처리 - 이미지 필터

이미지 필터는 화면 크기의 사각형을 그리는 쉐이더이다.

 

	D3D11_SAMPLER_DESC sampDesc;
        sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
        sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
        sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;

        D3D11_RASTERIZER_DESC rastDesc;
        rastDesc.DepthClipEnable = false;

 

이미지 처리를 할 때는 샘플러를 Clamp를 일반적으로 사용하기 때문에 Clamp로 해주고,

이미지이기 때문에 Depth가 필요없으므로 래스터라이저의 DepthClipEnable을 false로 해준다.

 

   ZeroMemory(&m_viewport, sizeof(D3D11_VIEWPORT));
        m_viewport.TopLeftX = 0;
        m_viewport.TopLeftY = 0;
        m_viewport.Width = float(width);
        m_viewport.Height = float(height);
        m_viewport.MinDepth = 0.0f;
        m_viewport.MaxDepth = 1.0f;

이미지의 해상도에 따라 Viewport를 바꿔줘야 하므로 이미지 필터 내에서 Viewport를 따로 세팅해준다.

 

        D3D11_TEXTURE2D_DESC txtDesc;
        txtDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; //  이미지 처리용도
        txtDesc.Usage = D3D11_USAGE_DEFAULT;
        txtDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE |
                            D3D11_BIND_RENDER_TARGET;

 

텍스쳐의 BindFlag는 DEFAULT나 IMMUTABLE이 아닌 SHADER_RESOURCE | RENDER_TARGET이다.

이미지 처리할 때는 텍스쳐를 읽을 뿐만 아니라 쉐이더를 렌더링할 수 있어야 하기 때문이다.

원래는 SwapChain이 메모리에 렌더링한 결과를 보여줄 수 있게 버퍼를 교체하는 역할을 하는데,

여기서는 SwapChain을 거치지 않고 별도의 메모리를 만들어 그 텍스쳐에 렌더링하는 방식을 사용한다. 

 

        device->CreateTexture2D(&txtDesc, NULL, texture.GetAddressOf());
        device->CreateRenderTargetView(texture.Get(), &viewDesc,
                                       m_renderTargetView.GetAddressOf());
        device->CreateShaderResourceView(texture.Get(), nullptr,
                                         m_shaderResourceView.GetAddressOf());

        m_pixelConstData.dx = 1.0f / width;
        m_pixelConstData.dy = 1.0f / height;

 

CreateTexture2D로 텍스쳐를 만들고 그것을 이용해서 CreateRenderTargetView, CreateShaderResourceView를 한다. 

이미지의 픽셀단위 간격을 알고 있으면 배열처럼 근처 픽셀 값들을 샘플링해서 가져올 수 있다.

 

    DXGI_SWAP_CHAIN_DESC sd;
    sd.BufferUsage = DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT;

 

이미지 후처리를 위해 SwapChain에서 RenderTarget을 출력할 뿐만 아니라 렌더링한 결과를 다시 한번 바꾸기 위해 

Input으로 넣어줄 필요가 있기 때문에 Shader_Input을 추가한다. 

 

  m_device->CreateRenderTargetView(backBuffer.Get(), nullptr,
                                         m_renderTargetView.GetAddressOf());
  m_device->CreateShaderResourceView(backBuffer.Get(), nullptr,
                                           m_shaderResourceView.GetAddressOf());

 

마찬가지로 AppBase::CreateRenderTargetView에서 백버퍼를 RenderTarget으로 사용할 뿐만 아니라 후처리를 위해

ShaderResource로도 사용할 것이므로 CreateShaderResourceView도 추가한다. 그러면 RenderTarget으로 사용되는 백버퍼를 ShaderResource로 이미지 필터에 넣어줄 수가 있다.

 

    auto copyFilter =
        make_shared<ImageFilter>(m_device, m_context, L"Sampling", L"Sampling",
                                 m_screenWidth, m_screenHeight);

    copyFilter->SetShaderResources({this->m_shaderResourceView});
    m_filters.push_back(copyFilter);
    
    auto finalFilter =
    make_shared<ImageFilter>(m_device, m_context, L"Sampling", L"Sampling",
                                 m_screenWidth, m_screenHeight);

    finalFilter->SetShaderResources({m_filters.back()->m_shaderResourceView);
    finalFilter->SetRenderTargets({this->m_renderTargetView});
    m_filters.push_back(finalFilter);

 

그 후 초기화할 때 m_filters에 이미지 필터들을 넣어준다. 먼저 copyFilter에서 백버퍼의 shaderResourceView를 입력받는다. 그 입력받은 결과를 finalFilter에서 다시 shaderResource로 받아준다음 renderTarget으로 설정해 보여지게 한다.

 

    for (auto &f : m_filters) {
        f->Render(m_context);
    }

 

그 후 Render에서 필터를 돌면서 후처리를 진행하게 된다.