很多人只知道写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;