【问题标题】:OpenTK GL.DrawElements causing GL_INVALID_OPERATION errorOpenTK GL.DrawElements 导致 GL_INVALID_OPERATION 错误
【发布时间】:2015-05-27 09:33:45
【问题描述】:

正如标题中所述,我从 glDrawElements 收到 GL_INVALID_OPERATION 错误,但是,这只发生在 GL 3.1 以上的 GL 上下文中,在 3.1 及以下的上下文中,它可以正常呈现,没有任何问题。我的模型是这样加载的:

public IModel LoadData( Shape a_Shape ) {
        int VertexStride = BlittableValueType<Vertex>.Stride;
        int IndexStride = sizeof ( uint );

        m_Backing = a_Shape;

        m_GLDataBuffer = GL.GenBuffer( );
        GL.BindBuffer( BufferTarget.ArrayBuffer, m_GLDataBuffer );
        GL.BufferData( BufferTarget.ArrayBuffer, ( IntPtr ) ( m_Backing.Vertices.Count * VertexStride ), m_Backing.Vertices.ToArray( ), BufferUsageHint.StaticDraw );

        m_GLIndexBuffer = GL.GenBuffer( );
        GL.BindBuffer( BufferTarget.ElementArrayBuffer, m_GLIndexBuffer );
        GL.BufferData( BufferTarget.ElementArrayBuffer, ( IntPtr ) ( m_Backing.Indices.Count * IndexStride ), m_Backing.Indices.ToArray( ), BufferUsageHint.StaticDraw );

        GL.EnableVertexAttribArray( 0 );
        GL.EnableVertexAttribArray( 1 );
        GL.EnableVertexAttribArray( 2 );
        GL.VertexAttribPointer( 0, 3, VertexAttribPointerType.Float, false, VertexStride, 0 );
        GL.VertexAttribPointer( 1, 3, VertexAttribPointerType.Float, false, VertexStride, ( sizeof ( float ) * 3 ) );
        GL.VertexAttribPointer( 2, 2, VertexAttribPointerType.Float, false, VertexStride, ( sizeof ( float ) * 6 ) );
        GL.DisableVertexAttribArray( 2 );
        GL.DisableVertexAttribArray( 1 );
        GL.DisableVertexAttribArray( 0 );

        GL.BindBuffer( BufferTarget.ArrayBuffer, 0 );
        GL.BindBuffer( BufferTarget.ElementArrayBuffer, 0 );
        return this;
    }

其中 a_Shape 只是从文件加载或由用户定义的任意形状,它包含一个顶点列表,其中仅包含两个 Vector3(位置和法线)和一个 Vector2(TexCoords)。然后我用这个函数绘制模型:

void IModel.Draw( ) {
        GL.BindBuffer( BufferTarget.ElementArrayBuffer, m_GLIndexBuffer );
        GL.EnableVertexAttribArray( 0 );
        GL.EnableVertexAttribArray( 1 );
        GL.EnableVertexAttribArray( 2 );
        GL.DrawElements( PrimitiveType.Triangles, m_Backing.Indices.Count, DrawElementsType.UnsignedInt, 0 ); //This is the line that's producing the Error
        GL.DisableVertexAttribArray( 2 );
        GL.DisableVertexAttribArray( 1 );
        GL.DisableVertexAttribArray( 0 );
        GL.BindBuffer( BufferTarget.ElementArrayBuffer, 0 );
    }

正如我所说,在我尝试使用高于 3.1 版本的 GL 上下文之前,这一切正常。我真的很困惑,因为它在 3.1 以上就无法工作。我查看了它,我在 OpenGL 参考上说:

如果非零缓冲区对象名称绑定到启用的数组或元素数组并且缓冲区对象的数据存储当前已映射,则会生成 GL_INVALID_OPERATION。

不过,我不太明白这意味着什么。所以我想我的问题是:什么可能导致 glDrawElements 只能在某些情况下工作,我该如何解决它?还是我的代码中有一些我没有看到的问题?

任何帮助都将不胜感激,经过大约 5 个小时的努力,我想是时候寻求帮助了。

更新

