问题描述
我一直在尝试弄清楚如何在 DX12 中将整个场景渲染为纹理。我知道如何在 OpenGL 中执行此操作,但在 DirectX12 中无法解决此问题。另外,网上关于它是如何完成的资源并不多。
(目前我们在场景中有一个应用了纹理的 3D 模型渲染)
有人能指点我一些资源,我可以使用这些资源在 DX12 中学习渲染目标和渲染到纹理吗?或者有什么好的网站?
非常感谢任何帮助。
亲切的问候, 查理
解决方法
OpenGL 更像 Direct3D 11,而 Direct3D 12 和 Vulkan 在设计/使用和有效使用它们所需的图形知识水平方面更相似。因此,您可能会发现在进入 Direct3D 12 渲染之前更容易从 Direct3D 11 开始。 11 和 12 之间的概念和 HLSL 编程都非常相似,因此可以从这里开始。
关于 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-Graphics-Samples。
-
Xbox Advanced Technology Group 编写的公开示例位于 Xbox-ATG-Samples。特别是,请参阅 IntroGraphics 示例,其中提供了许多 DX11 和 DX12 形式的基本示例。
-
DirectX 工具包 是一个开源 C++ 库,为开始 Direct3D 开发提供帮助。有 DirectX 11 和 DirectX 12 版本。如果您先学习 DX 11 版本,那么从那里迁移到 DX 12 非常简单,因为它会在您学习新 API 时为您处理许多“内务管理”任务。
至于 DirectX 12 中“渲染到纹理”的问题,有一些具体示例可以查看:
-
SimpleMSAA 进行渲染到纹理。
-
这个用于 DirectX Tool Kit for DX12 的 HDR rendering tutorial 可以渲染到纹理。
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 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,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(),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(),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 */,nullptr);
m_commandList->ClearDepthStencilView(dsvDescriptor,D3D12_CLEAR_FLAG_DEPTH,1.0f,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,/* 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 的所有内容一样,在您实际关闭命令列表并将其提交以供执行之前,什么都不会发生。