使用结构从 .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() 方法为您提供指向对象内部字符串数据的指针。如上所述,当 allLinesshaderReader 构造函数结束时超出范围时,此数据将被释放,因此您最终会得到一个指向已释放内存的指针。这会导致未定义的行为,因为此内存现在可用于其他用途。

解决此问题的最简洁方法是始终如一地使用 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 风格的字符串和内存管理操心。