本教程不考虑Effects11(FX11),而是基于原始的HLSL。html
目前编译与加载着色器的方法以下:windows
.cso
(Compiled Shader Object)对象文件,在运行期间加载该文件以读取字节码。.inc
或.h
的头文件,着色器字节码在编译期间就能够肯定。在我的的DX11项目中,使用的是方法1(优先)和方法3的混合形式。尽管方法2是最近了解到的,但我的目前并不考虑更换为该方法。数组
DirectX11 With Windows SDK完整目录安全
欢迎加入QQ群: 727623616 能够一块儿探讨DX11,以及有什么问题也能够在这里汇报。函数
为了符合微软的约定,须要为你的着色器代码使用下面的扩展名(有所修改):布局
.hlsl
的文件用于编写HLSL的源代码,参与编译.hlsli
的文件做为HLSL的标头文件,不参与编译.cso
的文件做为已编译的着色器对象(Compiled Shader Object).inc
或.h
的文件是C++的头文件,但它的内部包含了着色器的字节码,使用BYTE
数组来记录如今以Rendering a Triangle项目为例,如今咱们已经编写好的着色器文件有Triangle.hlsli
, Triangle_VS.hlsl
, Triangle_PS.hlsl
这三个,它们存放项目在HLSL文件夹内。如今你能够将它拉进项目当中。大数据
其中Triangle.hlsli
做为HLSL的头文件默认不参与项目的编译过程。优化
而对于Triangle_VS.hlsl
和Triangle_PS.hlsl
,则在项目属性要这样设置:3d
其中入口点名称指的是该着色器阶段最早开始调用的函数名。好比在C/C++/新建的.hlsl文件中,默认的入口点名称是main。而上面的例子中,咱们但愿让顶点着色器从VS
函数开始运行,则须要指定入口点为VS
。调试
关于着色器模型,由于假定用户的显卡已经支持特性等级11.0,这里使用的是Shader Model 5.0
,若是你的显卡不支持特性等级11.0,则须要将特性等级降为10.0/10.1,分别对应能使用的着色器模型为4.0/4.1
生成项目后,须要留意在输出窗口(生成)中是否出现了下面的内容:
只有出现了上述内容,才说明成功编译出对象文件,不然说明没有被编译出来。若是你以前已经编译出对象文件,再编译时没有出现该输出结果,可能须要先删除以前编译出来的对象文件再试一次。
对着色器代码或文件的相关操做位于头文件d3dcompiler.h
,并且你还须要添加静态库d3dcompiler.lib
接下来,咱们使用下面的函数来读取编译好的着色器二进制信息:
HRESULT D3DReadFileToBlob(LPCWSTR pFileName, // [In].cso文件名 ID3DBlob** ppContents); // [Out]获取二进制大数据块
注意:若是你的项目中不存在该函数,说明你可能预先包含了DX SDK,然而该教程使用的是Windows SDK,该函数位于D3DCompiler >= 46的版本,所以你须要剔除DX SDK的包含路径和库路径。
使用方式也十分简单(以建立顶点着色器和顶点布局为例):
ComPtr<ID3DBlob> blob; HR(D3DReadFileToBlob(L"HLSL\\Triangle_VS.cso", blob.GetAddressOf())); HR(md3dDevice->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_pVertexShader.GetAddressOf())); // 建立顶点布局 HR(md3dDevice->CreateInputLayout(VertexPosColor::inputLayout, ARRAYSIZE(VertexPosColor::inputLayout), blob->GetBufferPointer(), blob->GetBufferSize(), m_pVertexLayout.GetAddressOf()));
而后就能够拿获取到的ID3DBlob
来建立着色器了。建立着色器和顶点布局的部分在本文不进行讨论,请回到教程02继续查看。
该方法的特色是会在你的项目文件夹中产生编译好的着色器二进制文件,而且须要你在程序运行的时候直接读进来。
对于Triangle_VS.hlsl
和Triangle_PS.hlsl
,在项目属性要这样设置:
这里关于头文件的名称以及内部的全局变量名能够自行决定。
头文件 通过编译后会在HLSL文件夹产生Triangle_VS.inc
和Triangle_PS.inc
两个文件,观察里面的代码你能够发现里面有汇编部分(不会包含进代码中)和一个全局变量,在Triangle_VS.inc
中产生的是全局变量gTriangle_VS
,而在Triangle_PS.inc
中产生的是全局变量gTriangle_PS
。这两个变量都是BYTE
数组,里面的内容正是编译好的字节码。
如今须要在你须要编写建立着色器相关代码的源文件上面包含这两个头文件:
#include "HLSL/Triangle_VS.inc" #include "HLSL/Triangle_PS.inc"
而后建立顶点着色器和顶点布局的代码变成了这样:
// 建立顶点着色器 HR(m_pd3dDevice->CreateVertexShader(gTriangle_VS, sizeof(gTriangle_VS), nullptr, m_pVertexShader.GetAddressOf())); // 建立并绑定顶点布局 HR(m_pd3dDevice->CreateInputLayout(VertexPosColor::inputLayout, ARRAYSIZE(VertexPosColor::inputLayout), gTriangle_VS, sizeof(gTriangle_VS), m_pVertexLayout.GetAddressOf()));
接下来就能够生成整个项目了,须要留意是否有红色部分的输出,不然可能没有成功编译出.inc
文件(这可能会在已有.inc
文件再次编译的时候致使出现问题,须要删除原来的.inc
文件)。
因为上述两个头文件的产生(即着色器的编译)先于项目的编译,在没有产生这两个头文件的时候,你也能够忍着编译错误先把上述代码添加进去,而后编译的时候就一切正常了。
该方法的特色是全部的过程均在编译期完成,着色器字节码镶嵌在了你的应用程序内部,可能会致使应用程序变大。
如今你须要了解这些函数
HRESULT D3DCompileFromFile( LPCWSTR pFileName, // [In]要编译的.hlsl文件 CONST D3D_SHADER_MACRO* pDefines, // [In_Opt]忽略 ID3DInclude* pInclude, // [In_Opt]如何应对#include宏 LPCSTR pEntrypoint, // [In]入口函数名 LPCSTR pTarget, // [In]使用的着色器模型 UINT Flags1, // [In]D3DCOMPILE系列宏 UINT Flags2, // [In]D3DCOMPILE_FLAGS2系列宏 ID3DBlob** ppCode, // [Out]得到着色器的二进制块 ID3DBlob** ppErrorMsgs); // [Out]可能会得到错误信息的二进制块
再次注意:若是你的项目中不存在该函数,说明你可能预先包含了DX SDK,然而该教程使用的是Windows SDK,该函数位于D3DCompiler >= 46的版本,所以你须要剔除DX SDK的包含路径和库路径。
其中pInclude
用于决定如何处理包含文件。若是设为nullptr
,则编译的着色器代码包含#include
时会引起编译器报错。若是你须要使用#include
,能够传递D3D_COMPILE_STANDARD_FILE_INCLUDE
宏,这是一个默认的包含句柄,能够按该着色器代码所处的相对路径去搜索对应的头文件并包含进来。
#define D3D_COMPILE_STANDARD_FILE_INCLUDE ((ID3DInclude*)(UINT_PTR)1)
HRESULT D3DWriteBlobToFile( ID3DBlob* pBlob, // [In]编译好的着色器二进制块 LPCWSTR pFileName, // [In]输出文件名 BOOL bOverwrite); // [In]是否容许覆盖
对于bOverwrite
来讲,不管是TRUE
仍是FALSE
都可有可无,由于咱们只有在检测到没有编译好的着色器文件时才会启动运行期编译,而后再保存到文件。
具体用法已经集成在下面的CreateShaderFromFile
函数中了
下面是CreateShaderFromFile
函数的实现,如今该函数已经放到了d3dUtil.h中,须要依赖dxerr
,你也能够本身修改这个函数的实现:
// 安全COM组件释放宏 #define SAFE_RELEASE(p) { if ((p)) { (p)->Release(); (p) = nullptr; } } // ------------------------------ // CreateShaderFromFile函数 // ------------------------------ // [In]csoFileNameInOut 编译好的着色器二进制文件(.cso),如有指定则优先寻找该文件并读取 // [In]hlslFileName 着色器代码,若未找到着色器二进制文件则编译着色器代码 // [In]entryPoint 入口点(指定开始的函数) // [In]shaderModel 着色器模型,格式为"*s_5_0",*能够为c,d,g,h,p,v之一 // [Out]ppBlobOut 输出着色器二进制信息 HRESULT CreateShaderFromFile(const WCHAR * csoFileNameInOut, const WCHAR * hlslFileName, LPCSTR entryPoint, LPCSTR shaderModel, ID3DBlob ** ppBlobOut) { HRESULT hr = S_OK; // 寻找是否有已经编译好的顶点着色器 if (csoFileNameInOut && D3DReadFileToBlob(csoFileNameInOut, ppBlobOut) == S_OK) { return hr; } else { DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS; #ifdef _DEBUG // 设置 D3DCOMPILE_DEBUG 标志用于获取着色器调试信息。该标志能够提高调试体验, // 但仍然容许着色器进行优化操做 dwShaderFlags |= D3DCOMPILE_DEBUG; // 在Debug环境下禁用优化以免出现一些不合理的状况 dwShaderFlags |= D3DCOMPILE_SKIP_OPTIMIZATION; #endif ID3DBlob* errorBlob = nullptr; hr = D3DCompileFromFile(hlslFileName, nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, entryPoint, shaderModel, dwShaderFlags, 0, ppBlobOut, &errorBlob); if (FAILED(hr)) { if (errorBlob != nullptr) { OutputDebugStringA(reinterpret_cast<const char*>(errorBlob->GetBufferPointer())); } SAFE_RELEASE(errorBlob); return hr; } // 若指定了输出文件名,则将着色器二进制信息输出 if (csoFileNameInOut) { return D3DWriteBlobToFile(*ppBlobOut, csoFileNameInOut, FALSE); } } return hr; }
使用方式以下:
ComPtr<ID3DBlob> blob; // 建立顶点着色器 HR(CreateShaderFromFile(L"HLSL\\Triangle_VS.cso", L"HLSL\\Triangle_VS.hlsl", "VS", "vs_5_0", blob.ReleaseAndGetAddressOf())); HR(m_pd3dDevice->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_pVertexShader.GetAddressOf())); // 建立并绑定顶点布局 HR(m_pd3dDevice->CreateInputLayout(VertexPosColor::inputLayout, ARRAYSIZE(VertexPosColor::inputLayout), blob->GetBufferPointer(), blob->GetBufferSize(), m_pVertexLayout.GetAddressOf()));
参考文章:
DirectX11 With Windows SDK完整目录
欢迎加入QQ群: 727623616 能够一块儿探讨DX11,以及有什么问题也能够在这里汇报。