需求:写glsl shader文件写多了,有一些公共的函数要在不同shader文件重复写一遍,相当烦!因此想include一个公共的shader文件,这个文件存一些公共函数。
实现这个功能有两个思路:要么在编译shader文件时处理宏定义include,同时要有自己一套的文件系统,shader文件里面include哪个文件,就去搜索自己的valid路径下是否存在这个文件,如果存在,读这个文件成字符串,与原来的shader文件拼接成一个字符串,再用gl compiler去编译;要么是用扩展GL_ARB_shading_language_include
,这个扩展的资料相当少(指的是万能的谷歌,而不是废物baidu)。这里用了第二种思路来实现shader文件的include功能。
GL_ARB_shading_language_include
这个是GL的扩展,需要先检查自己的环境是否支持这个扩展,如果不支持,你可以关掉了(对不起)。下面是检查是否支持扩展的代码(截取我项目的代码)
static std::vector g_supportExtensions;
void GetSupportExtensions()
{
if (!g_supportExtensions.empty())
return;
GLint n, i;
glGetIntegerv(GL_NUM_EXTENSIONS, &n);
for (i = 0; i < n; i++)
{
std::string extension = (char*)glGetStringi(GL_EXTENSIONS, i);
//CEPHEI_LOGDEBUGF("%s ", extension.c_str());
g_supportExtensions.push_back(extension);
}
}
bool CheckExtension(const std::string& extensionName)
{
for (int i = 0; i < g_supportExtensions.size(); i++)
{
if (g_supportExtensions[i] == extensionName)
return true;
}
return false;
}
// 怎么用, CEPHEI_LOGXXX那些不用管,拿来输出的
if (!CheckExtension("GL_ARB_shading_language_include"))
{
CEPHEI_LOGWARNING("GPU doesn't support GL_ARB_shading_language_include");
}
用的是glad这个库,里面包含了GL的函数和各厂商的扩展函数。这里是遇到的第一个坑:因为要用到扩展函数glNamedStringARB()
和glCompileShaderIncludeARB()
,然后在glad头文件压根没有这两个函数,看了一些配置资料,发现我之前从glad在线网址下载的api文件是core的,因此重新下了profile为compatibility的api文件,将新的代码文件重新覆盖了旧的代码文件即可,这里不懂的地方百度搜怎么配置glad。
实现思路:通过glNamedStringARB()
函数将被include的shader文件传入GL的虚拟文件系统,然后在编译需要include的shader文件时,调用glCompileShaderIncludeARB()
函数去包含相应的文件。限制:被include的文件需要先被传入虚拟文件系统,才能去编译自己的shader代码(不过在代码里面稍微注意一下即可,问题不大)。
void AddCommonShaderFile(const std::string& fileName)
{
if (fileName.empty())
return;
string shaderFolder = m_context->GetConfigParams("ShaderPath");
string shaderPath = shaderFolder + "Common/" + fileName + ".glsl";
/// 读common shader文件
std::string code;
std::ifstream shaderFile;
shaderFile.exceptions(std::ifstream::badbit);
try
{
/// 打开文件
shaderFile.open(shaderPath);
std::stringstream shaderStream;
/// 读取文件的缓冲内容到流中;
shaderStream << shaderFile.rdbuf();
/// 关闭文件
shaderFile.close();
/// 转换流至GLchar数组
code = shaderStream.str();
}
catch (std::ifstream::failure e) {
CEPHEI_LOGERROR("init shader error: failed to read shader file");
}
/// 将common shader文件通过glNamedStringARB函数传入GL的虚拟文件系统
/// 例如传入的shader文件名为 commonFunction.glsl
/// 那么glNamedStringARB的第二个参数为:"/commonFunction.glsl", "/"是一定需要的
/// 第四个参数为 代码的字符串
string fullFileName = "/" + fileName + ".glsl";
glNamedStringARB(GL_SHADER_INCLUDE_ARB, fullFileName.size(), fullFileName.c_str(), code.size(), code.c_str());
}
在编译正常shader时,
static const char* const searchPath = "/";
GLuint m_handle = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(m_handle, 1, &shaderCode, NULL);
glCompileShaderIncludeARB(m_handle, 1, &searchPath, nullptr);
glCompileShader(m_handle);
shader代码:
#version 430 core
#extension GL_ARB_shading_language_include : require
#include "/CommonFunction.glsl"
....
你的shader代码
我遇到的坑是要用version 430, 如果用version 450的话,在编译代码时会报extension要放在其他非预定义语句前面,我改成430就没报错了。
CommonFunciton.glsl代码:
/// gamma校正
vec3 GammaCorrected(vec3 color)
{
float gamma = 2.2;
return pow(color, vec3(1.0 / gamma));
}
注意一下使用顺序:
AddCommonShaderFile("CommonFunction");
/// 再去编译你的shader文件
...
参考资料: