我正在开发一个使用OpenGL 4.0着色器的项目。
我必须向glShaderSource()调用一个char数组数组,它代表着色器的来源。
着色器编译失败,出现以下错误:
(0) : error C0206: invalid token "<null atom>" in version line
(0) : error C0000: syntax error, unexpected $end at token "<EOF>"
这是我的(hello world)着色器 - 直接来自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 ++代码按预期执行,但着色器编译失败。谁能发现我可能做错了什么?
我有一种感觉,这可能与某些行尾字符有关,但由于这是我第一次尝试着色器编译,我被卡住了!
我已经在着色器编译中阅读了其他SO答案,但它们似乎特定于Java /其他语言,而不是C ++。如果它有帮助,我就在win32平台上。
答案 0 :(得分:15)
你犯了别人犯过的错误。这是glShaderSource
:
void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length);
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 );
如果您从某处复制了此加载代码,我强烈建议您找到different place to learn about OpenGL。因为这是可怕的编码。
答案 1 :(得分:3)
glShaderSource(m_nShaderId,nNumLines,(const GLchar **)ppSrc,(GLint *)pnSrcLineLen);
我知道glShaderSource的签名看起来很容易分别发送着色器的每一行。但现在这就是它的意义所在。能够发送的点是多个阵列,以便可以将多个原始着色器源混合到单个着色器中,类似于包含文件。理解这一点,使得在着色器文件中读取更加简单 - 并避免这种令人讨厌的错误。
使用C ++可以做得更好更清洁。我已经在Getting garbage chars when reading GLSL files
中写了下面的内容你正在使用C ++,所以我建议你利用它。我建议您读入std :: string:
,而不是读入自我分配的char数组#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));
}
答案 2 :(得分:1)
快点预感:
您是否尝试使用NULL作为长度参数调用glShaderSource?在这种情况下,OpenGL将假设您的代码为空终止。
(由于愚蠢而编辑)