【问题标题】:OpenGL - Is vertex attribute state bound to specific VBOs?OpenGL - 顶点属性状态是否绑定到特定的 VBO?
【发布时间】:2015-11-12 15:26:29
【问题描述】:

据我目前了解 VAO/VBO,VAO 保留自绑定以来已设置的所有属性信息,例如。 VBO 中给定顶点属性的偏移量、步幅、组件数等。

我似乎不清楚 VAO 和 VBO 如何协同工作。我见过的很多例子都指定了与当前绑定的 VBO 相关的顶点属性,并且当绑定 VAO 时,VBO 中的数据变得可访问。我可以看到以这种方式使用 VAO 的一种方法是每个对象都有一个(每个对象使用自己的 VBO),但我已经读到这在性能方面很差,因为不必要地在许多 VAO 之间切换。我还希望避免将所有对象数据存储在一个整体 VBO 中,因为我需要随时在场景中添加和删除对象 - 作为 3D 编辑器,我觉得该应用程序更适合拥有每个几何对象都拥有自己的缓冲区,而不是在一些大型的、预先分配的 VBO 中。 (这是一个正确的假设吗?)

因此,我的问题是一个 VAO 是否可以独立于 VBO 存储顶点属性配置?我是否能够将 VAO 配置为期望某种格式的数据(例如位置、法线、UV),然后在我绘制不同的几何对象时“交换”不同的 VBO,或者格式信息本质上是否只绑定到VBO本身?如果是后者,我是否值得我使用 VAO?

【问题讨论】:

  • 通常每个模型使用一个 VAO,使用该模型所需的顶点和索引缓冲区设置它,然后在要绘制模型时绑定 VAO。
  • 这就是我所说的性能不佳的意思。我预计世界上可能会有数千个几何块,其部分渲染取决于相机位置,并且在每个块上切换 VAO 似乎会使它们变得毫无意义,因为它们被设计为不会频繁切换。
  • 停止过早的优化和假设。对其进行编码并查看其性能。

标签: opengl


【解决方案1】:

ARB_vertex_attrib_binding 允许您分离 Vao 属性格式和缓冲区绑定。

https://www.opengl.org/wiki/Vertex_Specification#Separate_attribute_format

在内部,当您配置 Vao 时,顶点缓冲区会自动与属性索引关联。使用 ARB_vertex_attrib_binding,您可以使用新的 gl 函数来定义独立于绑定缓冲区的属性格式,可以使用 VertexBuffer 函数进行切换。

这里有一段带有 openTK 的 c# 代码:(全文:https://github.com/jpbruyere/GGL/tree/ottd/Tetra

这里的解决方案是构建一个 VAO,将所有网格连接起来,只保留每个网格

  • BaseVertex = VAO 中的顶点偏移量
  • IndicesOffset = 元素缓冲区中的偏移量(ebo 索引)
  • IndicesCount = 和模型的总索引数

    protected void CreateVAOs()
    {
        //normal vao binding
    
        vaoHandle = GL.GenVertexArray();
        GL.BindVertexArray(vaoHandle);
    
        GL.EnableVertexAttribArray(0);
        GL.BindBuffer(BufferTarget.ArrayBuffer, positionVboHandle);
        GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, true, Vector3.SizeInBytes, 0);          
    
        ... other attrib bindings come here
    
        //ARB vertex attrib binding use for fast instance buffers switching
        //note that I use 4 attrib indices to bind a matrix
        GL.VertexBindingDivisor (instanceBufferIndex, 1);
        for (int i = 0; i < 4; i++) {                   
            GL.EnableVertexAttribArray (instanceBufferIndex + i);   
            GL.VertexAttribBinding (instanceBufferIndex+i, instanceBufferIndex);
            GL.VertexAttribFormat(instanceBufferIndex+i, 4, VertexAttribType.Float, false, Vector4.SizeInBytes * i);
        }
    
        if (indices != null)
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, eboHandle);
    
        GL.BindVertexArray(0);
    }
    

然后,我定义网格实例,每个实例只包含一个矩阵数组,这是一个正常的缓冲区创建,但不是静态绑定到 vao。

    instancesVboId = GL.GenBuffer ();       

    GL.BindBuffer (BufferTarget.ArrayBuffer, instancesVboId);
    GL.BufferData<Matrix4> (BufferTarget.ArrayBuffer,
                new IntPtr (modelMats.Length * Vector4.SizeInBytes * 4),
                modelMats, BufferUsageHint.DynamicDraw);
    GL.BindBuffer (BufferTarget.ArrayBuffer, 0);

为了渲染这样的 vao,我在我的实例数组中循环:

    public void Bind(){
        GL.BindVertexArray(vaoHandle);
    }

    public void Render(PrimitiveType _primitiveType){
        foreach (VAOItem item in Meshes) {
            GL.ActiveTexture (TextureUnit.Texture1);
            GL.BindTexture (TextureTarget.Texture2D, item.NormalMapTexture);
            GL.ActiveTexture (TextureUnit.Texture0);
            GL.BindTexture (TextureTarget.Texture2D, item.DiffuseTexture);
            //Here I bind the Instance buffer with my matrices
            //that's a fast switch without changing vao confing
            GL.BindVertexBuffer (instanceBufferIndex, item.instancesVboId, IntPtr.Zero,Vector4.SizeInBytes * 4);
            //here I draw instanced with base vertex
            GL.DrawElementsInstancedBaseVertex(_primitiveType, item.IndicesCount, 
                DrawElementsType.UnsignedShort, new IntPtr(item.IndicesOffset*sizeof(ushort)),
                item.modelMats.Length, item.BaseVertex);
        }
    }

最终的 VAO 只绑定一次。

【讨论】:

  • 您似乎非常关心VAO绑定的成本。你测量过吗?绑定 VAO 通常应该相当便宜。这就是首先拥有 VAO 的全部目的,能够通过一次有效的调用来设置所有状态。它当然不应该比绑定纹理更昂贵。
  • @RetoKoradi:进行测试,有很大的不同。我已经快速测量(100 vao switch=>frameTime=0.53ms(300+10 个实例):单个 vao=>frameTime=0.055ms(相同计数)中的 100 个对象。这个解决方案的限制来自于理想的顶点数vao(对我来说大约 250.000),但我不是专家。
  • 相信我,我已经做了很多测量。我在两个不同的驱动程序中实现/优化了这个功能,其中包括大量的性能基准测试。如果您发现绑定 VAO 比其他类似的状态更改要昂贵得多,那么您要么使用了非常糟糕的 OpenGL 实现,要么您测量性能的方式存在问题。
  • @RetoKoradi:我并不是说它比其他方法更广泛,我只是指出绑定 VAO 的成本可能会被索引、实例和 VertexBuffer 渲染技术绕过。 less vao Binding=>more performance,我只用 vao。我做过几款游戏,测试了几种方法,目前我最喜欢这个。
  • @RetoKoradi:请注意,我已经编辑了我的帖子,以便更清楚地回答关于拆分 vao 和缓冲区绑定的可能性的 Op 问题。
猜你喜欢
  • 2011-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-05
  • 1970-01-01
  • 2011-12-23
  • 2021-03-05
  • 1970-01-01
相关资源
最近更新 更多