使用结构从 .txt 文件读取着色器
Reading a shader from a .txt file using a structure
我已经开始学习如何使用 OpenGL,我非常讨厌在 main() 之前将我的着色器声明为全局变量。
我认为制作一个结构或 class 可以从我的项目目录中的文件中读取着色器会很酷。文件读取工作得很好,但由于某种原因,它实际上不会像我在 main 之前声明着色器那样输出图像。这是我的例子。
着色器读取结构:
#include "allHeader.h"
struct shaderReader
{
shaderReader::shaderReader(std::string);
const GLchar* source;
};
shaderReader::shaderReader(std::string name)
{
std::string line, allLines;
std::ifstream theFile(name);
if (theFile.is_open())
{
while (std::getline(theFile, line))
{
allLines = allLines + line + "\n";
}
source = allLines.c_str();
std::cout << source;
theFile.close();
}
else
{
std::cout << "Unable to open file.";
}
}
main() 之前的区域快照
shaderReader vertexShader = shaderReader("vertex.txt");
shaderReader fragmentShader = shaderReader("fragment.txt");
const GLchar* vertexSource =
"#version 150 core\n"
"in vec2 position;"
"void main() {"
" gl_Position = vec4(position, 0.0, 1.0);"
"}";
const GLchar* fragmentSource =
"#version 150 core\n"
"out vec4 outColor;"
"void main() {"
" outColor = vec4(1.0, 1.0, 1.0, 1.0);"
"}";
int main()
{
//stuff
}
有效
GLuint vertexShaderObject = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderObject, 1, &vertexSource, NULL);
glCompileShader(vertexShaderObject);
//Now let's create the Fragment Shader Object
GLuint fragmentShaderObject = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShaderObject, 1, &fragmentSource, NULL);
glCompileShader(fragmentShaderObject);
由于某些原因无效
const GLchar* ob1 = vertexShader.source;
const GLchar* ob2 = fragmentShader.source;
GLuint vertexShaderObject = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderObject, 1, &ob1, NULL);
glCompileShader(vertexShaderObject);
//Now let's create the Fragment Shader Object
GLuint fragmentShaderObject = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShaderObject, 1, &ob2, NULL);
glCompileShader(fragmentShaderObject);
也不行
GLuint vertexShaderObject = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderObject, 1, &vertexShader.source, NULL);
glCompileShader(vertexShaderObject);
//Now let's create the Fragment Shader Object
GLuint fragmentShaderObject = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShaderObject, 1, &fragmentShader.source, NULL);
glCompileShader(fragmentShaderObject);
上面的代码在工作时在黑屏中间打印出一个白色三角形。但是,当不工作时,我只会看到黑屏。我尝试检查着色器是否存在编译错误,但没有发现任何错误。我想也许结构有问题,我没有做对。感谢您阅读我的问题,如果您有类似的问题希望我的问题对您有所帮助。
改变
source = allLines.c_str();
进入
source = new char[allLines.length()+1];
strcpy(source,allLines.c_str());
并且在shaderReader的析构函数中,释放分配给source的内存
if(source)
{
delete[] source;
source = nullptr;
}
参考:
我很想将您的着色器存储在 std::vector<GLchar>
中,这样您就不必担心分配和取消分配内存:
也许是这样的:
struct shaderReader
{
shaderReader(std::string);
std::vector<GLchar> source;
const GLchar* data;
GLint size;
};
shaderReader::shaderReader(std::string name)
{
std::string line, allLines;
std::ifstream theFile(name);
if(theFile.is_open())
{
while(std::getline(theFile, line))
{
allLines = allLines + line + "\n";
}
source.assign(allLines.begin(), allLines.end());
size = source.size();
data = source.data();
theFile.close();
}
else
{
std::cout << "Unable to open file.";
}
}
int main()
{
shaderReader sr("fragment-shader.txt");
GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(shader, 1, &sr.data, &sr.size);
}
您混合使用了 C++ (std::string
) 和 C (char*
) 字符串。
在构造函数中,您将在 C++ string
中构建代码,它是一个对象,可以随着字符串的增长自动分配和重新分配内存以保存字符串数据。当数据在构造函数结束时超出范围时,它也会自动释放该数据。
问题的根源在这里:
source = allLines.c_str();
其中 source
是 class/struct 成员。 string
上的 c_str()
方法为您提供指向对象内部字符串数据的指针。如上所述,当 allLines
在 shaderReader
构造函数结束时超出范围时,此数据将被释放,因此您最终会得到一个指向已释放内存的指针。这会导致未定义的行为,因为此内存现在可用于其他用途。
解决此问题的最简洁方法是始终如一地使用 C++ 字符串。将结构定义更改为:
struct shaderReader
{
shaderReader::shaderReader(std::string);
std::string source;
};
然后在构造函数中,可以直接把源码读入这个class成员:
while (std::getline(theFile, line))
{
source = source + line + "\n";
}
您不得不为 char*
烦恼的唯一原因是 glShaderSource()
需要一个。最安全的做法是在最后一刻转换:
const GLchar* ob1 = vertexShader.source.c_str();
GLuint vertexShaderObject = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderObject, 1, &ob1, NULL);
glCompileShader(vertexShaderObject);
这样,您只需非常临时地使用指向内部 string
数据的指针,而不必为 C 风格的字符串和内存管理操心。
我已经开始学习如何使用 OpenGL,我非常讨厌在 main() 之前将我的着色器声明为全局变量。
我认为制作一个结构或 class 可以从我的项目目录中的文件中读取着色器会很酷。文件读取工作得很好,但由于某种原因,它实际上不会像我在 main 之前声明着色器那样输出图像。这是我的例子。
着色器读取结构:
#include "allHeader.h"
struct shaderReader
{
shaderReader::shaderReader(std::string);
const GLchar* source;
};
shaderReader::shaderReader(std::string name)
{
std::string line, allLines;
std::ifstream theFile(name);
if (theFile.is_open())
{
while (std::getline(theFile, line))
{
allLines = allLines + line + "\n";
}
source = allLines.c_str();
std::cout << source;
theFile.close();
}
else
{
std::cout << "Unable to open file.";
}
}
main() 之前的区域快照
shaderReader vertexShader = shaderReader("vertex.txt");
shaderReader fragmentShader = shaderReader("fragment.txt");
const GLchar* vertexSource =
"#version 150 core\n"
"in vec2 position;"
"void main() {"
" gl_Position = vec4(position, 0.0, 1.0);"
"}";
const GLchar* fragmentSource =
"#version 150 core\n"
"out vec4 outColor;"
"void main() {"
" outColor = vec4(1.0, 1.0, 1.0, 1.0);"
"}";
int main()
{
//stuff
}
有效
GLuint vertexShaderObject = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderObject, 1, &vertexSource, NULL);
glCompileShader(vertexShaderObject);
//Now let's create the Fragment Shader Object
GLuint fragmentShaderObject = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShaderObject, 1, &fragmentSource, NULL);
glCompileShader(fragmentShaderObject);
由于某些原因无效
const GLchar* ob1 = vertexShader.source;
const GLchar* ob2 = fragmentShader.source;
GLuint vertexShaderObject = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderObject, 1, &ob1, NULL);
glCompileShader(vertexShaderObject);
//Now let's create the Fragment Shader Object
GLuint fragmentShaderObject = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShaderObject, 1, &ob2, NULL);
glCompileShader(fragmentShaderObject);
也不行
GLuint vertexShaderObject = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderObject, 1, &vertexShader.source, NULL);
glCompileShader(vertexShaderObject);
//Now let's create the Fragment Shader Object
GLuint fragmentShaderObject = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShaderObject, 1, &fragmentShader.source, NULL);
glCompileShader(fragmentShaderObject);
上面的代码在工作时在黑屏中间打印出一个白色三角形。但是,当不工作时,我只会看到黑屏。我尝试检查着色器是否存在编译错误,但没有发现任何错误。我想也许结构有问题,我没有做对。感谢您阅读我的问题,如果您有类似的问题希望我的问题对您有所帮助。
改变
source = allLines.c_str();
进入
source = new char[allLines.length()+1];
strcpy(source,allLines.c_str());
并且在shaderReader的析构函数中,释放分配给source的内存
if(source)
{
delete[] source;
source = nullptr;
}
参考:
我很想将您的着色器存储在 std::vector<GLchar>
中,这样您就不必担心分配和取消分配内存:
也许是这样的:
struct shaderReader
{
shaderReader(std::string);
std::vector<GLchar> source;
const GLchar* data;
GLint size;
};
shaderReader::shaderReader(std::string name)
{
std::string line, allLines;
std::ifstream theFile(name);
if(theFile.is_open())
{
while(std::getline(theFile, line))
{
allLines = allLines + line + "\n";
}
source.assign(allLines.begin(), allLines.end());
size = source.size();
data = source.data();
theFile.close();
}
else
{
std::cout << "Unable to open file.";
}
}
int main()
{
shaderReader sr("fragment-shader.txt");
GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(shader, 1, &sr.data, &sr.size);
}
您混合使用了 C++ (std::string
) 和 C (char*
) 字符串。
在构造函数中,您将在 C++ string
中构建代码,它是一个对象,可以随着字符串的增长自动分配和重新分配内存以保存字符串数据。当数据在构造函数结束时超出范围时,它也会自动释放该数据。
问题的根源在这里:
source = allLines.c_str();
其中 source
是 class/struct 成员。 string
上的 c_str()
方法为您提供指向对象内部字符串数据的指针。如上所述,当 allLines
在 shaderReader
构造函数结束时超出范围时,此数据将被释放,因此您最终会得到一个指向已释放内存的指针。这会导致未定义的行为,因为此内存现在可用于其他用途。
解决此问题的最简洁方法是始终如一地使用 C++ 字符串。将结构定义更改为:
struct shaderReader
{
shaderReader::shaderReader(std::string);
std::string source;
};
然后在构造函数中,可以直接把源码读入这个class成员:
while (std::getline(theFile, line))
{
source = source + line + "\n";
}
您不得不为 char*
烦恼的唯一原因是 glShaderSource()
需要一个。最安全的做法是在最后一刻转换:
const GLchar* ob1 = vertexShader.source.c_str();
GLuint vertexShaderObject = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderObject, 1, &ob1, NULL);
glCompileShader(vertexShaderObject);
这样,您只需非常临时地使用指向内部 string
数据的指针,而不必为 C 风格的字符串和内存管理操心。