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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8024433/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 18:24:27  来源:igfitidea点击:

glsl shader compilation issue at runtime

c++windowsopenglglsl

提问by Nicol Bolas

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.

我必须向glShaderSource()提供一个 char 数组数组,它​​代表着色器的源。

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 着色语言食谱

#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] = '
void glShaderSource(GLuint shader,  GLsizei count,  const GLchar **string,  const GLint *length);
'; 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 平台上。

采纳答案by Shelling

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)

(因愚蠢而编辑)

回答by Nicol Bolas

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

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

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

The stringis an array of strings. It is notintended 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.

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

Since stream.getlinewill notput the \ncharacter in the string, each of the shader strings you generate will not have a newline at the end. Therefore, when glShaderSourcegoes to compile them, your shader will look like this:

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

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.

That's not legal GLSL.

这不是合法的 GLSL。

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

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

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 );

Then you can just pass that along to glShaderSourceeasily enough:

然后你可以glShaderSource很容易地将它传递给:

#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;
}

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。因为那是糟糕的编码。

回答by datenwolf

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:

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);
}

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 load_shader(GLuint shaderobject, char * const shadersourcefilename)
{
    glcppShaderSource(shaderobject, loadFileToString(shadersourcefilename));
}

You can use those two functions together like this:

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

##代码##