为啥当我的着色器文件的所有信息都正确读入字符串并且语法正确时,OpenGL会给出语法错误

Posted

技术标签:

【中文标题】为啥当我的着色器文件的所有信息都正确读入字符串并且语法正确时,OpenGL会给出语法错误【英文标题】:Why does OpenGL Give a syntax error when all the info of my shader file is read properly into a string and is syntactically correct为什么当我的着色器文件的所有信息都正确读入字符串并且语法正确时,OpenGL会给出语法错误 【发布时间】:2015-12-13 10:10:29 【问题描述】:

我正在学习 learnopengl.com 上的教程,他们的着色器已直接放入 const char* 中。当我尝试将相同的代码放入着色器文件中,然后将其读入 const char* 时,所有代码都相同,glGetShaderiv() 会生成语法错误。

这是错误:ERROR: 0:1: '' 语法错误:非法扩展 ASCII 字符 (0xdd)。

但是,将代码直接放入 const char* 时不会出现此错误

这是 const char* 代码与我的代码

常量字符*:

    const GLchar* vertexShaderSource = "#version 330 core\n"
    "layout (location = 0) in vec3 position;\n"
    "void main()\n"
    "\n"
    "gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
    "\0";

const GLchar* orangeFragmentShaderSource = "#version 330 core\n"
    "out vec4 color;\n"
    "void main()\n"
    "\n"
    "color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
    "\n\0";

const GLchar* yellowFragmentShaderSource = "#version 330 core\n"
    "out vec4 color;\n"
    "void main()\n"
    "\n"
    "color = vec4(1.0f, 1.0f, 0.0f, 1.0f); // The color yellow \n"
    "\n\0";

我的顶点文件:

      #version 330 core

      layout (location = 0) in vec3 position;

      void main() 

      gl_Position = vec4(position.x, position.y, position.z, 1.0);

   

我的黄色片段着色器:

      #version 430 core

      out vec4 colour;

      void main() 

      colour = vec4(1.0f, 1.0f, 0.0f, 1.0f);

  

我的橙色片段着色器:

     #version 430 core

    out vec4 colour;

    void main() 

    colour = vec4(1.0f, 1.0f, 0.0f, 1.0f);

 

还有,这是我用来将着色器转换为字符串的代码:

      std::ifstream shaderFile(filePath);
      std::stringstream tmp;

      std::string fileContents = "";
      std::string line = "";

      while (std::getline(shaderFile, line)) fileContents += line + "\n";

      return fileContents + "\0";

这是我尝试从文件中读取的另一种方法

   std::ifstream shaderFile(filePath);

   if (!shaderFile.good()) 
       std::cout << "File failed to load..." << filePath << std::endl;
       std::system("PAUSE");
       std::exit(1);
   

  return std::string(std::istreambuf_iterator<char>(shaderFile), std::istreambuf_iterator<char>());

我的着色器编译代码:

tmp = convertShaders("VertexShader.vert");
const GLchar *vertexShaderSource = tmp.c_str();
std::cout << vertexShaderSource << std::endl;

tmp = convertShaders("FragmentShaderYellow.frag");
const GLchar *yellowFragmentShaderSource = tmp.c_str();

tmp = convertShaders("FragmentShaderOrange.frag");
const GLchar *orangeFragmentShaderSource = tmp.c_str();

glShaderSource(vertexShaderID, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShaderID);
glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &success);

glShaderSource(yellowFragmentShaderID, 1, &yellowFragmentShaderSource, NULL);
glCompileShader(yellowFragmentShaderID);
glGetShaderiv(yellowFragmentShaderID, GL_COMPILE_STATUS, &success);


glShaderSource(orangeFragmentShaderID, 1, &orangeFragmentShaderSource, NULL);
glCompileShader(orangeFragmentShaderID);
glGetShaderiv(orangeFragmentShaderID, GL_COMPILE_STATUS, &success);

我不知道为什么我的字符串不起作用当我将字符串输出到控制台时,它看起来都一样。有人可以帮我吗?

【问题讨论】:

如何将 std::string 传递给glShaderSource 循环遍历字符串的字符。它们是否包含任何非 ASCII 字符?检查值 127 对于无符号字符)。如果您从网站复制文本,它可能包含看起来像完全有效字符的非 ASCII 字符,例如,有时它们会使用花哨的双向引号。 FWIW,0xdd 是 Microsoft(可能还有其他人)使用的字节模式,用于指示已释放的内存块。 BDL 是对的,您需要在调用 glShaderSource 的地方发布代码,您几乎可以肯定是错误地传入了字符串。 无论何时覆盖tmp,字符串内存都会被释放。因此vertexShaderSourceyellowFragmentShaderSource 指向无效地址。 @GallifreyDW:了解指针的工作原理。在您的情况下,vertexShaderSourceyellowFragmentShaderSource 均无效,因为它们指向的内存不再有效,因为它们指向的缓冲区由您 修改tmp 对象管理> 接过指针后。 【参考方案1】:

当您调用 tmp.c_str() 时,您会收到一个指向 tmp 拥有的字符串的指针。

当你调用 tmp = something_else;分配了新的内存块来保存新字符串,并且指针(例如 vertexShaderSource)现在无效。它被称为悬空指针。

一个简单的解决办法是改变:

tmp = convertShaders("VertexShader.vert");
const GLchar *vertexShaderSource = tmp.c_str();
std::cout << vertexShaderSource << std::endl;

tmp = convertShaders("FragmentShaderYellow.frag");
const GLchar *yellowFragmentShaderSource = tmp.c_str();

tmp = convertShaders("FragmentShaderOrange.frag");
const GLchar *orangeFragmentShaderSource = tmp.c_str();

到:

tmp = convertShaders("VertexShader.vert");
const GLchar *vertexShaderSource = tmp.c_str();
std::cout << vertexShaderSource << std::endl;

std::string tmp2 = convertShaders("FragmentShaderYellow.frag");
const GLchar *yellowFragmentShaderSource = tmp2.c_str();

std::string tmp3 = convertShaders("FragmentShaderOrange.frag");
const GLchar *orangeFragmentShaderSource = tmp3.c_str();

通过使用 3 个不同的 tmp std::string 可以避免旧内容失效的问题。

【讨论】:

以上是关于为啥当我的着色器文件的所有信息都正确读入字符串并且语法正确时,OpenGL会给出语法错误的主要内容,如果未能解决你的问题,请参考以下文章

Opengl 未使用着色器正确绘制 - 矩阵设置/初始化问题?

我的OpenGL学习进阶之旅 C++ 长行字符串多行书写的方法以及如何书写正确的OpenGL Shader着色器代码

使用着色器绘制 3D 对象的正确方法是啥?

为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?

顶点着色器中属性的位置不正确

我的OpenGL学习进阶之旅 C++ 长行字符串多行书写的方法以及如何书写正确的OpenGL Shader着色器代码