glDrawElements 不会在第二次调用时绘制,使用相同的顶点数组对象和相同的着色器程序

glDrawElements doesn't draw the second time it's called, using the same vertex array object and the same shader program

我是 opengl 的新手,我遇到了这种非常意外的行为。

我正在实现一个基本场景图并尝试添加重用着色器和网格数据的能力。

我有一个 GeometryData 对象,它包含对顶点数组对象、顶点缓冲区和索引缓冲区的引用。它有一个使用 glDrawElements 绘制模型的渲染方法。

GeometryData 具有对 Shader 对象的引用,该对象具有对程序着色器句柄的引用。

问题是,如果我渲染具有相同着色器实例的连续对象,只有第一个被渲染,其他的由于某种原因没有被渲染,没有错误。如果我在连续的对象绘制之间交替分配着色器,它们都会正常绘制。

我的GeometryData的Render方法如下:

    public override void Render()
    {
        GL.BindVertexArray(vertexArray);

        if (UseShader != null)
            UseShader.Use();

        var err = GL.GetError();

        GL.DrawElements(PrimitiveType.Triangles, numIndices, DrawElementsType.UnsignedInt, IntPtr.Zero);

        err = GL.GetError();
        GL.BindVertexArray(0);
    }

UseShader.Use()方法是这样的

    public void Use()
    {
        int current = GL.GetInteger(GetPName.CurrentProgram);

        if (current != ProgramHandle) 
            GL.UseProgram(ProgramHandle);

        // Use a uniform block for the project and modelview matrices
        var index = GL.GetUniformBlockIndex(ProgramHandle, "Matrices");
        GL.UniformBlockBinding(ProgramHandle, index, World.BINDING_POINT);

        // assume interleaved vertex data
        GL.VertexAttribPointer(positionLoc, 3, VertexAttribPointerType.Float, false, 2 * Vector3.SizeInBytes, Vector3.SizeInBytes);
        GL.EnableVertexAttribArray(positionLoc);

        GL.VertexAttribPointer(colorLoc, 3, VertexAttribPointerType.Float, false, 2 * Vector3.SizeInBytes, 0);
        GL.EnableVertexAttribArray(colorLoc);
    }

这里是一些截图。这些框是使用此循环创建的:

 for (int i = -3; i <= 3; i+=3)
 {
     Transform child = new Transform();
     child.Translation = new Vector3(i, 0, 0);

     // this causes the second red box to not render
     Geometry geom = new Geometry(i > 0 ? coloredShader : redShader, box); 


     // this causes all boxes to render normally
     // Geometry geom = new Geometry(i == 0 ? coloredShader : redShader, box);

     child.addChild(geom);

     root.addChild(child);
 }

具有交替着色器(红色、彩色、红色)的图片

使用相同着色器(红色、红色、彩色)的两个连续对象的图片。第二个红色对象未绘制。我对场景所做的唯一更改是着色器分配。

更新

经过进一步的检查和调试,问题的根源似乎是uniform block。我正在使用统一块将投影和模型视图矩阵发送到我的着色器。我怀疑第二次调用 glBufferSubData 来更新模型视图矩阵并没有更新制服,或者着色器没有先切换到另一个着色器就没有从制服读取该值。

我尝试将模型视图矩阵从统一块中取出,并将其分别发送到每个着色器。这使得所有对象都正确呈现,但我仍然想为此使用统一块以避免冗余上传。

已解决! 这真的很愚蠢。我需要在使用 glBufferSubData 更新统一块值后调用 glFlush()。

    public static void setModelView(ref Matrix4 modelView)
    {
        GL.BindBuffer(BufferTarget.UniformBuffer, uniformBuffer);
        GL.BufferSubData(BufferTarget.UniformBuffer, (IntPtr)(16 * sizeof(float)), (IntPtr)(16 * sizeof(float)), ref modelView);
        GL.Flush(); // I just added this line
        GL.BindBuffer(BufferTarget.UniformBuffer, 0);
    }