繁体   English   中英

将 HLSL 像素着色器应用于 Win32 屏幕捕获

[英]Applying HLSL Pixel Shaders to Win32 Screen Capture

一点背景知识:我正在尝试制作一个 Windows (10) 应用程序,它使屏幕看起来像旧的 CRT 显示器、扫描线、模糊等等。 我使用这个官方的 Microsoft 屏幕截图演示作为起点:在这个阶段,我可以捕获 window,并通过 window 在新的鼠标中显示它,就好像它是原来的 window。

我正在尝试使用通常被认为是最好的 CRT 着色器的CRT-Royale CRT 着色器; 这些都以 .cg 格式提供。 我使用 cgc 将它们转换为 hlsl,然后将 hlsl 文件编译为使用 fxc 编译的着色器字节码。 我能够成功加载编译的着色器并创建像素着色器。 然后我在 d3d 上下文中设置像素着色器。 然后我尝试将捕获表面帧复制到像素着色器资源并设置创建的着色器资源。 所有这些都构建并运行,但我在 output 映像中看不到任何差异,并且不确定如何继续。 下面是相关代码。 我不是 c++ 开发人员,我将其作为个人项目,一旦我有一个原始的工作版本,我计划开源。 任何建议表示赞赏,谢谢。

SimpleCapture::SimpleCapture(
IDirect3DDevice const& device,
GraphicsCaptureItem const& item)
{
m_item = item;
m_device = device;

// Set up 
auto d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);
d3dDevice->GetImmediateContext(m_d3dContext.put());
auto size = m_item.Size();

m_swapChain = CreateDXGISwapChain(
    d3dDevice, 
    static_cast<uint32_t>(size.Width),
    static_cast<uint32_t>(size.Height),
    static_cast<DXGI_FORMAT>(DirectXPixelFormat::B8G8R8A8UIntNormalized),
    2);

// ADDED THIS
HRESULT hr1 = D3DReadFileToBlob(L"crt-royale-first-pass-ps_4_0.fxc", &ps_1_buffer);
HRESULT hr = d3dDevice->CreatePixelShader(
    ps_1_buffer->GetBufferPointer(),
    ps_1_buffer->GetBufferSize(),
    nullptr,
    &ps_1
);
m_d3dContext->PSSetShader(
    ps_1,
    nullptr,
    0
);
// END OF ADDED CHANGES

// Create framepool, define pixel format (DXGI_FORMAT_B8G8R8A8_UNORM), and frame size. 
m_framePool = Direct3D11CaptureFramePool::Create(
    m_device,
    DirectXPixelFormat::B8G8R8A8UIntNormalized,
    2,
    size);
m_session = m_framePool.CreateCaptureSession(m_item);
m_lastSize = size;
m_frameArrived = m_framePool.FrameArrived(auto_revoke, { this, &SimpleCapture::OnFrameArrived });
}

void SimpleCapture::OnFrameArrived(
Direct3D11CaptureFramePool const& sender,
winrt::Windows::Foundation::IInspectable const&)
{
auto newSize = false;

{
    auto frame = sender.TryGetNextFrame();
    auto frameContentSize = frame.ContentSize();

    if (frameContentSize.Width != m_lastSize.Width ||
        frameContentSize.Height != m_lastSize.Height)
    {
        // The thing we have been capturing has changed size.
        // We need to resize our swap chain first, then blit the pixels.
        // After we do that, retire the frame and then recreate our frame pool.
        newSize = true;
        m_lastSize = frameContentSize;
        m_swapChain->ResizeBuffers(
            2, 
            static_cast<uint32_t>(m_lastSize.Width),
            static_cast<uint32_t>(m_lastSize.Height),
            static_cast<DXGI_FORMAT>(DirectXPixelFormat::B8G8R8A8UIntNormalized), 
            0);
    }

    {
        auto frameSurface = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());

        com_ptr<ID3D11Texture2D> backBuffer;
        check_hresult(m_swapChain->GetBuffer(0, guid_of<ID3D11Texture2D>(), backBuffer.put_void()));

        // ADDED THIS
        D3D11_TEXTURE2D_DESC txtDesc = {};
        txtDesc.MipLevels = txtDesc.ArraySize = 1;
        txtDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
        txtDesc.SampleDesc.Count = 1;
        txtDesc.Usage = D3D11_USAGE_IMMUTABLE;
        txtDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;

        auto d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);

        ID3D11Texture2D *tex;
        d3dDevice->CreateTexture2D(&txtDesc, NULL,
            &tex);
        frameSurface.copy_to(&tex);

        d3dDevice->CreateShaderResourceView(
            tex,
            nullptr,
            srv_1
        );

        auto texture = srv_1;
        m_d3dContext->PSSetShaderResources(0, 1, texture);
        // END OF ADDED CHANGES

        m_d3dContext->CopyResource(backBuffer.get(), frameSurface.get());
    }
}

DXGI_PRESENT_PARAMETERS presentParameters = { 0 };
m_swapChain->Present1(1, 0, &presentParameters);
... // Truncated

着色器定义了事物的绘制方式。 但是,您不绘制任何东西——您只是复制,这就是着色器不做任何事情的原因。

您应该做的是删除 CopyResource 调用,而是在后台缓冲区上绘制一个全屏四边形(这需要您创建一个可以绑定的顶点缓冲区,然后将后台缓冲区设置为渲染目标,最后调用 Draw/ DrawIndexed 以实际渲染某些内容,然后将调用着色器)。

另外 - 因为我不确定你是否已经这样做并且只是从显示的代码中删除它 - 像 CreatePixelShader 这样的函数不会仅仅为了好玩而返回 HRESULT - 你应该检查实际返回的内容,因为 DirectX 会默默地返回大多数错误并希望您处理它们,而不是使程序崩溃。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM