D3D12渲染技术之编译Shader

本文深入介绍了在Direct3D12中如何编译Shader,包括使用 HLSL 源代码文件,D3DCompileFromFile 函数,以及编译时的参数设置。强调了编译Shader的重要性,如提高加载速度,提前发现错误。还提到了离线编译Shader的益处,如使用FXC工具,以及Visual Studio的集成支持和其限制。
摘要由CSDN通过智能技术生成

很多人只知道写Shader,但是并不了解DX或者OpenGL是如何编译Shader的,我们写的Shader是一种文本文件,它可以被DX或者OpenGL读取,说明它们提供了接口编译Shader。3D引擎都与DX或者OpenGL相关的,本篇博客就给读者介绍如何编译Shader的。
在Direct3D中,必须首先将着色器程序编译为可移植字节码, 然后,图形驱动程序将采用此字节码并将其再次编译为系统GPU 的最佳本机指令。 在运行时,我们可以使用以下函数编译着色器:

HRESULT D3DCompileFromFile(
  LPCWSTR pFileName,
  const D3D_SHADER_MACRO *pDefines,
  ID3DInclude *pInclude,
  LPCSTR pEntrypoint,
  LPCSTR pTarget,
  UINT Flags1,
  UINT Flags2,
  ID3DBlob **ppCode,
  ID3DBlob **ppErrorMsgs);

介绍一下,上面接口的参数含义:
pFileName:包含我们要编译的HLSL源代码的.hlsl文件的名称。
pDefines:我们不使用的高级选项; 请参阅SDK文档。 我们总是在案例中指定null;
pInclude:我们不使用的高级选项; 请参阅SDK文档。 我们总是在案例中指定null。
pEntrypoint:着色器入口点的函数名称, .hlsl可以包含多个着色器程序(例如,一个顶点着色器和一个像素着色器),因此我们需要指定我们想要编译的特定着色器的入口点。
pTarget:一个字符串,指定我们正在使用的着色器程序类型和版本。 在本博客中,我们以5.0和5.1为目标。
a)vs_5_0和vs_5_1:分别为顶点着色器5.0和5.1。
b)hs_5_0和hs_5_1:分别为Hull着色器5.0和5.1。
c)ds_5_0和ds_5_1:域着色器5.0和5.1。
d)gs_5_0和gs_5_1:分别为几何着色器5.0和5.1。
e)ps_5_0和ps_5_1:分别为像素着色器5.0和5.1。
f)cs_5_0和cs_5_1:分别计算着色器5.0和5.1。
Flags1:标志,用于指定着色器代码的编译方式, SDK文档中列出了相当多的这些标志,但我们在本博客中使用的只有两个:
a)D3DCOMPILE_DEBUG:在调试模式下编译着色器。
b)D3DCOMPILE_SKIP_OPTIMIZATION:指示编译器跳过优化(对调试很有用)。
Flags2:我们不使用的高级效果编译选项; 请参阅SDK文档。
ppCode:返回指向存储已编译着色器对象字节码的ID3DBlob数据结构的指针。
ppErrorMsgs:返回指向ID3DBlob数据结构的指针,该数据结构存储包含编译错误的字符串(如果有)。
类型ID3DBlob只是一个通用的内存块,有两种方法:
LPVOID GetBufferPointer:返回数据的void *,因此在使用之前必须将其转换为适当的类型(请参阅下面的示例)。
SIZE_T GetBufferSize:返回缓冲区的字节大小。
为了支持错误输出,我们实现了以下帮助函数,以便在运行时在d3dUtil.h / .cpp中编译着色器:

