运行时glsl着色器编译问题

时间:2021-02-01 14:57:04

I'm working on a project that uses OpenGL 4.0 shaders.

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

I have to supply the call to glShaderSource() with an array of char arrays, which represents the source of the shader.

我必须使用char数组数组向glShaderSource()提供调用,这些数组表示着色器的来源。

The shader compilation is failing, with the following errors:

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

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

Here's my (hello world) shader - straight from OpenGL 4.0 shading language cookbook

这是我的(hello world)着色器 - 直接来自OpenGL 4.0着色语言cookbook

#version 400

in  vec3        VertexPosition;
in  vec3        VertexColor;
out vec3        Color;

void main()
{
    Color       = VertexColor;
    gl_Position = vec4( VertexColor, 1.0 );
}

And here's my code to read the shader file into my C++ code, and compile the shader at runtime:

这是我的代码,用于将着色器文件读入我的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 );

The C++ code executes as expected, but the shader compilation fails. Can anyone spot what I might be doing wrong?

C ++代码按预期执行,但着色器编译失败。谁能发现我可能做错了什么?

I have a feeling that this may be to do with end of line characters somehow, but as this is my first attempt at shader compilation, I'm stuck!

我有一种感觉,这可能与某些行尾字符有关,但由于这是我第一次尝试着色器编译,我被卡住了!

I've read other SO answers on shader compilation, but they seem specific to Java / other languages, not C++. If it helps, I'm on the win32 platform.

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

3 个解决方案

#1


1  

Just a quick hunch:

只是一个快速的预感:

Have you tried calling glShaderSource with NULL as length parameter? In that case OpenGL will assume your code to be null-terminated.

您是否尝试使用NULL作为长度参数调用glShaderSource?在这种情况下,OpenGL将假定您的代码为空终止。

(Edited because of stupidity)

(由于愚蠢而编辑)

#2


14  

You have made a mistake that others have made. This is the definition of glShaderSource:

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

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

The string is an array of strings. It is not intended to be an array of lines in your shader. The way the compiler will interpret this array of strings is by concatenating them together, one after another. Without newlines.

字符串是一个字符串数组。它不是您的着色器中的线阵列。编译器解释这个字符串数组的方式是将它们一个接一个地连接在一起。没有换行符。

Since stream.getline will not put the \n character in the string, each of the shader strings you generate will not have a newline at the end. Therefore, when glShaderSource goes to compile them, your shader will look like this:

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

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

That's not legal GLSL.

那不是合法的GLSL。

The proper way to do this is to load the file as a string.

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

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.

Then you can just pass that along to glShaderSource easily enough:

然后你可以轻松地将它传递给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 );

If you copied this loading code from somewhere, then I strongly suggest you find a different place to learn about OpenGL. Because that's terrible coding.

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

#3


2  

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

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

I know the signature of glShaderSource looks tempting to send each line of the shader separately. But that's now what it's meant for. The point of being able to send is multiple arrays is so that one can mix multiple primitive shader sources into a single shader, kind of like include files. Understanding this, makes it much simpler to read in a shader file – and avoids such nasty bugs.

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

Using C++ you can do it much nicer and cleaner. I already wrote the follwing in Getting garbage chars when reading GLSL files

使用C ++,你可以做得更好,更清洁。我在阅读GLSL文件时已经写过了获取垃圾字符的内容


You're using C++, so I suggest you leverage that. Instead of reading into a self allocated char array I suggest you read into a std::string:

你正在使用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;
}

That automatically takes care of all memory allocation and proper delimiting -- the keyword is RAII: Resource Allocation Is Initialization. Later on you can upload the shader source with something like

这会自动处理所有内存分配和正确分隔 - 关键字是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);
}

You can use those two functions together like this:

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

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

#1


1  

Just a quick hunch:

只是一个快速的预感:

Have you tried calling glShaderSource with NULL as length parameter? In that case OpenGL will assume your code to be null-terminated.

您是否尝试使用NULL作为长度参数调用glShaderSource?在这种情况下,OpenGL将假定您的代码为空终止。

(Edited because of stupidity)

(由于愚蠢而编辑)

#2


14  

You have made a mistake that others have made. This is the definition of glShaderSource:

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

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

The string is an array of strings. It is not intended to be an array of lines in your shader. The way the compiler will interpret this array of strings is by concatenating them together, one after another. Without newlines.

字符串是一个字符串数组。它不是您的着色器中的线阵列。编译器解释这个字符串数组的方式是将它们一个接一个地连接在一起。没有换行符。

Since stream.getline will not put the \n character in the string, each of the shader strings you generate will not have a newline at the end. Therefore, when glShaderSource goes to compile them, your shader will look like this:

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

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

That's not legal GLSL.

那不是合法的GLSL。

The proper way to do this is to load the file as a string.

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

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.

Then you can just pass that along to glShaderSource easily enough:

然后你可以轻松地将它传递给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 );

If you copied this loading code from somewhere, then I strongly suggest you find a different place to learn about OpenGL. Because that's terrible coding.

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

#3


2  

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

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

I know the signature of glShaderSource looks tempting to send each line of the shader separately. But that's now what it's meant for. The point of being able to send is multiple arrays is so that one can mix multiple primitive shader sources into a single shader, kind of like include files. Understanding this, makes it much simpler to read in a shader file – and avoids such nasty bugs.

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

Using C++ you can do it much nicer and cleaner. I already wrote the follwing in Getting garbage chars when reading GLSL files

使用C ++,你可以做得更好,更清洁。我在阅读GLSL文件时已经写过了获取垃圾字符的内容


You're using C++, so I suggest you leverage that. Instead of reading into a self allocated char array I suggest you read into a std::string:

你正在使用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;
}

That automatically takes care of all memory allocation and proper delimiting -- the keyword is RAII: Resource Allocation Is Initialization. Later on you can upload the shader source with something like

这会自动处理所有内存分配和正确分隔 - 关键字是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);
}

You can use those two functions together like this:

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

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