所以我将我的代码修改为以下内容:

    public IModel LoadData( Shape a_Shape ) {
        m_Backing = a_Shape;

        Single[] Positions = new Single[a_Shape.Vertices.Count * 3];
        Single[] Normals = new Single[a_Shape.Vertices.Count * 3];
        Single[] TexCoords = new Single[a_Shape.Vertices.Count * 2];

        for ( int i = 0; i < Positions.Length; i += 3 ) {
            Positions[i + 0] = a_Shape.Vertices[i / 3].Position.X;
            Positions[i + 1] = a_Shape.Vertices[i / 3].Position.Y;
            Positions[i + 2] = a_Shape.Vertices[i / 3].Position.Z;

            Normals[i + 0] = a_Shape.Vertices[i / 3].Normal.X;
            Normals[i + 1] = a_Shape.Vertices[i / 3].Normal.Y;
            Normals[i + 2] = a_Shape.Vertices[i / 3].Normal.Z;
        }

        for ( int i = 0; i < TexCoords.Length; i += 2 ) {
            TexCoords[i + 0] = a_Shape.Vertices[i / 2].TexCoord.X;
            TexCoords[i + 1] = a_Shape.Vertices[i / 2].TexCoord.Y;
        }

        m_GLVertexArray = GL.GenVertexArray( );
        GL.BindVertexArray( m_GLVertexArray );
        m_GLIndexBuffer = GL.GenBuffer( );
        GL.GenBuffers( 3, m_GLDataBuffers );
        GL.EnableVertexAttribArray( 0 );
        GL.EnableVertexAttribArray( 1 );
        GL.EnableVertexAttribArray( 2 );

        GL.BindBuffer( BufferTarget.ElementArrayBuffer, m_GLIndexBuffer );
        GL.BufferData( BufferTarget.ElementArrayBuffer, ( IntPtr ) ( sizeof ( uint ) * a_Shape.Indices.Count ), a_Shape.Indices.ToArray( ), BufferUsageHint.StaticDraw );

        GL.BindBuffer( BufferTarget.ArrayBuffer, m_GLDataBuffers[0] ); // Bind the Position Buffer
        GL.BufferData( BufferTarget.ArrayBuffer, ( IntPtr ) ( sizeof ( Single ) * Positions.Length ), Positions, BufferUsageHint.StaticDraw );
        GL.VertexAttribPointer( 0, 3, VertexAttribPointerType.Float, false, 0, 0 );

        GL.BindBuffer( BufferTarget.ArrayBuffer, m_GLDataBuffers[1] ); // Bind the Normal Buffer
        GL.BufferData( BufferTarget.ArrayBuffer, ( IntPtr ) ( sizeof ( Single ) * Normals.Length ), Normals, BufferUsageHint.StaticDraw );
        GL.VertexAttribPointer( 1, 3, VertexAttribPointerType.Float, false, 0, 0 );

        GL.BindBuffer( BufferTarget.ArrayBuffer, m_GLDataBuffers[2] ); // Bind the TexCoord Buffer
        GL.BufferData( BufferTarget.ArrayBuffer, ( IntPtr ) ( sizeof ( Single ) * TexCoords.Length ), TexCoords, BufferUsageHint.StaticDraw );
        GL.VertexAttribPointer( 2, 2, VertexAttribPointerType.Float, false, 0, 0 );

        GL.BindBuffer( BufferTarget.ArrayBuffer, 0 );
        GL.BindBuffer( BufferTarget.ElementArrayBuffer, 0 );
        GL.BindVertexArray( 0 );
        return this;
    }

对于加载和:

    void IModel.Draw( ) {
        GL.BindVertexArray( m_GLVertexArray );
        GL.DrawElements( PrimitiveType.Triangles, m_Backing.Indices.Count, DrawElementsType.UnsignedInt, 0 );
        GL.BindVertexArray( 0 );
    }

对于绘图,使用我从 Reto 的评论中可以找到的内容。我不再收到任何 GL 错误,但现在根本没有任何渲染。我又看不到问题出在哪里。

编辑 修复了未正确将顶点转换为位置、法线和 TexCoord 数组的问题。

【问题讨论】:

  • 您可能会获得 3.1 以上版本的核心配置文件上下文,并且您的代码与核心配置文件不兼容。例如:stackoverflow.com/questions/29336227/….
  • @RetoKoradi 我已经使用顶点数组更新了我的代码,但是现在什么都没有渲染。我已经用我当前的代码更新了我的主要帖子。
  • 我刚刚意识到我实际上并没有正确地将数据存储在我的数组中。我将编辑我的主要帖子以解决此问题。
  • 如果支持,您可以请求兼容性配置文件。
  • 我没有看到使用 OpenTK 的方法,我会继续寻找,但这不是我所看到的。

标签: c# opengl opengl-3 opentk


【解决方案1】:

我最初的问题的解决方案是使用顶点数组,因为从 OpenGL 3.1 核心到 OpenGL 3.2 核心的弃用,尝试在没有人的情况下进行渲染会引发 GL_INVALID_OPERATION 错误。之后我遇到了另一个问题,什么都不会渲染,这是我的一个简单错误,没有重新绑定 GL_ELEMENT_ARRAY_BUFFER。主帖中LoadData方法场景正常工作,最终Draw方法如下:

    void IModel.Draw( ) {
        GL.BindVertexArray( m_GLVertexArray );
        GL.BindBuffer( BufferTarget.ElementArrayBuffer, m_GLIndexBuffer ); //Rebinding the GL_ELEMENT_ARRAY_BUFFER solved the second issue.
        GL.DrawElements( PrimitiveType.Triangles, m_Backing.Indices.Count, DrawElementsType.UnsignedInt, 0 );
        GL.BindBuffer( BufferTarget.ElementArrayBuffer, 0 );
        GL.BindVertexArray( 0 );
    }

编辑

