为什么 fgetc() 读取非 ASCII 字符?(尝试加载 GLSL 着色器)

d__*_*d__ 2 c++ opengl glsl non-ascii-characters

我希望我的程序为我画一个简单的红色三角形。顶点和片段着色器应该通过我的 loadShader() 函数从外部文件加载,但由于某种奇怪的原因,我的 loadShader() 函数正在读取非 ASCII 字符,这样就会生成着色器编译错误。

尝试按照此处提供的说明(使用 Notepad++)将我的两个着色器文件转换为 ASCII 格式失败,因为结果是相同的——即关于非 ASCII 字符的着色器编译器错误(参见下面的屏幕截图)和白色而不是预期的红色三角形(由于着色器未编译)。

进一步的故障排除尝试:

(注意:我另外上传了我的源代码到Pastebin以便于引用行号。)

关键代码部分从 14 到 44 —— 我的 loadShader 函数。从第 22 行开始的“告诉文件大小”部分工作正常,如下面的屏幕截图所示,因为我的调试输出(第 25 行)与 Windows 资源管理器提供的文件大小具有相同的字节数。此外,缓冲区(第 28 行)与第 41 行的调试输出中所证明的着色器文件大小完全对应(参见屏幕截图)。最后,我的两个着色器的语法是正确的,因为我之前对它们进行了硬编码,结果是所需的红色三角形渲染。

截屏:

在此处输入图片说明

源代码:

// Expected result: Draws a simple red colored triangle to the screen
// Problem to debug: Why does my loadShader function read non-ASCII characters?

#include <glad/glad.h>
#define GLFW_DLL
#include <GLFW\glfw3.h>
#include <cstdio>
#include <iostream>

// TODO: Debug
/* Loads shader text files from a given file name (extension required) 
 * and returns the shader code as a null terminated string from that file. 
 */
const char * loadShader(const char * shaderFileName) {
    FILE * shaderFile{};
    fopen_s(&shaderFile, shaderFileName, "r");
    if (!shaderFile) {
        std::cerr << "ERROR: Cannot open file" << std::endl;
        return "\0";
    }
    // Tell file size
    fseek(shaderFile, 0L, SEEK_END);
    unsigned long shaderFileSize{};
    shaderFileSize = ftell(shaderFile);
    std::cout << "DEBUG: shaderFileSize: " << shaderFileSize << std::endl; // Debug output
    rewind(shaderFile);
    // Read from file
    char * buffer = (char *)malloc(sizeof(char)*(shaderFileSize+1UL));
    if (!buffer) {
        std::cerr << "ERROR: Failed to allocate memory" << std::endl;
        return "\0";
    }
    int c{};
    int i = 0;
    while ((c = fgetc(shaderFile))!= EOF) {
        buffer[i++] = c;
    }
    // Put '\0' at the end of the buffer (required for OpenGL)
    buffer[shaderFileSize] = '\0';
    std::cout << "DEBUG: buffer: " << buffer << std::endl; // Debug output
    std::cout << "DEBUG: strlen: " << strlen(buffer) << std::endl; // Debug output
    fclose(shaderFile);
    return buffer;
} // end of loadShader() 

int main() {
    // Initialize GLFW
    if (!glfwInit()) {
        std::cerr << "ERROR: Failed to initialize GLFW3" << std::endl;
        return -1;
    }
    // Create window
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    GLFWwindow* window = glfwCreateWindow(640, 480, "OpenGL Game", nullptr, nullptr);
    if (!window) {
        std::cerr << "ERROR: Failed to create window with GLFW3" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    // Load all OpenGL function pointers.
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cerr << "ERROR: Failed to initialize GLAD" << std::endl;
        return -1;
    }
    // Get info from renderer
    const GLubyte* rendererName = glGetString(GL_RENDERER);
    const GLubyte* OpenGLVersionSupported = glGetString(GL_VERSION);
    std::cout << rendererName << std::endl << OpenGLVersionSupported << std::endl;
    // Enable depth
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    // Define triangle
    GLfloat points[] = { 0.0f, 0.5f, 0.0f,
                        0.5f, -0.5f, 0.0f,
                        -0.5f, -0.5f, 0.0f };
    // Create buffer object
    GLuint vertexBufferObject = 0;
    glGenBuffers(1, &vertexBufferObject);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
    glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
    // Create vertex attribute object
    GLuint vertexAttributeObject = 0;
    glGenVertexArrays(1, &vertexAttributeObject);
    glBindVertexArray(vertexAttributeObject);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    // Load shaders
    const char * vertexShaderCode = loadShader("VertexShader.glsl");
    const char * fragmentShaderCode = loadShader("FragmentShader.glsl");
    // Compile shaders
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderCode, nullptr);
    glCompileShader(vertexShader);
    // Check vertex shader for compile errors
    int success = 0;
    char message[512] = "";
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, 512, nullptr, message);
        std::cerr << "ERROR: Failed to compile vertex shader" << std::endl << message;
    }
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderCode, nullptr);
    glCompileShader(fragmentShader);
    // Check fragment shader for compile errors
    success = 0;
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, 512, nullptr, message);
        // TODO: Specify error type in message
        std::cerr << "ERROR: Failed to compile fragment shader" << std::endl << message;
    }
    // Create shader program and link it
    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    // Check for linking errors
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(shaderProgram, 512, nullptr, message);
        // TODO: Specify error type in message
        std::cerr << "ERROR: Failed to link shaders" << std::endl << message;
    }
    // Render loop
    while (!glfwWindowShouldClose(window)) {
        // Wipe the drawing surface clear
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        // Use shader program and vertex attribute object
        glUseProgram(shaderProgram);
        glBindVertexArray(vertexAttributeObject);
        // Draw from the currently bound vertex attribute object
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glfwPollEvents();
        glfwSwapBuffers(window);
    }
    // Exit program 
    glfwTerminate();
    return 0;
} // end of main()
Run Code Online (Sandbox Code Playgroud)

ybu*_*ill 5

0xcd是 MSVC CRT 用来填充未初始化内存的值。发生的情况是您的文件使用\r\n行尾,但您以文本模式打开它,CRT 将它们转换为\n行尾。因此,您读入的buffer字节数少于 返回的大小ftell,因此 的最后一个值i小于shaderFileSize,因此,在写入的最后一个值buffer[i]和空终止符之间有一些未初始化的字节。

相反,将您的代码替换为:

FILE * shaderFile{};
fopen_s(&shaderFile, shaderFileName, "rb"); // <-------- HERE !!!!
fseek(shaderFile, 0L, SEEK_END);
unsigned long shaderFileSize = ftell(shaderFile);
rewind(shaderFile);
char * buffer = (char *)malloc(shaderFileSize+1);
fread(buffer, shaderFileSize, 1, shaderFile);
buffer[shaderFileSize] = '\0';
Run Code Online (Sandbox Code Playgroud)