ComPtr<ID3DBlob> d3dUtil::CompileShader(
   const std::wstring& filename,
   const D3D_SHADER_MACRO* defines,
   const std::string& entrypoint,
   const std::string& target)
{
 // Use debug flags in debug mode.
  UINT compileFlags = 0;
#if defined(DEBUG) || defined(_DEBUG) 
  compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#endif

  HRESULT hr = S_OK;

  ComPtr<ID3DBlob> byteCode = nullptr;
  ComPtr<ID3DBlob> errors;
  hr = D3DCompileFromFile(filename.c_str(), defines, D3D_COMPILE_STANDARD_FILE_INCLUDE,
    entrypoint.c_str(), target.c_str(), compileFlags, 0, &byteCode, &errors);

  // Output errors to debug window.
  if(errors != nullptr)
    OutputDebugStringA((char*)errors->GetBufferPointer());

  ThrowIfFailed(hr);

  return byteCode;
 
在 D3D12 中,渲染超大图片可以使用纹理切割技术,将大型纹理分成多个小型纹理,并将它们分别加载到显存中。然后,在渲染时,可以使用纹理数组来绘制超过功能级别最大纹理大小的图片。 下面是使用纹理切割渲染超大图片的代码示例: ```c++ // 定义纹理大小 const UINT textureWidth = 4096; const UINT textureHeight = 4096; // 加载大型纹理 ComPtr<ID3D12Resource> textureResource; ThrowIfFailed(CreateTextureFromFile(device, commandList, L"large_texture.png", textureResource.GetAddressOf())); // 创建小型纹理 const UINT subTextureWidth = 512; const UINT subTextureHeight = 512; for (UINT y = 0; y < textureHeight; y += subTextureHeight) { for (UINT x = 0; x < textureWidth; x += subTextureWidth) { // 计算子纹理的大小和位置 const UINT subTextureX = x; const UINT subTextureY = y; const UINT subTextureW = min(subTextureWidth, textureWidth - x); const UINT subTextureH = min(subTextureHeight, textureHeight - y); // 创建子纹理资源 ComPtr<ID3D12Resource> subTextureResource; ThrowIfFailed(device->CreateCommittedResource(..., D3D12_RESOURCE_STATE_COPY_DEST, ...)); // 将大型纹理的子区域复制到小型纹理中 D3D12_TEXTURE_COPY_LOCATION srcLocation = CD3DX12_TEXTURE_COPY_LOCATION(textureResource.Get(), subTextureX, subTextureY, 0); D3D12_TEXTURE_COPY_LOCATION dstLocation = CD3DX12_TEXTURE_COPY_LOCATION(subTextureResource.Get(), 0, 0, 0); commandList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, nullptr); // 添加小型纹理到纹理数组中 m_textureArray.push_back(subTextureResource); } } // 绑定纹理数组到着色器资源视图 CD3DX12_CPU_DESCRIPTOR_HANDLE srvHandle(m_srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); for (UINT i = 0; i < m_textureArray.size(); i++) { device->CreateShaderResourceView(m_textureArray[i].Get(), nullptr, srvHandle); srvHandle.Offset(m_srvDescriptorSize); } // 绘制超大图片 const UINT numVertices = 4; const UINT vertexBufferSize = sizeof(Vertex) * numVertices; Vertex quadVertices[] = { { { -1.0f, 1.0f, 0.0f }, { 0.0f, 0.0f } }, { { 1.0f, 1.0f, 0.0f }, { 1.0f, 0.0f } }, { { -1.0f, -1.0f, 0.0f }, { 0.0f, 1.0f } }, { { 1.0f, -1.0f, 0.0f }, { 1.0f, 1.0f } } }; ComPtr<ID3D12Resource> vertexBuffer; ThrowIfFailed(device->CreateCommittedResource(..., D3D12_RESOURCE_STATE_COPY_DEST, ...)); ComPtr<ID3D12Resource> vertexUploadBuffer; ThrowIfFailed(device->CreateCommittedResource(..., D3D12_RESOURCE_STATE_GENERIC_READ, ...)); D3D12_SUBRESOURCE_DATA vertexData = {}; vertexData.pData = reinterpret_cast<BYTE*>(quadVertices); vertexData.RowPitch = vertexBufferSize; vertexData.SlicePitch = vertexBufferSize; UpdateSubresources(commandList, vertexBuffer.Get(), vertexUploadBuffer.Get(), 0, 0, 1, &vertexData); commandList->ResourceBarrier(..., D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, ...); commandList->IASetVertexBuffers(...); commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); CD3DX12_GPU_DESCRIPTOR_HANDLE srvGpuHandle(m_srvDescriptorHeap->GetGPUDescriptorHandleForHeapStart()); commandList->SetGraphicsRootDescriptorTable(0, srvGpuHandle); commandList->DrawInstanced(numVertices, 1, 0, 0); // 清理资源 m_textureArray.clear(); ``` 在这个示例中,我们首先加载大型纹理,然后将其分成多个小型纹理。然后,我们将每个小型纹理复制到单独的资源中,并将它们添加到纹理数组中。最终,我们绑定纹理数组到着色器资源视图中,并使用它来绘制超过功能级别最大纹理大小的图片。 需要注意的是,纹理切割技术会增加额外的资源开销和绘制开销,因此应该尽可能地避免使用超过功能级别最大纹理大小的图片。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海洋_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值