【问题标题】:glDrawElements crash (OpenGL 3.2 / Windows 7)glDrawElements 崩溃 (OpenGL 3.2 / Windows 7)
【发布时间】:2014-04-12 05:43:39
【问题描述】:

我正在尝试在 OpenGL 3.2 中绘制一个简单的四边形,但是当我调用“glDrawElements”时,应用程序会因“访问冲突读取位置 0x00000000”而崩溃。

我认为问题是顶点缓冲区数据错误,但我不确定如何解决/调试它(OpenGL 跟踪会很棒,但我也不知道如何启用它......)

初始化代码:

std::vector<CUShort> Indices;
const CUShort IndicesArray[] = { 0, 1, 2, 2, 0, 3 };

for(size_t j = 0; j < 1; j++) {
    for(size_t i = 0; i < sizeof(IndicesArray) / sizeof(*IndicesArray); i++) {
        Indices.push_back(4 * j + IndicesArray[i]);
    }
}

glGenBuffers(1, &m_Elements);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Elements);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, Indices.size() * sizeof(CUShort), &Indices[0], GL_STATIC_DRAW);

glGenVertexArrays(1, &m_Array);
glBindVertexArray(m_Array);

glGenBuffers(1, &m_Buffer);
glBindBuffer(GL_ARRAY_BUFFER, m_Buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(TexColorVertex), NULL, GL_DYNAMIC_DRAW);

glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_TRUE, sizeof(TexColorVertex), (const GLvoid*)offsetof(TexColorVertex, Color));

glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(TexColorVertex), (const GLvoid*)offsetof(TexColorVertex, Position));

glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(TexColorVertex), (const GLvoid*)offsetof(TexColorVertex, Texcoord));

glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

绘图代码:

glBindBuffer(GL_ARRAY_BUFFER, m_Buffer);

TexColorVertex Vertices[4];

glm::vec4 SpritePos = glm::vec4(0, 0, 1024.0f, 384.0f);
Vertices[0].Position = glm::vec2(SpritePos.x, SpritePos.y);
Vertices[1].Position = glm::vec2(SpritePos.x, SpritePos.w);
Vertices[2].Position = glm::vec2(SpritePos.z, SpritePos.w);
Vertices[3].Position = glm::vec2(SpritePos.z, SpritePos.y);

Color Kittens = Color::HotPink();
Vertices[0].Color = glm::vec4(Kittens.R(), Kittens.G(), Kittens.B(), 1.0f);
Vertices[1].Color = glm::vec4(Kittens.R(), Kittens.G(), Kittens.B(), 1.0f);
Vertices[2].Color = glm::vec4(Kittens.R(), Kittens.G(), Kittens.B(), 1.0f);
Vertices[3].Color = glm::vec4(Kittens.R(), Kittens.G(), Kittens.B(), 1.0f);

Vertices[0].Texcoord = glm::vec2(0.0f, 0.0f);
Vertices[1].Texcoord = glm::vec2(0.0f, 1.0f);
Vertices[2].Texcoord = glm::vec2(1.0f, 1.0f);
Vertices[3].Texcoord = glm::vec2(1.0f, 0.0f);

glBufferSubData(GL_ARRAY_BUFFER, sizeof(Vertices), sizeof(Vertices), Vertices);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Elements);
glBindVertexArray(m_Array);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const GLvoid*)0);

顶点结构体声明如下:

struct TexColorVertex
{
    TexColorVertex(void) { }
    TexColorVertex(glm::vec2 const& Position, glm::vec2 const& Texcoord) :
        Position(Position), Texcoord(Texcoord)
    { }
    glm::vec2 Position;
    glm::vec2 Texcoord;
    glm::vec4 Color;
};

有没有人对如何解决这个问题并绘制一个占据一半屏幕的简单四边形有任何建议?

【问题讨论】:

  • 您将NULL 作为索引数组发送到glDrawElements
  • @JesusRamos 我相信这是一个偏移量。 Opengl 从当前绑定的元素数组缓冲区中获取索引数据。
  • @Ben 这是绑定到顶点数组的偏移量数组。在这种情况下,它应该是一个包含 6 个索引的数组。

