前言

本教程不考虑Effects11(FX11),而是基于原始的HLSL。

目前编译与加载着色器的方法如下:

  1. 使用Visual Studio中的HLSL编译器,随项目编译期间一同编译,并生成.cso(Compiled Shader Object)对象文件,在运行期间加载该文件以读取字节码。
  2. 使用Visual Studio中的HLSL编译器,随项目编译期间一同编译,并生成.inc.h的头文件,着色器字节码在编译期间就可以确定。
  3. 在程序运行期间编译着色器代码,并读取生成的字节码。

在个人的DX11项目中,使用的是方法1(优先)和方法3的混合形式。尽管方法2是最近了解到的,但个人目前并不考虑更换为该方法。

DirectX11 With Windows SDK完整目录

欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。

与着色器相关的文件扩展名

为了符合微软的约定,需要为你的着色器代码使用下面的扩展名(有所修改):

  1. 扩展名为.hlsl的文件用于编写HLSL的源代码,参与编译
  2. 扩展名为.hlsli的文件作为HLSL的标头文件,不参与编译
  3. 扩展名为.cso的文件作为已编译的着色器对象(Compiled Shader Object)
  4. 扩展名为.inc.h的文件是C++的头文件,但它的内部包含了着色器的字节码,使用BYTE数组来记录

方法1:编译期产生对象文件,并在运行期加载

现在以Rendering a Triangle项目为例,现在我们已经编写好的着色器文件有Triangle.hlsli, Triangle_VS.hlsl, Triangle_PS.hlsl这三个,它们存放项目在HLSL文件夹内。现在你可以将它拉进项目当中。

其中Triangle.hlsli作为HLSL的头文件默认不参与项目的编译过程。

而对于Triangle_VS.hlslTriangle_PS.hlsl,则在项目属性要这样设置:

其中入口点名称指的是该着色器阶段最先开始调用的函数名。比如在C/C++/新建的.hlsl文件中,默认的入口点名称是main。而上面的例子中,我们希望让顶点着色器从VS函数开始运行,则需要指定入口点为VS

关于着色器模型,因为假定用户的显卡已经支持特性等级11.0,这里使用的是Shader Model 5.0,如果你的显卡不支持特性等级11.0,则需要将特性等级降为10.0/10.1,分别对应能使用的着色器模型为4.0/4.1

生成项目后,需要留意在输出窗口(生成)中是否出现了下面的内容:

只有出现了上述内容,才说明成功编译出对象文件,否则说明没有被编译出来。如果你之前已经编译出对象文件,再编译时没有出现该输出结果,可能需要先删除之前编译出来的对象文件再试一次。

D3DReadFileToBlob函数--读取编译好的着色器二进制信息

对着色器代码或文件的相关操作位于头文件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继续查看。

该方法的特点是会在你的项目文件夹中产生编译好的着色器二进制文件,并且需要你在程序运行的时候直接读进来。

方法2:编译器产生头文件,并在项目中包含该文件

对于Triangle_VS.hlslTriangle_PS.hlsl,在项目属性要这样设置:

这里关于头文件的名称以及内部的全局变量名可以自行决定。

头文件 经过编译后会在HLSL文件夹产生Triangle_VS.incTriangle_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文件)。

由于上述两个头文件的产生(即着色器的编译)先于项目的编译,在没有产生这两个头文件的时候,你也可以忍着编译错误先把上述代码添加进去,然后编译的时候就一切正常了。

该方法的特点是所有的过程均在编译期完成,着色器字节码镶嵌在了你的应用程序内部,可能会导致应用程序变大。

方法3:运行期间编译着色器代码,生成字节码

现在你需要了解这些函数

D3DCompileFromFile函数--运行期编译.hlsl文件

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)

D3DWriteBlobToFile函数--将编译好的着色器二进制信息写入文件

HRESULT D3DWriteBlobToFile(ID3DBlob* pBlob,    // [In]编译好的着色器二进制块LPCWSTR pFileName,  // [In]输出文件名BOOL bOverwrite);   // [In]是否允许覆盖

对于bOverwrite来说,无论是TRUE还是FALSE都无关紧要,因为我们只有在检测到没有编译好的着色器文件时才会启动运行期编译,然后再保存到文件。

具体用法已经集成在下面的CreateShaderFromFile函数中了

CreateShaderFromFile函数的实现

下面是CreateShaderFromFile函数的实现,现在该函数已经放到了d3dUtil.h中,需要依赖dxerr和标准库的filesystem,你也可以自己修改这个函数的实现:

// 该函数需要包含filesystem头文件,并using namespace std::experimental;(C++11/14)// 安全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 && filesystem::exists(csoFileNameInOut)){return D3DReadFileToBlob(csoFileNameInOut, ppBlobOut);}else{DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#ifdef _DEBUG// 设置 D3DCOMPILE_DEBUG 标志用于获取着色器调试信息。该标志可以提升调试体验,// 但仍然允许着色器进行优化操作dwShaderFlags |= D3DCOMPILE_DEBUG;// 在Debug环境下禁用优化以避免出现一些不合理的情况dwShaderFlags |= D3DCOMPILE_SKIP_OPTIMIZATION;
#endifID3DBlob* 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()));

参考文章:

Compiling Shaders

How To: Compile a Shader

DirectX11 With Windows SDK完整目录

欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。

转载于:https://www.cnblogs.com/X-Jun/p/10066282.html

DirectX11--HLSL编译着色器的三种方法相关推荐

  1. 【Android】Eclipse自动编译NDK/JNI的三种方法

    [Android]Eclipse自动编译NDK/JNI的三种方法 SkySeraph Sep. 18th  2014 Email:skyseraph00@163.com 更多精彩请直接访问SkySer ...

  2. 用键盘打开计算机管理,windows10系统使用键盘打开设备管理器的三种方法

    我们经常都会通过设备管理器,来查看windows10系统中的硬件是否已经正常安装驱动.那么,在鼠标没用的情况下,该如何打开win10设备管理器呢?接下来,系统城小编给大家介绍下没有鼠标直接使用键盘打开 ...

  3. 如何用键盘打开设备管理器里计算机的属性,技巧:在Windows10系统中使用键盘打开设备管理器的三种方法...

    我们经常使用设备管理器来检查Windows10系统中的硬件是否已正确安装驱动程序. 那么,当鼠标无用时如何打开win10设备管理器?接下来,System City的编辑器将介绍三种方法,无需使用键盘即 ...

  4. GLSL三种基本类型(着色器语言三种变量)

    着色器语言和C语言一样,通过一个表示特定数据类型的关键词声明一个变量,比如int num;通过int关键字声明一个整数型变量num,不过着色器语言还提供了三个关键字attribute.uniform和 ...

  5. 反编译APK文件的三种方法

    因为学习Android编程的需要,有时我们需要对网络上发布的应用项目进行学习,可是Android项目一般是通过APK文件进行发布的,我们看不到源代码,嘿嘿,办法总会有的,而且不止一个... ps:对于 ...

  6. OpenGL编译着色器

    1.读入着色器的代码 一般着色器的代码可以写在raw下创建glsl文件. gls是openGL的着色语言. 1.1.顶点着色器 定义的每个单一的顶点,顶点着色器都会调用一次,当他被调用的时候,会在a_ ...

  7. python3.4.4实现网页爬虫基础之网页下载器三种方法

    这是是慕课网<Python开发简单爬虫>中网页下载器的三种实现方法,课程用的是python2.7,这里用最新的3.4.4实现出来,分享给新人: import urllib.request ...

  8. linux下dd做优盘启动_Linux中制作U盘启动盘的三种方法,使用启动盘创建器/dd命令和Etcher...

    本文介绍在Linux系统中制作U盘启动盘的三种方法:使用启动盘创建器.dd命令和Etcher,以下以优麒麟Ubuntu Kylin 19.04为例,为你介绍这三种方法.如果要在Windows中制作U盘 ...

  9. Android播放器的三种实现方法

    转载自:http://blog.csdn.net/wozuihaole/article/details/60867076 今天来说一下Android中怎么实现视频播放,我主要说三种: 1.MediaP ...

最新文章

  1. 20162316刘诚昊 第九周学习总结
  2. 阿里巴巴技术专家三画:如何画好架构图
  3. 蓝桥学院2019算法题1.3
  4. PHP不能连接MS SQL Server的解决方法
  5. java 连接mysql工具类_java连接Mysql数据库的工具类
  6. Microsoft Visual Studio Ultimate 2012 ISO 映像
  7. SAI钢笔工具如何使用,入门篇
  8. 泛微E9 获取附件内容,泛微Ecology9获取附件范例,Ecology9附件、E9 附件下载及上传集成平台
  9. 以图搜图 图像匹配_以图搜图是什么 如何实现以图搜图
  10. oracle查询sql对应用户名,Oracle EBS-SQL (SYS-21):sys_用户名与人员对应关系查询.sql
  11. 从零开始做运营-读书笔记一
  12. vue开发企业微信应用-开发必读
  13. Cloud-Platform 开源项目环境搭建及运行
  14. 安卓沉浸式状态栏_安卓平板也能有品质感,小新Pad Pro上手
  15. LaTex - PPT 换页动态效果(亲测有效)
  16. 基于Scrapy的链家二手房爬虫
  17. 成长的旅途,未知的邂逅
  18. 删掉微信聊天记录怎么恢复 ?怎么恢复微信聊天记录?
  19. 解决外星人笔记本Alienware15r3风扇声音过大的问题
  20. 网络安全之入侵win7主机自学笔记

热门文章

  1. 51赛尔号巅峰多精灵使用的常胜精灵全面分析
  2. 无法加载服务器指定地图,用javascript调用iserver服务器上的地图不显示
  3. 智立方董事长杨石头经典语录(仅供…
  4. (转)一个真实的幼升小故事 女儿的上学辛酸史
  5. java从入门到精通陈丹丹_五子棋设计报告.doc
  6. sqlserver数据库一键备份还原(全备份还原)亲测可用
  7. pip公开安装源速度测试及验证可用性
  8. Scrapy爬虫框架学习之Response对象
  9. C++ socketpair函数
  10. 微信小程序radio选中