OpenTKOpenGL如何正确使用带有多个对象的BufferSubData和DrawElements

问题描述

我试图实现一个渲染器,该渲染器仅使用一个VBO和一个EBO来存储所有对象的顶点和索引。 为此,我找到了一些教程,最后得出了不错的结果。

问题在于,它只能与一个单一对象一起正常使用。一旦我要添加一个,渲染就会显示出奇怪的行为。

您能帮我吗?

您可以在此处找到完整的代码https://github.com/BanditBloodwyn/TerritorySimulator。 重要的类是:

  • Rendering.Core.Rendering.Renderer.cs
  • Rendering.Core.Classes.Shapes.GLShape.cs
  • Rendering.Core.RenderGUI.RenderGUI.cs

这是渲染器中的初始化方法

    public void Initialize(GLShape[] shapeArray)
    {
        Shapes = shapeArray;

        GL.Enable(EnableCap.DepthTest);
        GL.ClearColor(0.0f,0.0f,0.10f,1.0f);

        InitializeBuffers(Shapes);
        InitializeVertexArrayObject(Shapes);
        SetupShader();
        BindBuffers();
    }

方法如下。

    private void InitializeBuffers(GLShape[] shapeArray)
    {
        int vertexBufferSize = shapeArray.Sum(shape => shape.VertexBufferSize);
        int indexBufferSize = shapeArray.Sum(shape => shape.IndexBufferSize);

        // Vertex buffer
        vertexBufferObject = GL.GenBuffer();
        GL.BindBuffer(BufferTarget.ArrayBuffer,vertexBufferObject);
        GL.BufferData(BufferTarget.ArrayBuffer,vertexBufferSize,(IntPtr)0,BufferUsageHint.StaticDraw);

        IntPtr offset = (IntPtr)0;
        foreach (GLShape shape in shapeArray)
        {
            GL.BufferSubData(BufferTarget.ArrayBuffer,offset,shape.VertexBufferSize,shape.Vertices);
            offset += shape.VertexBufferSize;
        }

        // Element buffer
        elementBufferObject = GL.GenBuffer();
        GL.BindBuffer(BufferTarget.ElementArrayBuffer,elementBufferObject);
        GL.BufferData(BufferTarget.ElementArrayBuffer,indexBufferSize,BufferUsageHint.StaticDraw);

        offset = (IntPtr)0;
        foreach (GLShape shape in shapeArray)
        {
            GL.BufferSubData(BufferTarget.ElementArrayBuffer,shape.IndexBufferSize,shape.Indices);
            offset += shape.IndexBufferSize;
        }
    }

    private void InitializeVertexArrayObject(GLShape[] shapeArray)
    {
        foreach (GLShape shape in shapeArray)
        {
            shape.VertexArrayObject = GL.GenVertexArray();
            GL.BindVertexArray(shape.VertexArrayObject);
        }
    }

    private void SetupShader()
    {
        // shader
        string vertexPath = Path.Combine(Environment.CurrentDirectory,@"GLSL\","Vertex.vert");
        string fragmentPath = Path.Combine(Environment.CurrentDirectory,"Fragment.frag");
        shader = new Shader(vertexPath,fragmentPath);
        shader.Use();

        int vertexLocation = shader.GetAttribLocation("aPosition");
        GL.EnabLevertexAttribArray(vertexLocation);
        GL.VertexAttribPointer(
            vertexLocation,3,VertexAttribPointerType.Float,false,5 * sizeof(float),0);

        int texCoordLocation = shader.GetAttribLocation("aTexCoord");
        GL.EnabLevertexAttribArray(texCoordLocation);
        GL.VertexAttribPointer(
            texCoordLocation,2,3 * sizeof(float));

        shader.SetInt("texture0",0);
        shader.SetInt("texture1",1);
    }

    private void BindBuffers()
    {
        GL.BindBuffer(BufferTarget.ArrayBuffer,vertexBufferObject);
        GL.BindBuffer(BufferTarget.ElementArrayBuffer,elementBufferObject);
    }

render函数本身看起来像这样。

    public void Render()
    {
        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

        if (Shapes == null || Shapes.Length == 0)
            return;

        IntPtr offset = (IntPtr)0;
        foreach (GLShape shape in Shapes)
        {
            foreach (var texture in shape.Textures)
            {
                if (LayerConfiguration.ShowEarthTexture)
                    texture.Key.Use(texture.Value);
                else
                    texture.Key.MakeTransparent(texture.Value);
            }

            ApplyModelTransforms(shape,out Matrix4 model);
            shader.SetMatrix4("model",model);
            shader.SetMatrix4("view",Camera.GetViewMatrix());

            GL.DrawElements(PrimitiveType.Triangles,shape.Indices.Length,DrawElementsType.UnsignedInt,offset);
            offset += shape.IndexBufferSize;
        }

        shader.SetMatrix4("projection",Camera.GetProjectionMatrix());
        shader.Use();
    }

解决方法

一旦我想添加另一个,渲染就会显示出奇怪的行为

当然,因为第二个及以下对象的索引是错误的。您需要将先前网格的顶点总和添加到索引中。在第一个网格的索引中,您必须添加0,在第二个网格的索引中,您必须添加第一个网格的顶点数,在第三个网格的索引中,您必须添加顶点的总和。第一和第二网格,...

offset = (IntPtr)0;
uint firstVertexIndex = 0;
foreach (GLShape shape in shapeArray)
{
    var indexArray = shape.Indices.Select(index => index + firstVertexIndex).ToArray();
                
    GL.BufferSubData(
        BufferTarget.ElementArrayBuffer,offset,shape.IndexBufferSize,indexArray);
    offset += shape.IndexBufferSize;
    firstVertexIndex += (uint)(shape.VertexBufferSize / (5 * sizeof(float)));
}

完整方法InitializeBuffers

private void InitializeBuffers(GLShape[] shapeArray)
{
    int vertexBufferSize = shapeArray.Sum(shape => shape.VertexBufferSize);
    int indexBufferSize = shapeArray.Sum(shape => shape.IndexBufferSize);

    // Vertex buffer
    vertexBufferObject = GL.GenBuffer();
    GL.BindBuffer(BufferTarget.ArrayBuffer,vertexBufferObject);
    GL.BufferData(BufferTarget.ArrayBuffer,vertexBufferSize,(IntPtr)0,BufferUsageHint.StaticDraw);

    IntPtr offset = (IntPtr)0;
    foreach (GLShape shape in shapeArray)
    {
        GL.BufferSubData(BufferTarget.ArrayBuffer,shape.VertexBufferSize,shape.Vertices);
        offset += shape.VertexBufferSize;
    }

    // Element buffer
    elementBufferObject = GL.GenBuffer();
    GL.BindBuffer(BufferTarget.ElementArrayBuffer,elementBufferObject);
    GL.BufferData(BufferTarget.ElementArrayBuffer,indexBufferSize,BufferUsageHint.StaticDraw);

    offset = (IntPtr)0;
    uint firstVertexIndex = 0;
    foreach (GLShape shape in shapeArray)
    {
        var indexArray = shape.Indices.Select(index => index + firstVertexIndex).ToArray();
                
        GL.BufferSubData(BufferTarget.ElementArrayBuffer,indexArray);
        offset += shape.IndexBufferSize;
        firstVertexIndex += (uint)(shape.VertexBufferSize / (5 * sizeof(float)));
    }
}