OpenGL shader文件 include

需求:写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文件
...

参考资料:

  1. How to Using the #include in glsl support ARB_shading_language_include

你可能感兴趣的:(OpenGL学习笔记,遇到的问题)