首先是 ShaderCombination结构体
参数有:
1 uint32 combMask
这个变量主要用来 表示#define的位,每一位表示一个 #define
如在 shader文件中 有这样的语句:
...
#ifdef _F03_ParallaxMapping
#define _F02_NormalMapping
#endif
...
#ifdef _F02_NormalMapping
uniform sampler2D normalMap;
#endif
....
这里面 _F03_, _F02_中的 03, 02 就是一个#define标识, 它会被 ShaderResource类的 cacCombMask 分别计算成为 num = 0*10 + 3 , num = 0*10 + 2
然后 与 combMask 进行 |= 1<<(num-1);
2 uint32 shaderObj
这个变量是 glCreateProgram创建 shader工程的句柄
3 就是 引擎里需要的 一些 uniform名字的句柄, 即 通过 glGetUniformLocation返回的句柄
其次 是 ShaderContext 结构体
在上一帖中,我们讲过 Shader文件 分为 3个部分,FX部分, VS_*和 FS_*部分。
ShaderContext结构体主要是用来存储 FX部分的一些信息
假如 shader文件 有这样的语句:
context ATTRIBPASS
{
VertexShader = compile GLSL VS_GENERAL;
PixelShader = compile GLSL FS_ATTRIBPASS;
}
像Map结构一样,每个name对应一个value; 每个context都对应一个名字
1 std::string id
它就是 context 对应的名字 如 ATTRIBPASS
2 形如 blendMode这样的宏 或者 depthTest这样的bool值
它们对应的 就是 context里
ZWriteEnable = false;
BlendMode = Add;
这样的语句
3 int vertCodeIdx, fragCodeIdx
这两个 是 存入到 shaderResource类 _codeSections里 CodeResource的 标识/句柄
即 VS_*部分或 FS_*的 CodeResource在 _codeSections里 的 位置
由于 每一个 context对应一个 VS_*, FS_*,在 ShaderResource类的 ParseFXSection函数里
会依次 存储 VS_*, FS_*到 _codeSections里,并 设置其位置 为 vertCodeIdx或 fragCodeIdx
4 std::vector<ShaderCombination> shaderCombs
参见上面 ShaderCombination结构体
5 flagMask
它是 CodeResource类在解析 VS_*, FS_*这部分内容时,会将 其中 #define的标识,计算出一个 类似 combMask的值,用来统计 shader文件里的 #define
Shader文件中,一个 context对应一个 ShaderContext及一个 ShaderCombination
像Logo这种简单的Shader文件,可能只有一个 context。
有的比较复杂的场景,可能同时又 场景,精灵,灯光,就需要多个 context
我们以渲染 一个 简单的Logo 为 例,来展示 Horde3D 如何一步一步解释 Shader类的工作原理:
首先,需要 以下文件:
logo.material.xml
内容:
<Material>
<Shader source = "shaders/overlay.shader"/>
<ShaderFlag name = "_F01_TEST"/>
<ShaderFlag name = "_F02_TEST"/>
</Material>
这里面,我们省去了 图片 Sampler类的加载,而添加了 一个 ShaderFlag用于解释 flagMask 和 combMask的用法
overlay.shader 存在 shaders文件夹下 其内容为:
[[FX]]
context OVERLAY
{
VertexShader = compile GLSL VS_OVERLAY;
PixelShader = compile GLSL FS_OVERLAY;
ZWriteEnable = false;
BlendMode = Blend;
}
[[VS_OVERLAY]]
uniform mat4 projMat;
attribute vec2 vertPos;
void main(void)
{
gl_Position = projMat*vec4(vertPos.x, vertPos.y, 1, 1);
}
[[FS_OVERLAY]]
#ifdef _F01_TEST
uniform sampler2D albedoMap;
#endif
#ifdef_F02_TEST
uniform vec4 olayColor;
#endif
void main(void)
{
.....
}
首先,将 创建 一个 MaterialResource资源类,
然后将 上述 logo.material.xml 里的 文本读取到 char *data 里,并调用 该MaterialResource 资源类的 load方法
利用 rapidxml对 其解析, 将 ShaderFlag的 值 _F01_TEST存到 _shaderFlags 里
根据 Shader 的值 即其Shader文件的路径名,添加一个ShaderResource类
然后 调用 ShaderResource的 calcCombMask 来计算出 _combMask,
calcCombMask 接收 _shaderFlags 作为其参数
logo.material.xml里的 ShaderFlag 有 _F01_TEST 和 _F02_TEST两个
那么, combMask的值 为 11,即 将 _F01_TEST的 01
num = 0*10 +1 = 1;
然后 1<<(1-1) 就为 个位的1
将 _F02_TEST的02
num = 0*10 +2 = 2;
然后 1<<(2-1) 就为 十位的 1
依此类推
其中 01, 02为 ShaderContext结构体中的flagMask的值, combMask即为 ShaderCombination中 combMask的值, combMask可以理解为 shader文件中 用户定义的#define的 集合
之后,调用 ShaderResource的 preLoadCombiantion(_combMask)
为什么叫 preLoad呢,因为在Material资源类处于加载的过程中, 调用的ShaderResource类的load方法还没有被调用,_combMask 是要与 。所以这里只是预先将 _combMask存储在 ShaderResource类的 _preLoadList里.
最后,调用ShaderResource类的load方法
在这个里面解析 shader文件的数据,然后调用compileContexts()方法
context.flagMask可以理解为 shader文件中 #define的集合, 将 _preLoadList的 combMask 与 context.flagMask进行与,可以理解为取 用户定义的 且 shader文件里有的 #define
调用compileCombiantion, 将 VS_*, FS_*的内容前面加上宏定义,然后交给 底层的渲染类去 compile和 link成shader工程
至此,除了CodeResource类,整个从加载Material类,到 读取Shader内容 给 RendererBase类就 全部走通了。
CodeResource类也只是简单的讲 VS_*, FS_*的内容进行解析, 并交给 compileCombianation来执行。