问题:glsl 不支持 include。在进行如通用光照模型,阴影计算相关的shader编码时,会有一堆相同的代码需要编写。光照模型变更,升级时也需要维护,修改大面积代码,十分麻烦。
查找相关的问题后,发现,可以通过OpenGL的 ARB 中的 GL_ARB_shading_language_include 拓展,可以解决该问题。
原理理解:
shader (glsl)不支持文件系统,所以include 无法支持。
GL_ARB_shading_language_include 拓展,通过在内部构建一个shader的虚拟文件系统,解决该问题。
解决步骤及问题:
- 检查是否支持拓展
QOpenGLExtraFunctions 子类中,添加拓展支持相关逻辑,代码大致如下。
bool DMaterial::ExtetionSupport(QString sCheckExteion)
{
static QList<QString> szExteions;
if (szExteions.count() <= 0)
{
GLint nEAmount;
glGetIntegerv(GL_NUM_EXTENSIONS, &nEAmount);
for (int i = 0; i < nEAmount; i++) {
const char* sExName = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i));
qDebug() << sExName;
szExteions.append(sExName);
}
}
//QString sCheckExteion = "GL_ARB_shading_language_include";
if (szExteions.contains(sCheckExteion))
return true;
return false;
}
for 循环,统计当前支持的拓展
如果支持该拓展,继续往下,不支持,暂时没有其他方案。
- 虚拟文件系统,添加包含的文件
QByteArray szInShaderPath = sFPath.toLatin1();
const char* glslName = szInShaderPath.data();
QFile shaderReader(tr("Shaders/%1").arg(glslName));
shaderReader.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream sReader(&shaderReader);
sReader.setCodec("UTF-8");
QString sInclude = sReader.readAll();
qDebug() << sInclude;
QByteArray szShaderCode = sInclude.toLatin1();
const char* glslContent = szShaderCode.data();
m_pIncludeARB->glNamedStringARB(GL_SHADER_INCLUDE_ARB, strlen(glslName), glslName, strlen(glslContent), glslContent);
通过上述代码将,包含的文件添加到虚拟文件系统中
注:文件必须以"/" 开始,(这块容易遇到问题,之前没有以"/" 开头,glGetError 一致报 1281错误)。
如上,glsl 中可以通过include包含文件。
- “/” 开头导致的新问题
由于 glsl 中,include 的 文件 要与 “/” 开始,导致 visual studio glsl 支持的的插件,会报红,找不到引用的文件。如下图:
思考:如上,虽然运行,不会有错,甚至可以使用其他编辑器,但是膈应人。
是不是可以读取shader 后,替换 include 内容,手动添加 “/”.
修改 shader初始化部分代码,提炼include 包含接口,每次加载初始化shader 时,动态添加include 的shader。同时为include 的文件名,添加 “/” 前置。
shader include 文件接口如下
QList<QString> DMaterial::szIncludeShaders;
QOpenGLExtension_ARB_shading_language_include* DMaterial::m_pIncludeARB = nullptr;
void DMaterial::InitIncludeShaders(QString sFPath)
{
if (m_pIncludeARB == nullptr)
{
m_pIncludeARB = new QOpenGLExtension_ARB_shading_language_include();
bool bRet = m_pIncludeARB->initializeOpenGLFunctions();
if (!bRet)
{
SafeDelete(m_pIncludeARB);
return;
}
}
if (szIncludeShaders.contains(sFPath)) return;
QByteArray szInShaderPath = sFPath.toLatin1();
const char* glslName = szInShaderPath.data();
QFile shaderReader(tr("Shaders/%1").arg(glslName));
shaderReader.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream sReader(&shaderReader);
//sReader.open(QIODevice::ReadOnly | QIODevice::Text);
sReader.setCodec("UTF-8");
QString sInclude = sReader.readAll();
qDebug() << sInclude;
//QByteArray szShaderCode = shaderReader.readAll();
QByteArray szShaderCode = sInclude.toLatin1();
//qDebug() << shaderReader.errorString();
//qDebug() << szShaderCode;
//szShaderCode.data();
const char* glslContent = szShaderCode.data();
//qDebug() << glGetError();
//glslContent = "uniform mat4 _ShadowMat;\r\n";
m_pIncludeARB->glNamedStringARB(GL_SHADER_INCLUDE_ARB, strlen(glslName), glslName, strlen(glslContent), glslContent);
//pTestInclude->glNamedStringARB(GL_SHADER_INCLUDE_ARB, strlen(glslName), glslName,glslContent.count(), glslContent.data());
//qDebug() << glGetError();
bool bRet = m_pIncludeARB->glIsNamedStringARB(strlen(glslName), glslName);
if (bRet)
szIncludeShaders.append(sFPath);
}
shader 初始化,由文件名加载,修改为 读取shader 内容后,查找,添加,以及调整 include 文件格式。
原代码
QList<DShaderInfo> DMaterial::szDefaultShader = {
{ QOpenGLShader::Vertex, "Shaders/Default.vs.glsl",nullptr },
{ QOpenGLShader::Fragment, "Shaders/Default.fs.glsl",nullptr },
};
if (m_szShaders.count() <= 0)
m_szShaders = DMaterial::szDefaultShader;
m_nGlSProgram.create();
bool bLoadShaderFailed = false;
for (QList<DShaderInfo>::iterator it = m_szShaders.begin(); it != m_szShaders.end(); it++)
{
it->pShader = new QOpenGLShader(it->eType,this);
if (it->pShader->compileSourceFile(it->sFileName))
m_nGlSProgram.addShader(it->pShader);
else
{
qDebug() << "Shader " << it->sFileName << " compilation failed : " << it->pShader->log();
bLoadShaderFailed = true;
break;
}
}
if (!bLoadShaderFailed)
{
if (!m_nGlSProgram.link())
{
bLoadShaderFailed = true;
qDebug() << "Shader Program link failed : " << m_nGlSProgram.log();
}
}
修改后加载代码:
if (m_szShaders.count() <= 0)
m_szShaders = DMaterial::szDefaultShader;
m_nGlSProgram.create();
bool bLoadShaderFailed = false;
for (QList<DShaderInfo>::iterator it = m_szShaders.begin(); it != m_szShaders.end(); it++)
{
it->pShader = new QOpenGLShader(it->eType,this);
QFile shaderReader(it->sFileName);
shaderReader.open(QIODevice::ReadOnly);
QByteArray szShaderCode = shaderReader.readAll();
int nStartIndex = szShaderCode.indexOf("#include");
while (nStartIndex != -1)
{
int nEndIndex = szShaderCode.indexOf("\n", nStartIndex);
QByteArray sFile = szShaderCode.mid(nStartIndex + 9, nEndIndex - nStartIndex - 10);
int nFilsePath = szShaderCode.indexOf("\"", nStartIndex);
if (szShaderCode[nFilsePath+1] != '/')
{
szShaderCode.insert(nFilsePath+1, "/");
sFile = "/" + sFile;
}
sFile = sFile.replace(" ", "");
sFile = sFile.replace("\"", "");
PrintLog(sFile);
DMaterial::InitIncludeShaders(sFile);
nStartIndex = szShaderCode.indexOf("#include", nEndIndex);
}
//if (it->pShader->compileSourceFile(it->sFileName))
QString szCode = szShaderCode;
if (it->pShader->compileSourceCode(szCode))
m_nGlSProgram.addShader(it->pShader);
else
{
QString sErrLog = it->pShader->log();
qDebug() << "Shader " << it->sFileName << " compilation failed : " << sErrLog;
bLoadShaderFailed = true;
break;
}
}
以上,处理后,可以比较好的再glsl 中使用#include “fileNam.glsl” 引用
参考 文档连接:https://blog.csdn.net/linjf520/article/details/107525793