为什么 fgetc() 读取非 ASCII 字符? (尝试加载 GLSL 着色器)
Why is fgetc() reading a non-ASCII character? (Trying to load GLSL shaders)
我希望我的程序能给我画一个简单的红色三角形。顶点和片段着色器应该通过我的 loadShader() 函数从外部文件加载,但由于某些奇怪的原因,我的 loadShader() 函数正在读取非 ASCII 字符,因此会生成着色器编译错误。
按照 here 提供的说明(使用 Notepad++)尝试将我的两个着色器文件转换为 ASCII 格式失败,因为结果相同——即关于非ASCII 字符(见下面的屏幕截图)和一个白色三角形而不是预期的红色三角形(由于着色器未编译)。
进一步的故障排除尝试:
(注意:我另外将我的源代码上传到 Pastebin 以便于引用行号。)
关键代码部分从 14 变为 44 -- 我的 loadShader 函数。
从第 22 行开始的 "tell file size" 部分工作正常,如下面的屏幕截图所示,因为我的调试输出(第 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 "[=10=]";
}
// 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 "[=10=]";
}
int c{};
int i = 0;
while ((c = fgetc(shaderFile))!= EOF) {
buffer[i++] = c;
}
// Put '[=10=]' at the end of the buffer (required for OpenGL)
buffer[shaderFileSize] = '[=10=]';
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()
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] = '[=10=]';
我希望我的程序能给我画一个简单的红色三角形。顶点和片段着色器应该通过我的 loadShader() 函数从外部文件加载,但由于某些奇怪的原因,我的 loadShader() 函数正在读取非 ASCII 字符,因此会生成着色器编译错误。
按照 here 提供的说明(使用 Notepad++)尝试将我的两个着色器文件转换为 ASCII 格式失败,因为结果相同——即关于非ASCII 字符(见下面的屏幕截图)和一个白色三角形而不是预期的红色三角形(由于着色器未编译)。
进一步的故障排除尝试:
(注意:我另外将我的源代码上传到 Pastebin 以便于引用行号。)
关键代码部分从 14 变为 44 -- 我的 loadShader 函数。 从第 22 行开始的 "tell file size" 部分工作正常,如下面的屏幕截图所示,因为我的调试输出(第 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 "[=10=]";
}
// 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 "[=10=]";
}
int c{};
int i = 0;
while ((c = fgetc(shaderFile))!= EOF) {
buffer[i++] = c;
}
// Put '[=10=]' at the end of the buffer (required for OpenGL)
buffer[shaderFileSize] = '[=10=]';
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()
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] = '[=10=]';