如何在 DirectX12 和 C++ 中渲染到纹理?流程是什么?

How to Render To Texture in DirectX12 & C++? What is the process?

本文关键字:纹理 是什么 DirectX12 C++      更新时间:2024-09-22

我一直在尝试如何在DX12中将整个场景渲染为纹理。我知道如何在OpenGL中做到这一点,但在DirectX12中我很难弄清楚。此外,网上没有太多关于它是如何做到的资源。

(目前我们在场景中有一个应用纹理的3D模型渲染(

有人能给我介绍一些资源吗?我可以用这些资源学习在DX12中渲染目标和渲染到纹理?或者有什么好的网站?

非常感谢您的帮助。

谨致问候,Charlie

OpenGL更像Direct3D 11,其中Direct3D 12和Vulkan在设计/使用以及有效使用它们所需的图形知识水平方面更相似。因此,在跳转到Direct3D 12渲染之前,您可能会发现从Direct3D 11开始更容易。概念和HLSL编程在11&12,所以这可能是一个很好的起点。

关于DirectX 12,最需要了解的是,它使应用程序(即程序员(负责Direct3D 11运行时处理的许多方面:CPU/GPU同步、内存管理、资源调度等。DirectX 12旨在为经验丰富的图形程序员提供更多的控制,从而能够在相同复杂度的渲染下实现更高级别的CPU端性能。然而,对于刚接触图形或DirectX的人来说,这种额外的控制和责任可能是压倒性的。在DX12中,写一些"在我的机器上工作",但不会在别人的机器上运行甚至崩溃的东西要容易得多。

有了所有这些,开始使用Direct3D 12:的一些好资源

  • DirectX有一个新的"登录页",其中包含许多用于DirectX 12开发的有用链接和资源:https://devblogs.microsoft.com/directx/landing-page/

  • DirectX图形团队编写的DirectX 12官方样例在DirectX图形样例中。

  • Xbox高级技术小组编写的公开样本在Xbox ATG样本中。特别是,请参阅IntroGraphics示例,它们在DX11&DX12型

  • DirectX工具包是一个开源C++库,为Direct3D开发提供帮助。DirectX 11和DirectX 12都有版本。如果您首先学习DX 11版本,那么从那里转到DX 12非常简单,因为在学习新的API时,它会为您处理许多"管家"任务。

关于DirectX 12中"渲染到纹理"的问题,有一些特定的示例需要查看:

  • SimpleMSAA确实渲染到纹理。

  • 适用于DX12的DirectX工具包的HDR渲染教程确实渲染到纹理。

第二个使用这个this-helper类h/cpp。

class RenderTexture
{
public:
RenderTexture(DXGI_FORMAT format) noexcept;
void SetDevice(_In_ ID3D12Device* device, D3D12_CPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptor);
void SizeResources(size_t width, size_t height);
void ReleaseDevice() noexcept;
void TransitionTo(_In_ ID3D12GraphicsCommandList* commandList, D3D12_RESOURCE_STATES afterState);
void BeginScene(_In_ ID3D12GraphicsCommandList* commandList)
{
TransitionTo(commandList, D3D12_RESOURCE_STATE_RENDER_TARGET);
}
void EndScene(_In_ ID3D12GraphicsCommandList* commandList)
{
TransitionTo(commandList, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
}
void SetClearColor(DirectX::FXMVECTOR color)
{
DirectX::XMStoreFloat4(reinterpret_cast<DirectX::XMFLOAT4*>(m_clearColor), color);
}
ID3D12Resource* GetResource() const noexcept { return m_resource.Get(); }
D3D12_RESOURCE_STATES GetCurrentState() const noexcept { return m_state; }
void SetWindow(const RECT& rect);
DXGI_FORMAT GetFormat() const noexcept { return m_format; }
private:
Microsoft::WRL::ComPtr<ID3D12Device>                m_device;
Microsoft::WRL::ComPtr<ID3D12Resource>              m_resource;
D3D12_RESOURCE_STATES                               m_state;
D3D12_CPU_DESCRIPTOR_HANDLE                         m_srvDescriptor;
D3D12_CPU_DESCRIPTOR_HANDLE                         m_rtvDescriptor;
float                                               m_clearColor[4];
DXGI_FORMAT                                         m_format;
size_t                                              m_width;
size_t                                              m_height;
};
RenderTexture::RenderTexture(DXGI_FORMAT format) noexcept :
m_state(D3D12_RESOURCE_STATE_COMMON),
m_srvDescriptor{},
m_rtvDescriptor{},
m_clearColor{},
m_format(format),
m_width(0),
m_height(0)
{
}
void RenderTexture::SetDevice(_In_ ID3D12Device* device, D3D12_CPU_DESCRIPTOR_HANDLE srvDescriptor, D3D12_CPU_DESCRIPTOR_HANDLE rtvDescriptor)
{
if (device == m_device.Get()
&& srvDescriptor.ptr == m_srvDescriptor.ptr
&& rtvDescriptor.ptr == m_rtvDescriptor.ptr)
return;
if (m_device)
{
ReleaseDevice();
}
{
D3D12_FEATURE_DATA_FORMAT_SUPPORT formatSupport = { m_format, D3D12_FORMAT_SUPPORT1_NONE, D3D12_FORMAT_SUPPORT2_NONE };
if (FAILED(device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &formatSupport, sizeof(formatSupport))))
{
throw std::runtime_error("CheckFeatureSupport");
}
UINT required = D3D12_FORMAT_SUPPORT1_TEXTURE2D | D3D12_FORMAT_SUPPORT1_RENDER_TARGET;
if ((formatSupport.Support1 & required) != required)
{
#ifdef _DEBUG
char buff[128] = {};
sprintf_s(buff, "RenderTexture: Device does not support the requested format (%u)!n", m_format);
OutputDebugStringA(buff);
#endif
throw std::runtime_error("RenderTexture");
}
}
if (!srvDescriptor.ptr || !rtvDescriptor.ptr)
{
throw std::runtime_error("Invalid descriptors");
}
m_device = device;
m_srvDescriptor = srvDescriptor;
m_rtvDescriptor = rtvDescriptor;
}
void RenderTexture::SizeResources(size_t width, size_t height)
{
if (width == m_width && height == m_height)
return;
if (m_width > UINT32_MAX || m_height > UINT32_MAX)
{
throw std::out_of_range("Invalid width/height");
}
if (!m_device)
return;
m_width = m_height = 0;
auto heapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
D3D12_RESOURCE_DESC desc = CD3DX12_RESOURCE_DESC::Tex2D(m_format,
static_cast<UINT64>(width),
static_cast<UINT>(height),
1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
D3D12_CLEAR_VALUE clearValue = { m_format, {} };
memcpy(clearValue.Color, m_clearColor, sizeof(clearValue.Color));
m_state = D3D12_RESOURCE_STATE_RENDER_TARGET;
// Create a render target
ThrowIfFailed(
m_device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES,
&desc,
m_state, &clearValue,
IID_GRAPHICS_PPV_ARGS(m_resource.ReleaseAndGetAddressOf()))
);
SetDebugObjectName(m_resource.Get(), L"RenderTexture RT");
// Create RTV.
m_device->CreateRenderTargetView(m_resource.Get(), nullptr, m_rtvDescriptor);
// Create SRV.
m_device->CreateShaderResourceView(m_resource.Get(), nullptr, m_srvDescriptor);
m_width = width;
m_height = height;
}
void RenderTexture::ReleaseDevice() noexcept
{
m_resource.Reset();
m_device.Reset();
m_state = D3D12_RESOURCE_STATE_COMMON;
m_width = m_height = 0;
m_srvDescriptor.ptr = m_rtvDescriptor.ptr = 0;
}
void RenderTexture::TransitionTo(_In_ ID3D12GraphicsCommandList* commandList, D3D12_RESOURCE_STATES afterState)
{
TransitionResource(commandList, m_resource.Get(), m_state, afterState);
m_state = afterState;
}
void RenderTexture::SetWindow(const RECT& output)
{
// Determine the render target size in pixels.
auto width = size_t(std::max<LONG>(output.right - output.left, 1));
auto height = size_t(std::max<LONG>(output.bottom - output.top, 1));
SizeResources(width, height);
}

你会这样使用它:

// Setup
m_scene = std::make_unique<DX::RenderTexture>( /* format that matches your resource and your Pipeline State Objects you will use to render */ );
m_scene->SetClearColor( /* color value you use to clear */ );
m_scene->SetDevice(m_device,
/* CPU descriptor handle for your scene as a SRV texture */,
/* CPU descriptor handle for your scene as a RTV texture */);
m_scene->SetWindow( /* provide viewport size for your render texture */ );
// Reset command list and allocator.
// Transition the backbuffer target into the correct state to allow for 
// Clear the render texture
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor(
/* CPU descriptor handle for your scene as a RTV texture */
static_cast<INT>(m_backBufferIndex), m_rtvDescriptorSize);
CD3DX12_CPU_DESCRIPTOR_HANDLE dsvDescriptor(m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
m_commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor);
m_commandList->ClearRenderTargetView(rtvDescriptor, /* clear color */, 0, nullptr);
m_commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
// Set the viewport and scissor rect.
D3D12_VIEWPORT viewport = { 0.0f, 0.0f, /* width/height of your render texture */, D3D12_MIN_DEPTH, D3D12_MAX_DEPTH };
D3D12_RECT scissorRect = { 0, 0, /* width/height of your render texture */ };
m_commandList->RSSetViewports(1, &viewport);
m_commandList->RSSetScissorRects(1, &scissorRect);
// Tell helper we are starting the render
m_scene->BeginScene(m_commandList);

/* Do rendering to m_commandList */
m_scene->EndScene(m_commandList);

在这里,我们已经安排了到渲染目标资源状态的转换,填充了所有绘制调用,然后将屏障插入回像素着色器资源状态。此时,可以使用渲染纹理SRV的描述符句柄进行渲染。与所有DirectX 12一样,在您真正关闭命令列表并提交执行之前,不会发生任何事情。