使用顶点缓冲区绘制大量相同的模型?

Meh*_*ard 3 performance xna vertex-buffer

我面临着许多开发人员可能找到解决方案的问题.我有一个小项目,地板设计有小立方体(100X100).如果我超过这个限制,我的游戏遭遇重大减速和联赛!

这是我如何画地板的:

//Function to draw my ground ( 100 X 100 X 1)
    public void DrawGround(GameTime gameTime)
    {
        // Copy any parent transforms.
        Matrix[] transforms = new Matrix[this.model.Bones.Count];
        this.model.CopyAbsoluteBoneTransformsTo(transforms);

        //loop 1 cube high
        for (int a = 0; a < 1; a++)
        {
            //loop 100 along cube
            for (int b = 0; b < 100; b++)
            {
                //loop 100 cubic wide
                for (int c = 0; c < 100; c++)
                {
                    // Draw the model. A model can have multiple meshes, so loop.
                    foreach (ModelMesh mesh in this.model.Meshes)
                    {
                        // This is where the mesh orientation is set, as well 
                        // as our camera and projection.
                        foreach (BasicEffect effect in mesh.Effects)
                        {
                            effect.EnableDefaultLighting();
                            effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateTranslation(this.position);
                            effect.View = this.view;
                            effect.Projection = this.projection;


                        }

                        // Draw the mesh, using the effects set above.
                        mesh.Draw();
                    }
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

我认为最好使用[VertexBuffer]"显卡内存",但我还没有找到我想做的教程......

你能举个例子在我的函数"DrawGround"中使用[VertexBuffer]吗? 在此输入图像描述 非常感谢你!

Luc*_*ius 7

TL;博士;

使用硬件实例化来绘制多维数据集.(可选)使用View Frustum Culling以避免绘制不可见实例.通过阅读Riemer的教程,开始使用顶点和索引缓冲区.


实例化

这个想法是,顶点和索引缓冲区一次绑定到设备,然后在一次绘制调用中多次绘制.实例是一小块数据,仅包含模型实例的唯一信息.此实例数据将写入另一个顶点缓冲区,并与包含实际顶点和索引的缓冲区绑定到设备.

你怎么能利用它?

在您的情况下,您将具有以下设置:

  • 包含立方体网格索引的IndexBuffer
  • 包含立方体网格顶点的VertexBuffer
  • 第二个VertexBuffer,包含要绘制的所有多维数据集的转换
  • 一种特殊的输入布局,可以将实例数据考虑在内

由于您的立方体的位置不同,您甚至不必将完整的转换矩阵发送到着色器.只需发送实例位置并将其添加到顶点位置即可.您还可以为每个实例发送颜色等其他数据.

你是如何实现它的?

一个快速的谷歌搜索给了我大量的xna实例的结果,所以这应该让你开始.这里有两个看似有希望的随机结果:


沮丧剔除

在任何时候,场景中只有一小部分网格实际上是可见的.对象可能被另一个对象遮挡,或者完全位于玩家的视野之外.在绘制之前删除这些不可见网格的过程称为剔除.查看Frustum Culling的第二个案例:

所有与相机音量不相交的音量都是不可见的.

相机的音量是多少?正交投影具有盒子形式的边界体积.透视投影,这个盒子是锥形的,金字塔,由近和远的平面裁剪; 因此看到视锥体.

你怎么能利用它?

使用视锥体来识别当前帧中不可见的实例.仅绘制与相机的视锥体相交的那些立方体.当玩家仰望天空时,渲染负荷可降低高达100%.;-)

您可以将其与空间层次结构(四叉树八叉树)组合以减少箱形截头积分交叉计算的数量.

你是如何实现它的?

Xna提供了BoundingBox结构以及BoundingFrustum类.你所要做的就是使用它们.


一个小小的缺点

结合View Frustum Culling硬件实例化可能很棘手.剔除实例意味着您还必须从实例缓冲区中删除它们=>这意味着重新创建整个事物.也许考虑计算立方体可见性并仅每隔几帧或快速移动摄像机时更新实例缓冲区.您最终如何以及在何种程度上实施这些技术取决于您并且取决于您的要求.


我刚才意识到你想知道顶点缓冲区是如何工作的:

顶点缓冲区

在将三角形绘制到屏幕之前,必须准备好图形设备.这包括绑定着色器,采样器,纹理,当然还可以绑定几何数据.进行绘制调用时,图形设备运行着色器并将输出写入渲染目标.

目前您正在使用XNA的内置抽象,这简化了过程(并从您那里获得了所有控制权).绑定着色器,常量,输入布局等目前都隐藏在Effect类中,而Mesh类负责几何方面.

'mesh.Draw()'有什么作用?

创建网格时,会创建两个缓冲区,一个VertexBuffer和一个IndexBuffer.创建这个缓冲区是很昂贵的,所以这只做一次是件好事.当您调用draw方法时,缓冲区将绑定到设备,而不是绘制调用.正如您在代码中正确评论的那样,必须首先设置效果状态.

但您也可以创建自己的顶点和索引缓冲区:

// Instantiate the VertexBuffer
var vertexBuffer = new VertexBuffer(
    device, 
    VertexPositionColorNormal.VertexDeclaration, 
    vertices.Length, 
    BufferUsage.WriteOnly
);

// Write data to the buffer
vertexBuffer.SetData(vertices);

// Instantiate the IndexBuffer
var indexBuffer = new IndexBuffer(
    device, 
    typeof(int), 
    indices.Length,      
    BufferUsage.WriteOnly
);

// Write data to the buffer
indexBuffer.SetData(indices);
Run Code Online (Sandbox Code Playgroud)

...并将它们手动绑定到设备:

device.Indices = myIndexBuffer;
device.SetVertexBuffer(myVertexBuffer);
Run Code Online (Sandbox Code Playgroud)

此代码直接取自Riemer的XNA教程:

看看网站,当我第一次开始图形编程时,它确实帮了我很多忙.