在运行时glsl着色器编译问题

我正在开发一个使用OpenGL 4.0着色器的项目。

我必须用char数组的数组来提供对glShaderSource()的调用,该数组代表着色器的来源。

着色器编译失败,出现以下错误:

(0) : error C0206: invalid token "<null atom>" in version line (0) : error C0000: syntax error, unexpected $end at token "<EOF>" 

这是我的(你好,世界)着色器 – 直接从OpenGL 4.0着色语言食谱

 #version 400 in vec3 VertexPosition; in vec3 VertexColor; out vec3 Color; void main() { Color = VertexColor; gl_Position = vec4( VertexColor, 1.0 ); } 

下面是我的代码,将着色器文件读入我的C ++代码,并在运行时编译着色器:

 const int nMaxLineSize = 1024; char sLineBuffer[nMaxLineSize]; ifstream stream; vector<string> vsLines; GLchar** ppSrc; GLint* pnSrcLineLen; int nNumLines; stream.open( m_sShaderFile.c_str(), std::ios::in ); while( (stream.good()) && (stream.getline(sLineBuffer, nMaxLineSize)) ) { if( strlen(sLineBuffer) > 0 ) vsLines.push_back( string(sLineBuffer) ); } stream.close(); nNumLines = vsLines.size(); pnSrcLineLen = new GLint[nNumLines]; ppSrc = new GLchar*[nNumLines]; for( int n = 0; n < nNumLines; n ++ ) { string & sLine = vsLines.at(n); int nLineLen = sLine.length(); char * pNext = new char[nLineLen+1]; memcpy( (void*)pNext, sLine.c_str(), nLineLen ); pNext[nLineLen] = '\0'; ppSrc[n] = pNext; pnSrcLineLen[n] = nLineLen+1; } vsLines.clear(); // just for debugging purposes (lines print out just fine..) for( int n = 0; n < nNumLines; n ++ ) ATLTRACE( "line %d: %s\r\n", n, ppSrc[n] ); // Create the shader m_nShaderId = glCreateShader( m_nShaderType ); // Compile the shader glShaderSource( m_nShaderId, nNumLines, (const GLchar**)ppSrc, (GLint*) pnSrcLineLen ); glCompileShader( m_nShaderId ); // Determine compile status GLint nResult = GL_FALSE; glGetShaderiv( m_nShaderId, GL_COMPILE_STATUS, &nResult ); 

C ++代码按预期执行,但着色器编译失败。 任何人都可以发现我可能做错了吗?

我有一种感觉,这可能是以某种方式结束行字符,但因为这是我在着色器编译的第一次尝试,我卡住了!

我已经阅读了其他关于着色器编译的答案,但它们看起来特定于Java /其他语言,而不是C ++。 如果有帮助,我在win32平台上。

只是一个快速的预感:

你有没有尝试调用glShaderSource与NULL作为长度参数? 在这种情况下,OpenGL会假定你的代码是以null结尾的。

(由于愚蠢编辑)

你犯了一个别人犯的错误。 这是glShaderSource的定义:

 void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length); 

string是一个string数组。 它并不打算在着色器中成为一行数组。 编译器解释这个字符串数组的方式是将它们连在一起。 没有换行符。

由于stream.getline 不会在字符串中放置\n字符,因此您生成的每个着色器字符串在末尾都不会有换行符。 因此,当glShaderSource去编译它们时,着色器将如下所示:

 #version 400in vec3 VertexPosition;in vec3 VertexColor;out vec3 Color;... 

这不是合法的GLSL。

正确的方法是将文件作为字符串加载。

 std::ifstream shaderFile(m_sShaderFile.c_str()); if(!shaderFile) //Error out here. std::stringstream shaderData; shaderData << shaderFile.rdbuf(); //Loads the entire string into a string stream. shaderFile.close(); const std::string &shaderString = shaderData.str(); //Get the string stream as a std::string. 

那么你可以轻松地将它传递给glShaderSource

 m_nShaderId = glCreateShader( m_nShaderType ); const char *strShaderVar = shaderString.c_str(); GLint iShaderLen = shaderString.size(); glShaderSource( m_nShaderId, 1, (const GLchar**)&strShaderVar, (GLint*)&iShaderLen ); glCompileShader( m_nShaderId ); 

如果你从某处复制了这个加载代码,那么我强烈建议你找一个不同的地方来学习OpenGL 。 因为这是可怕的编码。

glShaderSource(m_nShaderId,nNumLines,(const GLchar **)ppSrc,(GLint *)pnSrcLineLen);

我知道glShaderSource的签名看起来很诱人,分别发送着色器的每一行。 但现在就是这个意思。 能够发送的点是多个数组,以便可以将多个原始着色器源混合到单个着色器中,类似于包含文件。 了解这一点,使得在着色器文件中读取更简单 – 并避免这种令人讨厌的错误。

使用C ++,你可以做得更好,更干净。 在阅读GLSL文件时,我已经在“ 获取垃圾字符”中编写了这个follwing


你正在使用C ++,所以我建议你利用它。 而不是读入一个自分配的字符数组,我建议你读入一个std ::字符串:

 #include <string> #include <fstream> std::string loadFileToString(char const * const fname) { std::ifstream ifile(fname); std::string filetext; while( ifile.good() ) { std::string line; std::getline(ifile, line); filetext.append(line + "\n"); } return filetext; } 

这将自动处理所有的内存分配和正确的分隔 – 关键字RAII:资源分配是初始化。 稍后,您可以上传类似的着色器源代码

 void glcppShaderSource(GLuint shader, std::string const &shader_string) { GLchar const *shader_source = shader_string.c_str(); GLint const shader_length = shader_string.size(); glShaderSource(shader, 1, &shader_source, &shader_length); } 

你可以像这样一起使用这两个函数:

 void load_shader(GLuint shaderobject, char * const shadersourcefilename) { glcppShaderSource(shaderobject, loadFileToString(shadersourcefilename)); }