一个小的背景。我正在尝试制作一个Windows(10)的应用程序 使屏幕看起来像一个老式的CRT显示器,扫描线,模糊,和所有。我正在使用这个微软官方的 屏幕捕捉演示 作为起点。在这个阶段,我可以捕获一个窗口, 并显示在一个新的鼠标通过窗口,就像它是原来的窗口。
我正试图使用 CRT-Royale CRT着色器 一般认为是最好的CRT着色器;这些都是.cg格式的。我将它们用cgc移植到hlsl中,然后用fxc将hlsl文件编译成编译后的着色器字节码。我能够成功加载编译后的着色器并创建像素着色器。然后我在d3d上下文中设置了像素着色器。然后我尝试将捕获表面帧复制到像素着色器资源中,并设置创建的着色器资源。所有这些都能构建和运行,但我没有看到输出图像有任何不同,不知道该如何继续。下面是相关代码。我不是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调用,而不是在背面缓冲区上绘制一个全屏四边形(这需要你创建一个可以绑定的顶点缓冲区,然后将背面缓冲区设置为渲染目标,最后调用DrawDrawIndexed来实际渲染一些东西,这样就会调用着色器)。
另外--因为我不确定你是否已经这样做了,只是把它从所示的代码中剥离出来--像CreatePixelShader这样的函数不会为了好玩而返回HRESULTs--你应该检查实际返回的内容,因为DirectX会默默地返回大多数错误,并希望你处理它们,而不是让你的程序崩溃。