读取文件,数据损坏

3

看起来我无法编译我的GLSL着色器。有时(主要是在编辑文件后),我在编译时会遇到以下错误:

----- SRC ----- (150 B)
#version 330 core

uniform mat4 mvpMatrix;

in vec4 vertexPosition_modelspace;

void main() {
    gl_Position = mvpMatrix * vertexPosition_modelspace;
}
gp!
----- END -----
SimpleTransform.vertexshader:Vertex shader failed to compile with the following errors:
ERROR: 0:10: error(#132) Syntax error: 'gp' parse error
ERROR: error(#273) 1 compilation errors.  No code generated

这很奇怪,因为我发誓文件中不包含那个尴尬的 gp! 部分。尽管如此,我用 cat 进行了调查。

#version 330 core

uniform mat4 mvpMatrix;

in vec4 vertexPosition_modelspace;

void main() {
    gl_Position = mvpMatrix * vertexPosition_modelspace;
}

并且 更少

#version 330 core

uniform mat4 mvpMatrix;

in vec4 vertexPosition_modelspace;

void main() {
    gl_Position = mvpMatrix * vertexPosition_modelspace;
}

他们两个都证明了我的正确性。

我想知道是什么导致了这种奇怪的行为。

这是一个链接到我的项目的链接。您应该能够通过进入src目录并输入make(仅适用于Linux)轻松编译它。它需要GLFW,GLEW,GLM和GL3。

而且代码本身:

加载着色器文件

GLuint shader_load(GLenum type, const char filename[]) {
    if ((type != GL_VERTEX_SHADER && type != GL_FRAGMENT_SHADER) || !filename) return 0;

    /* wczytywanie pliku shadera */
    FILE *file = fopen(filename, "rb"); 

    //okreslenie rozmiaru pliku
    fseek(file, 0, SEEK_END);   
    uint32 iFileSize = ftell(file);
    fseek(file, 0, SEEK_SET);

    //wczytywanie
    char *tmp = new char[iFileSize];
    memset(tmp, 0, sizeof(tmp));
    uint32 iBytes = (uint32) fread(tmp, sizeof(char), iFileSize, file); 
    fclose(file);   
    if (iBytes != iFileSize) printf("Warning: reading error possible!\n");

    #ifdef _DEBUG_
    printf("----- SRC ----- (%d B)\n%s\n----- END -----\n", iBytes, tmp);
    #endif

    /* przygotowanie shadera */
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, const_cast<const GLchar**>(&tmp), NULL);
    delete[] tmp;
    glCompileShader(shader); //kompilacja shadera

    /* sprawdzenie statusu kompilacji */
    int status = GL_FALSE; 
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);  
    int logsize = 0;
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logsize);
    char *log = new char[logsize];
    glGetShaderInfoLog(shader, logsize, NULL, log);
    printf("%s:%s", filename, log);         
    delete[] log;
    if (status != GL_TRUE)  return 0;

    return shader;
}

请在此展示您相关的代码,您会获得比等待某人下载和深入研究您的项目更好的响应。我猜测问题可能出在着色器字符串没有以 null 结尾。 - Tim
不要将你的项目发送给我,使用一些复杂的平台相关的构建过程来构建它。只需发布相关代码(很可能是着色器加载代码)。这将持续存在,即使你删除了你的项目并且链接在下周失效了。 - Christian Rau
@ChristianRau 添加了“加载着色器”的源代码。 - Robin92
此外,如果您使用gDEBugger,您可以查看每个着色器实际发送到图形卡的代码。(并非所有图形驱动程序在报告错误消息时都会显示着色器源代码) - fintelia
添加了一个示例片段,展示了如何使用std::string而不是搞乱原始数组、memset、偏移量和复杂的转换来编写该片段。 - sehe
我会从我在早期回答中发布的类开始,并添加代码以从文件中读取数据并传递给编译器进行编译。 - Jerry Coffin
1个回答

6

首先,建议使用C++而不是C-with-a-cpp扩展名,以避免类似这样的灾难。

分析:


在valgrind下运行显示:

==15579== Invalid read of size 1
==15579==    at 0x5B95C65: vfprintf (vfprintf.c:1623)
==15579==    by 0x5B9E768: printf (printf.c:35)
==15579==    by 0x4019C1: shader_load(unsigned int, char const*) (shaders.cpp:88)
==15579==    by 0x401B30: program_create(char const*, char const*) (shaders.cpp:120)
==15579==    by 0x401D65: main (in /tmp/ogl-jg-3/test)
==15579==  Address 0xb3018a6 is 0 bytes after a block of size 150 alloc'd
==15579==    at 0x4C2864B: operator new[](unsigned long) (vg_replace_malloc.c:305)
==15579==    by 0x401961: shader_load(unsigned int, char const*) (shaders.cpp:81)
==15579==    by 0x401B30: program_create(char const*, char const*) (shaders.cpp:120)
==15579==    by 0x401D65: main (in /tmp/ogl-jg-3/test)

这段代码告诉我们它试图读取缓冲区tmp的末尾,但是在第81行中分配时并没有以null结尾。你似乎假定它是以null结尾的。为了解决问题,请添加:

//wczytywanie
char *tmp = new char[iFileSize+1];
memset(tmp, 0, (iFileSize+1)*sizeof(char));
uint32 iBytes = (uint32) fread(tmp, sizeof(char), iFileSize, file); 
fclose(file);   
if (iBytes != iFileSize) printf("Warning: reading error possible!\n");

#ifdef _DEBUG_
    printf("----- SRC ----- (%d B)\n%s\n----- END -----\n", iBytes, tmp);
#endif

我得到了半可接受的输出。但GL窗口仍然是空白的。

更新

为了更清楚地表达我所谓的切换到C++,这是我的想法:

GLuint shader_load(GLenum type, const char filename[]) {
    if ((type != GL_VERTEX_SHADER && type != GL_FRAGMENT_SHADER) || !filename) return 0;

    GLuint shader = glCreateShader(type);
    std::string src;
    {
        /* wczytywanie pliku shadera */
        std::ifstream ifs(filename, std::ios::binary);
        if (!std::getline(ifs, src, '\0'))
            std::cerr << "Warning: reading error possible!\n";
    }

#ifdef _DEBUG_
    std::cout << "----- SRC ----- " << src.size() << " B \n" << src << "\n----- END -----\n";
#endif

    /* przygotowanie shadera */
    const GLchar* sources[] = { src.c_str() };
    glShaderSource(shader, 1, sources, NULL);
    glCompileShader(shader); //kompilacja shadera

@datenwolf 我从不做图形编程,也不了解 API :) 感谢你的有用提示。 - sehe
1
当你转向C++时,为什么要使用C文件之类的东西,而不是只做一个简单的 std::istream file(filename); std::string tmp(std::istreambuf_iterator<char>(file), (std::istreambuf_iterator()));,而不是整个 freadresize 的混乱(更不用说显式的 fclose)? 你的代码并不比他的更少是C/C++。 - Christian Rau
你确定文件末尾包含一个 0 吗? - Christian Rau
@ChristianRau 嗯?如果你的意思是读取将在文件末尾停止,那我肯定是这样认为的 :) 我也确定嵌入的 NUL 字符不会(有意义地)出现,因为它们会终止源代码(strlen 在其上调用,正如 valgrind 告诉我的)。所以......_相当安全_。比原来的代码更安全,更短,我想说。 - sehe
@sehe 是的,你说得对,当然 getline 会在结尾停止,NUL 可能不在其中,这很有道理。 - Christian Rau
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接