这是我遇到的小功能:
GLuint LoadShaders()
{
// open and compile vertex shader
std::ifstream VSS("glfwtest.vert", std::ifstream::ate);
int len = VSS.tellg() + 1;
GLchar* vertexSource = new GLchar[len];
VSS.clear();
VSS.seekg(0, VSS.beg);
for(int i=0; i<len ; i++) { VSS.get(vertexSource[i]); }
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, NULL);
glCompileShader(vertexShader);
VSS.close();
// open and compile the fragment shader
std::ifstream FSS("glfwtest.frag", std::ifstream::ate);
len = FSS.tellg() + 1;
GLchar* fragmentSource = new GLchar[len];
FSS.clear();
FSS.seekg(0, FSS.beg);
for(int i=0; i<len ; i++) { FSS.get(fragmentSource[i]); }
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
glCompileShader(fragmentShader);
FSS.close();
// Link the vertex and fragment shader into a shader program
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glBindFragDataLocation(shaderProgram, 0, "outColor");
glLinkProgram(shaderProgram);
return shaderProgram;
}
我遵循了this指南,我设法正确渲染了白色三角形。在那之后,我认为将整个内容分解为功能以供以后重用可能是一个不错的练习。因此,我将两个着色器的着色器源代码放在单独的文件中(而不是直接在C ++源代码中),并编写了上面的函数以从各自的文件中读取它们。
现在是问题所在:我的顶点着色器可以很好地加载和编译,而片段着色器却可以。但是,当我将片段着色器源代码放入这样的代码中时
const GLchar* fragmentSource =
"#version 150 core\n"
"out vec4 outColor;"
"void main()"
"{"
" outColor = vec4(1.0, 1.0, 1.0, 1.0);"
"}";
GLuint LoadShaders()
{
[...]
// open and compile the fragment shader
//std::ifstream FSS("glfwtest.frag", std::ifstream::ate);
//len = FSS.tellg() + 1;
//GLchar* fragmentSource = new GLchar[len];
//FSS.clear();
//FSS.seekg(0, FSS.beg);
//for(int i=0; i<len ; i++) { FSS.get(fragmentSource[i]); }
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
glCompileShader(fragmentShader);
[...]
return shaderProgram;
}
它工作正常,并显示一个白色三角形。因此,从“ glfwtest.vert”加载并编译我的顶点着色器的同一代码不适用于片段着色器。
我以前从未使用过C ++中的<fstream>
,并将其与我发现的online documentation example放在一起。我猜该错误在某个地方,但由于我还不是C ++向导,所以我找不到它。
编辑:
在评论中的建议之后,我仍然很困惑。这是我得到的gdb输出:
(gdb) break main.cpp:55
Breakpoint 1 at 0x4017dc: file main.cpp, line 55.
(gdb) r
Starting program: /home/georg/code/redbook/glfwtest/glfwtest
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
Breakpoint 1, LoadShaders () at main.cpp:56
56 GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
(gdb) p vertexSource
$1 = (GLchar *) 0x8277e0 "#version 420 core\n\nin vec2 position;\n\nvoid main()\n{\n gl_Position = vec4(position, 0.0, 1.0);\n}"
(gdb) p fragmentSource
$2 = (GLchar *) 0x82a300 "#version 150 core\nout vec4 outColor;\nvoid main()\n{\n outColor = vec4(1.0, 1.0, 1.0, 1.0);\n}\377\177"
(gdb) p len
$3 = 92
(gdb)
您可以看到,长度正确存储在len
中,源中有92个字符。尽管如此,不知何故还有两个额外的字符。即使使用十六进制编辑器,我也检查了源文件,这两个字符肯定不在文件中。
(请跳过以获取最新的C ++ 17解决方案)
尽管此答案不能调试代码,但是您对使例程更具模块化性表示了兴趣。这是使用std::stringstream
并消除显式动态内存管理的更强大的功能:
static std::string read_shader_file (const char *shader_file)
{
// no feedback is provided for stream errors / exceptions.
std::ifstream file (shader_file);
if (!file) return std::string ();
file.ignore(std::numeric_limits<std::streamsize>::max());
auto size = file.gcount();
if (size > 0x10000) // 64KiB sanity check for shaders:
return std::string ();
file.clear();
file.seekg(0, std::ios_base::beg);
std::stringstream sstr;
sstr << file.rdbuf();
file.close();
return sstr.str();
}
[std::string
是可移动的,因此您不必担心通过'value'返回的成本-尽管与GL着色器的编译和链接在任何情况下相比都是微不足道的。
GLuint LoadShaders ()
{
...
std::string shader_source = read_shader_file("glfwtest.vert");
const char *shader_source_ptr = shader_source.c_str();
glShaderSource(vertexShader, 1, & shader_source_ptr, NULL);
...
}
我正在查看一些较早的答案,并意识到我已经更新了基于此答案的自己的glShader::load
代码。首先,我们现在有一个[[portable路径:std::filesystem::path
,例如在Windows上允许使用UTF-16(?)文件名编码(例如wchar_t字符串)。
std::wifstream
。从C ++ 17开始,std::ifstream
接受(std::filesystem::path)
或(...path::value_type *)
作为构造函数或open
方法。参见:path通过为ifstream
启用异常,我们可以希望捕获std::ios_base::failure
异常,如果只需要诊断std::runtime_error
消息,则该异常继承what()
。否则,code()
可能会提供更多信息-规范中没有说明诊断信息将如何提供信息...
无论如何,这是将GLSL文件读取到std::string
的惯用方式:
#include <filesystem>
#include <limits>
#include <fstream>
#include <iterator>
static std::string read_shader_file
(
const std::filesystem::path::value_type *shader_file)
{
std::ifstream ifs;
auto ex = ifs.exceptions();
ex |= std::ios_base::badbit | std::ios_base::failbit;
ifs.exceptions(ex);
ifs.open(shader_file);
ifs.ignore(std::numeric_limits<std::streamsize>::max());
auto size = ifs.gcount();
if (size > 0x10000) // 64KiB sanity check:
return false;
ifs.clear();
ifs.seekg(0, std::ios_base::beg);
return std::string {std::istreambuf_iterator<char> {ifs}, {}});
}
[发生异常时,资源将被取消缠绕,并且(可能)将引发std::ios_base::failure
异常。否则,我们有一个std::string
,现在我们可以使用c_str()
方法将着色器源传递给OpenGL。
[在将字符串传递给glShaderSource()
之前,需要确保该字符串以null结尾。如果您不这样做,则字符串末尾会有一个未定义的字符,几乎任何事情都可能发生:
'\0'
,它可能会起作用。std::ifstream VSS("glfwtest.vert", std::ifstream::ate);
int len = VSS.tellg();
GLchar* vertexSource = new GLchar[len + 1];
VSS.clear();
VSS.seekg(0, VSS.beg);
for(int i=0; i<len ; i++) { VSS.get(vertexSource[i]); }
vertexSource[len] = '\0';
与片段着色器相同。