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

问题描述 投票:0回答:2

这是我遇到的小功能:

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

我遵循了this指南,我设法正确渲染了白色三角形。在那之后,我认为将整个内容分解为功能以供以后重用可能是一个不错的练习。因此,我将两个着色器的着色器源代码放在单独的文件中(而不是直接在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;
}

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

我以前从未使用过C ++中的<fstream>,并将其与我发现的online documentation example放在一起。我猜该错误在某个地方,但由于我还不是C ++向导,所以我找不到它。

编辑:

在评论中的建议之后,我仍然很困惑。这是我得到的gdb输出:

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

您可以看到,长度正确存储在len中,源中有92个字符。尽管如此,不知何故还有两个额外的字符。即使使用十六进制编辑器,我也检查了源文件,这两个字符肯定不在文件中。

c++ opengl gdb fstream ifstream
2个回答
0
投票

(请跳过以获取最新的C ++ 17解决方案)

尽管此答案不能调试代码,但是您对使例程更具模块化性表示了兴趣。这是使用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是可移动的,因此您不必担心通过'value'返回的成本-尽管与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);
    ...
}

C ++ 17

我正在查看一些较早的答案,并意识到我已经更新了基于此答案的自己的glShader::load代码。首先,我们现在有一个[[portable路径:std::filesystem::path,例如在Windows上允许使用UTF-16(?)文件名编码(例如wchar_t字符串)。

第二,GLSL使用UTF-8方案。因此,没有理由使用std::wifstream。从C ++ 17开始,std::ifstream接受(std::filesystem::path)(...path::value_type *)作为构造函数或open方法。参见:path

通过为ifstream启用异常,我们可以希望捕获std::ios_base::failure异常,如果只需要诊断std::runtime_error消息,则该异常继承what()。否则,code()可能会提供更多信息-规范中没有说明诊断信息将如何提供信息...

无论如何,这是将GLSL文件读取到std::string的惯用方式:

#include <filesystem> #include <limits> #include <fstream> #include <iterator> static std::string read_shader_file ( const std::filesystem::path::value_type *shader_file) { std::ifstream ifs; auto ex = ifs.exceptions(); ex |= std::ios_base::badbit | std::ios_base::failbit; ifs.exceptions(ex); ifs.open(shader_file); ifs.ignore(std::numeric_limits<std::streamsize>::max()); auto size = ifs.gcount(); if (size > 0x10000) // 64KiB sanity check: return false; ifs.clear(); ifs.seekg(0, std::ios_base::beg); return std::string {std::istreambuf_iterator<char> {ifs}, {}}); }

[发生异常时,资源将被取消缠绕,并且(可能)将引发std::ios_base::failure异常。否则,我们有一个std::string,现在我们可以使用c_str()方法将着色器源传递给OpenGL。

0
投票
问题已通过注释中的@derhass诊断出来,但是由于OP实际修复代码仍然有问题:

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

    如果字符碰巧是'\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';

    与片段着色器相同。
  • © www.soinside.com 2019 - 2024. All rights reserved.