经过进一步讨论,我了解到通过在解除绑定 VAO 之前解除绑定 GL_ELEMENT_ARRAY_BUFFER(以下称为 IBO)会导致 VAO 引用 ID 为 0 的 IBO,而不是我想要的 IBO。下面是我正在使用的最终代码。

    public IModel LoadData( Shape a_Shape ) {
        m_Backing = a_Shape;

        Single[] Positions = new Single[a_Shape.Vertices.Count * 3];
        Single[] Normals = new Single[a_Shape.Vertices.Count * 3];
        Single[] TexCoords = new Single[a_Shape.Vertices.Count * 2];

        for ( int i = 0; i < Positions.Length; i += 3 ) {
            Positions[i + 0] = a_Shape.Vertices[i / 3].Position.X;
            Positions[i + 1] = a_Shape.Vertices[i / 3].Position.Y;
            Positions[i + 2] = a_Shape.Vertices[i / 3].Position.Z;

            Normals[i + 0] = a_Shape.Vertices[i / 3].Normal.X;
            Normals[i + 1] = a_Shape.Vertices[i / 3].Normal.Y;
            Normals[i + 2] = a_Shape.Vertices[i / 3].Normal.Z;
        }

        for ( int i = 0; i < TexCoords.Length; i += 2 ) {
            TexCoords[i + 0] = a_Shape.Vertices[i / 2].TexCoord.X;
            TexCoords[i + 1] = a_Shape.Vertices[i / 2].TexCoord.Y;
        }

        m_GLVertexArray = GL.GenVertexArray( );
        GL.BindVertexArray( m_GLVertexArray );
        m_GLIndexBuffer = GL.GenBuffer( );
        GL.GenBuffers( 3, m_GLDataBuffers );

        GL.BindBuffer( BufferTarget.ElementArrayBuffer, m_GLIndexBuffer );
        GL.BufferData( BufferTarget.ElementArrayBuffer, ( IntPtr ) ( sizeof ( uint ) * a_Shape.Indices.Count ), a_Shape.Indices.ToArray( ), BufferUsageHint.StaticDraw );

        GL.BindBuffer( BufferTarget.ArrayBuffer, m_GLDataBuffers[0] ); // Bind the Position Buffer
        GL.BufferData( BufferTarget.ArrayBuffer, ( IntPtr ) ( sizeof ( Single ) * Positions.Length ), Positions, BufferUsageHint.StaticDraw );
        GL.VertexAttribPointer( 0, 3, VertexAttribPointerType.Float, false, 0, 0 );
        GL.EnableVertexAttribArray( 0 );

        GL.BindBuffer( BufferTarget.ArrayBuffer, m_GLDataBuffers[1] ); // Bind the Normal Buffer
        GL.BufferData( BufferTarget.ArrayBuffer, ( IntPtr ) ( sizeof ( Single ) * Normals.Length ), Normals, BufferUsageHint.StaticDraw );
        GL.VertexAttribPointer( 1, 3, VertexAttribPointerType.Float, false, 0, 0 );
        GL.EnableVertexAttribArray( 1 );

        GL.BindBuffer( BufferTarget.ArrayBuffer, m_GLDataBuffers[2] ); // Bind the TexCoord Buffer
        GL.BufferData( BufferTarget.ArrayBuffer, ( IntPtr ) ( sizeof ( Single ) * TexCoords.Length ), TexCoords, BufferUsageHint.StaticDraw );
        GL.VertexAttribPointer( 2, 2, VertexAttribPointerType.Float, false, 0, 0 );
        GL.EnableVertexAttribArray( 2 );

        GL.BindBuffer( BufferTarget.ArrayBuffer, 0 );
        GL.BindVertexArray( 0 );
        GL.BindBuffer( BufferTarget.ElementArrayBuffer, 0 );
        return this;
    }

还有:

void IModel.Draw( ) {
    GL.BindVertexArray( m_GLVertexArray );
    GL.DrawElements( PrimitiveType.Triangles, m_Backing.Indices.Count, DrawElementsType.UnsignedInt, 0 );
    GL.BindVertexArray( 0 );
}

【讨论】:

  • GL_ELEMENT_ARRAY_BUFFER 绑定是 VAO 状态的一部分。因此,如果您在设置 VAO 时绑定它,而在绑定 VAO 时,您不必在 Draw() 方法中再次绑定它。
  • 这就是我的假设,这也是为什么我首先删除了在 draw 中重新绑定它的调用的原因,因为 GL_ELEMENT_ARRAY_BUFFER 的缓冲区正在生成、绑定和填充之后VAO 已生成并绑定,但即便如此我仍然需要重新绑定它。除非我对你所说的感到困惑。
  • 应该可以的。在最初发布的代码中,您还调用了取消绑定GL_ELEMENT_ARRAY_BUFFER。您当然必须删除它,否则存储在 VAO 状态中的绑定将再次重置。
  • 我现在明白了,出于某种原因,我认为我需要取消绑定它,但现在我考虑下次加载模型时它会绑定一个新的 VAO 和 IBO,所以有首先解绑它并没有任何意义。
猜你喜欢
  • 2012-08-14
  • 2020-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-01
  • 2020-10-05
相关资源
最近更新 更多