标签: c++ opengl opengl-3 glm-math


【解决方案1】:

虽然user3256930 确实提出了有关缓冲区分配大小的有效点,但这实际上并不是导致崩溃的原因。

问题不是与glBufferSubData (...)有关,而是与glDrawElements (...)的调用有关。此调用试图取消引用 NULL 指针,这是一个危险信号,没有任何东西绑定到 GL_ELEMENT_ARRAY_BUFFER。

当没有任何东西绑定到GL_ELEMENT_ARRAY_BUFFER 时,传递给glDrawElements (...) 的指针是指向客户端内存的实际指针,而不是缓冲区对象(服务器)的偏移量>) 内存。


要了解为什么会发生这种情况,请回忆一下 Vertex Array Objects 存储的内容:

  1. 顶点属性状态[指针,启用/禁用]
  2. 元素数组缓冲区绑定 (GL_ELEMENT_ARRAY_BUFFER)

现在,考虑这两个调用的顺序:

glBindBuffer      (GL_ELEMENT_ARRAY_BUFFER, m_Elements);
glBindVertexArray (m_Array);

首先将一些东西绑定到GL_ELEMENT_ARRAY_BUFFER (m_Elements),然后立即绑定一个顶点数组对象 (m_Array),它会替换您刚刚绑定的元素数组缓冲区使用它在内部跟踪的绑定。

您应该考虑 (1) 使用您的 VAO 永久引用单个元素数组缓冲区或 (2) 颠倒这两个调用的顺序。


如果您的顶点数组对象 (m_Array) 将始终与相同的元素数组缓冲区一起使用,那么我建议您使用第一种方法。这可以通过在初始化中移动以下代码来实现:

glGenVertexArrays (1, &m_Array);
glBindVertexArray (m_Array);

来之前:

glGenBuffers (1, &m_Elements);
glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, m_Elements);
glBufferData (GL_ELEMENT_ARRAY_BUFFER, Indices.size() * sizeof(CUShort), &Indices[0], GL_STATIC_DRAW);

使用这种方法,无需在绘图代码中显式绑定任何内容到 GL_ELEMENT_ARRAY_BUFFER。

【讨论】:

  • 这至少摆脱了崩溃,仍然没有画任何东西,但希望我能弄清楚那部分:)
【解决方案2】:

在调用 glBufferData 时,您没有为顶点分配足够的空间。您传递 sizeof(TexColorVertex) 为 1 个顶点分配空间。如果你想绘制一个四边形,那么你需要为 4 个顶点分配空间,所以你应该改变你对 glBuffer Data 的调用,如下所示:

glBufferData(GL_ARRAY_BUFFER, 4*sizeof(TexColorVertex), NULL, GL_DYNAMIC_DRAW);

这样,当您对 glBufferSubData 进行后续调用时,您将不会尝试访问超出范围的 GPU 内存。

【讨论】:

  • 可能值得一提的是,在这段代码中分配缓冲区时,缓冲区的大小实际上是未知的。出于这个原因,最好在 drawing 代码中使用glBufferData (...) 来orphan 旧缓冲区。没有内存会泄漏,只要管道中没有引用旧内存的未决命令,GL就会释放旧内存。事实上,这可能会提供更好的性能,因为它不需要 OpenGL 停止流水线或复制缓冲区以防止挂起的绘制命令使用错误的数据副本。
  • 这一切都归结为 GL 中称为隐式同步的东西。由于调用glBufferSubData (...) 时来自多个帧的绘制命令可能仍然不完整,为了防止此调用影响之前的帧,GL 必须在幕后进行一些同步。如果不是每帧修改缓冲区的现有内存的数据,您只需分配新内存,这可以防止修改前一帧所需内存的可能性并大大简化事情。再一次,没有任何东西会泄漏,因为在没有命令引用旧内存之后它被释放了。
  • 也非常正确,但由于 Andon M. Coleman 的回答修复了崩溃,我将他的回答标记为答案。感谢您对此的帮助:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-08
相关资源
最近更新 更多