我将测试申请发送给了几个人。第一个测试人员的结果与我的相同,但是其他两个结果却有些奇怪。由于某种原因,图像应该以原始尺寸显示在左下角,因此它们被拉伸为全屏显示。此外,他们也看不到GUI元素(但是,如果通过鼠标找到按钮,它们将起作用)。我将保留一点,即该图像不是与按钮重叠的拉伸图像,我给它们发送了带有透明图像的版本,并且按钮仍未绘制。对于GUI绘图,我使用Nuklear库。我将提供负责定位问题图像的屏幕截图和代码。是什么原因造成的?
[Good behavior / Bad behavior]
int width, height;
{
fs::path const path = fs::current_path() / "gamedata" / "images" / "logo.png";
unsigned char *const texture = stbi_load(path.u8string().c_str(), &width, &height, nullptr, STBI_rgb_alpha);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
stbi_image_free(texture);
}
...
{
float const x = -1.0f + width * 2.0f / xResolution;
float const y = -1.0f + height * 2.0f / yResolution;
float const vertices[30] = {
/* Position */ /* UV */
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, y, 0.0f, 0.0f, 1.0f,
x, y, 0.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
x, -1.0f, 0.0f, 1.0f, 0.0f,
x, y, 0.0f, 1.0f, 1.0f
};
glBufferData(GL_ARRAY_BUFFER, 30 * sizeof(float), vertices, GL_STATIC_DRAW);
}
[更新]
通过反复试验,我意识到问题是由负责渲染背景和徽标的类引起的,并且两者都是错误的。它们可以分别正常工作,但是一旦在游戏循环中添加了其他内容,一切就会崩溃。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
background.render();
glEnable(GL_BLEND);
glDepthMask(GL_FALSE);
logo.render();
nk_glfw3_render();
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
我自己写了这些课,所以很可能我错过了一些东西。发现一个错误之后,还有另一个错误,因为它们几乎相同。到目前为止,我还无法确定,这些类中到底有什么错误……
答案 0 :(得分:1)
我在发布的代码中发现了一些错误。最后,我将以戏剧性的方式揭示罪魁祸首。
例如,
glBindTexture(GL_TEXTURE_2D, NULL); // Incorrect
glBindTexture函数接受整数参数,而不是指针。这是正确的版本:
glBindTexture(GL_TEXTURE_2D, 0); // Correct
这也适用于glBindBuffer
和glBindVertexArray
。
explicit
关键字仅影响一元构造函数(采用一个参数的构造函数)。它不会影响具有任何其他数量参数的构造函数。
explicit Background() noexcept; // "explicit" does not do anything.
Background() noexcept; // Exact same declaration as above.
noexcept
关键字的意思是“此函数永远不会抛出异常”。但是,它包含以下可能抛出的代码:
new Shader("image")
根据标准,这可能引发std::bad_alloc
。因此,noexcept
注释不正确。参见Can the C++ `new` operator ever throw an exception in real life?
更实际地讲,构造函数从磁盘读取图像。这很可能会失败,并且抛出异常是处理此错误的合理方法。
noexcept
关键字在这里不是特别有用。也许编译器可以在调用位置生成较少的代码,但这可能最多产生无穷小的差别,因为构造函数是冷代码(冷=不经常调用)。 noexcept
限定符主要用于在不同的通用算法之间进行选择,请参见What is noexcept useful for?
请记住,如果发生错误,stdbi_load
将返回NULL
。这种情况无法处理。
Background
类没有定义副本构造函数或副本赋值运算符,即使它定义了析构函数。尽管不能保证这会使您的程序不正确,但这就像在厨房柜台上放了一把翘起的枪,希望没有人碰它。这称为Rule of Three,而且很容易修复。添加一个已删除的副本构造函数和副本分配运算符。
// These three go together, either define all of them or none.
// Hence, "rule of three".
Background(const Background &) = delete;
Background &operator=(const Background &) = delete;
~Background();
以下是一行:
glDeleteBuffers(1, &VBO);
简短版本...您应该将其移至Background::~Background()
。
长版本...删除缓冲区时,不会将其从VAO中删除,但是名称可以立即重用。根据{{3}} 5.1.2:
删除缓冲区,纹理或渲染缓冲区对象后,它会与绑定到当前上下文的容器对象的任何附件分离……。
因此,由于VAO当前未绑定,因此删除VBO不会将VBO从VAO中删除(如果绑定了VAO,则情况会有所不同)。但是,第5.1.3节:
当删除缓冲区,纹理,采样器,渲染缓冲区,查询或同步对象时,其名称立即变为无效(例如,标记为未使用),但是直到不再使用基础对象时,才会删除该对象。 / p>
因此,VBO将保留,但是 name 可以重用。这意味着以后调用glGenBuffers
可能会给您相同的名称。然后,当您调用glBufferData
时,它将覆盖背景和徽标中的数据。或者,glGenBuffers
可能会给您一个完全不同的缓冲区名称。这完全取决于实现,这解释了为什么您在不同的计算机上看到不同的行为。
通常,在使用完缓冲区之前,我将避免调用glDeleteBuffers
。从技术上讲,您可以更早地调用glDeleteBuffers
,但这意味着您可以从glGenBuffers
返回相同的缓冲区。