使用std :: ifstream读取文件以编译OpenGL着色器不起作用

时间:2016-01-24 14:25:24

标签: c++ opengl gdb fstream ifstream

这是我的小功能,我遇到了麻烦:

params

我已按照this指南操作,我设法正确渲染白色三角形。在那之后,我认为将整个事情分解为函数以便以后重用它可能是一个很好的练习。因此,我将两个着色器的着色器源代码放在单独的文件中(而不是直接在我的C ++源代码中),并编写上面的函数从各自的文件中读取它们。

现在这里是捕获:我的顶点着色器加载和编译很好,而我的片段着色器没有。但是,当我将片段着色器源放入代码中时,就像这样

GLuint LoadShaders()
{
    // open and compile vertex shader
    std::ifstream VSS("glfwtest.vert", std::ifstream::ate);
    int len = VSS.tellg() + 1;
    GLchar* vertexSource = new GLchar[len];
    VSS.clear();
    VSS.seekg(0, VSS.beg);
    for(int i=0; i<len ; i++) { VSS.get(vertexSource[i]); }

    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexSource, NULL);
    glCompileShader(vertexShader);
    VSS.close();

    // open and compile the fragment shader
    std::ifstream FSS("glfwtest.frag", std::ifstream::ate);
    len = FSS.tellg() + 1;
    GLchar* fragmentSource = new GLchar[len];
    FSS.clear();
    FSS.seekg(0, FSS.beg);
    for(int i=0; i<len ; i++) { FSS.get(fragmentSource[i]); }

    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
    glCompileShader(fragmentShader);
    FSS.close();

    // Link the vertex and fragment shader into a shader program
    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glBindFragDataLocation(shaderProgram, 0, "outColor");
    glLinkProgram(shaderProgram);
    return shaderProgram;
}

它工作正常并显示一个白色三角形。因此,从“glfwtest.vert”加载和编译顶点着色器的相同代码对片段着色器不起作用。

我之前从未在C ++中使用过const GLchar* fragmentSource = "#version 150 core\n" "out vec4 outColor;" "void main()" "{" " outColor = vec4(1.0, 1.0, 1.0, 1.0);" "}"; GLuint LoadShaders() { [...] // open and compile the fragment shader //std::ifstream FSS("glfwtest.frag", std::ifstream::ate); //len = FSS.tellg() + 1; //GLchar* fragmentSource = new GLchar[len]; //FSS.clear(); //FSS.seekg(0, FSS.beg); //for(int i=0; i<len ; i++) { FSS.get(fragmentSource[i]); } GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentSource, NULL); glCompileShader(fragmentShader); [...] return shaderProgram; } ,而是从我找到的online documentation example中将它放在一起。我猜错误是在那里,但我找不到它,因为我不是一个C ++向导(还)。

修改

在评论中的建议之后我仍然卡住了。这是我得到的gdb输出:

<fstream>

如您所见,长度正确存储在(gdb) break main.cpp:55 Breakpoint 1 at 0x4017dc: file main.cpp, line 55. (gdb) r Starting program: /home/georg/code/redbook/glfwtest/glfwtest [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1". Breakpoint 1, LoadShaders () at main.cpp:56 56 GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); (gdb) p vertexSource $1 = (GLchar *) 0x8277e0 "#version 420 core\n\nin vec2 position;\n\nvoid main()\n{\n gl_Position = vec4(position, 0.0, 1.0);\n}" (gdb) p fragmentSource $2 = (GLchar *) 0x82a300 "#version 150 core\nout vec4 outColor;\nvoid main()\n{\n outColor = vec4(1.0, 1.0, 1.0, 1.0);\n}\377\177" (gdb) p len $3 = 92 (gdb) 中,源中有92个字符。不过,不知何故还有两个额外的字符。我甚至用十六进制编辑器检查了源文件,这两个字符肯定不在文件中。

2 个答案:

答案 0 :(得分:0)

虽然这个答案没有调试你的代码,但你表示有兴趣让你的例程更加模块化。这里有一些更强大的功能,可以使用std::stringstream,并消除了显式的动态内存管理:

static std::string read_shader_file (const char *shader_file)
{
    // no feedback is provided for stream errors / exceptions.

    std::ifstream file (shader_file);
    if (!file) return std::string ();

    file.ignore(std::numeric_limits<std::streamsize>::max());
    auto size = file.gcount();

    if (size > 0x10000) // 64KiB sanity check for shaders:
        return std::string ();

    file.clear();
    file.seekg(0, std::ios_base::beg);

    std::stringstream sstr;
    sstr << file.rdbuf();
    file.close();

    return sstr.str();
}

std::string是可移动的,因此您无需担心以“值”返回的成本 - 尽管与任何情况下的GL着色器编译和链接相比都是微不足道的。

GLuint LoadShaders ()
{
    ...

    std::string shader_source = read_shader_file("glfwtest.vert");

    const char *shader_source_ptr = shader_source.c_str();
    glShaderSource(vertexShader, 1, & shader_source_ptr, NULL);
    ...
}

答案 1 :(得分:0)

@derhass在评论中已经诊断出问题,但由于OP仍然存在实际修复代码的问题:

在将字符串传递给glShaderSource()之前,您需要确保字符串以空值终止。如果你不这样做,字符串末尾会有一个未定义的字符,几乎任何事情都可能发生:

  • 如果角色恰好是'\0',它可以工作。
  • 它可能会根据无效字符给出语法错误。
  • 如果在可访问内存结束之前没有找到空字符,它可能会崩溃。

要使用最少的代码更改来修复它,请使用:

std::ifstream VSS("glfwtest.vert", std::ifstream::ate);
int len = VSS.tellg();
GLchar* vertexSource = new GLchar[len + 1];
VSS.clear();
VSS.seekg(0, VSS.beg);
for(int i=0; i<len ; i++) { VSS.get(vertexSource[i]); }
vertexSource[len] = '\0';

片段着色器也是一样。