【问题标题】:How to draw millions of cubes without idle , model instancing in XNA?如何在 XNA 中绘制数百万个没有空闲模型实例的立方体?
【发布时间】:2013-03-11 12:25:45
【问题描述】:

我为“我的世界”游戏风格的项目工作。

我开始使用“模型实例化”来生成大量具有相同模型的立方体。 到目前为止,一切都很好。 我的问题是,如果我增加矩阵的大小以从 [500-1-500](250,000 立方米) 我的程序非常慢。它从 60fps 到 20...

我真的不明白为什么。但是我正确地使用了“硬件实例化”技术。我还在论坛上注意到,这种技术可以让 XNA 绘制多达 700 万立方!! 你知道我的问题出在哪里吗?

非常感谢

这是我绘制实例化模型的函数:

// Draw the 3D map (world) of game 
public void drawWorld(GameTime gameTime)
{
    / * Draw all the structures that make up the world with the instancing system * /
    Array.Resize(ref instanceTransforms, smallListInstance.Count);

    for (int i = 0; i < ListInstance.Count; i++)
    {
        instanceTransforms[i] = ListInstance[i].Transform;
    }

    DrawModelHardwareInstancing(myModel, myTexture2D,instancedModelBones,instanceTransforms, arcadia.camera.View, arcadia.camera.Projection);


}
// ### end function drawWorld

这是我的函数 [DrawModelHardwareInstancing],它使用 microsoft 示例中使用的 [Hardware Instancing] 方法绘制模型。

// Function that will draw all the models instantiated in the list
void DrawModelHardwareInstancing(Model model,Texture2D texture, Matrix[] modelBones,
                                 Matrix[] instances, Matrix view, Matrix projection)
{
    Game.GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
    if (instances.Length == 0)
        return;

    // If we have more instances than room in our vertex buffer, grow it to the neccessary size.
    if ((instanceVertexBuffer == null) ||
        (instances.Length > instanceVertexBuffer.VertexCount))
    {
        if (instanceVertexBuffer != null)
            instanceVertexBuffer.Dispose();

        instanceVertexBuffer = new DynamicVertexBuffer(Game.GraphicsDevice, instanceVertexDeclaration,
                                                       instances.Length, BufferUsage.WriteOnly);
    }

    // Transfer the latest instance transform matrices into the instanceVertexBuffer.
    instanceVertexBuffer.SetData(instances, 0, instances.Length, SetDataOptions.Discard);

    foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (ModelMeshPart meshPart in mesh.MeshParts)
        {
            // Tell the GPU to read from both the model vertex buffer plus our instanceVertexBuffer.
            Game.GraphicsDevice.SetVertexBuffers(
                new VertexBufferBinding(meshPart.VertexBuffer, meshPart.VertexOffset, 0),
                new VertexBufferBinding(instanceVertexBuffer, 0, 1)
            );

            Game.GraphicsDevice.Indices = meshPart.IndexBuffer;


            // Set up the instance rendering effect.
            Effect effect = meshPart.Effect;
            //effect.CurrentTechnique = effect.Techniques["HardwareInstancing"];
            effect.Parameters["World"].SetValue(modelBones[mesh.ParentBone.Index]);
            effect.Parameters["View"].SetValue(view);
            effect.Parameters["Projection"].SetValue(projection);
            effect.Parameters["Texture"].SetValue(texture);


            // Draw all the instance copies in a single call.
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                Game.GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0,
                                                       meshPart.NumVertices, meshPart.StartIndex,
                                                       meshPart.PrimitiveCount, instances.Length);
            }
        }



    }
}
// ### end function DrawModelHardwareInstancing

【问题讨论】:

  • 你真的需要每帧更新你的顶点缓冲区和索引缓冲区吗?如果你想渲染一百万个立方体,你可能不应该每帧更新顶点缓冲区,因为它们可能不会有太大变化。这不是问题吗?每帧循环遍历很多对象仍然非常耗时。
  • 嗯很有趣!但是你能告诉我我在哪里更新我的顶点缓冲区吗?你谈那个? Game.GraphicsDevice.SetVertexBuffers ( new VertexBufferBinding (meshPart.VertexBuffer, meshPart.VertexOffset, 0), new VertexBufferBinding (instanceVertexBuffer, 0, 1) ); Game.GraphicsDevice.Indices = meshPart.IndexBuffer;
  • 我还没有用立方体做这个,但我想它应该可以工作。如果我创建一个 1000x1000 点的网格,我很容易创建一个顶点缓冲区和一个索引缓冲区,并确保一个点不会出现两次,然后存储顶点缓冲区和索引缓冲区,然后我不需要重新创建它,除非它被更改。但是在您的情况下,您可能必须支持更改,这样才能为不同区域创建多个缓冲区,因此您只需要更新一个 100 个顶点缓冲区,而不是每帧循环 100*1000 个。只是一个想法。
  • 我喜欢你的想法!我今晚会尝试类似的方法,然后我会回来通知你。

标签: model xna buffer draw


【解决方案1】:

非常感谢!感谢您的建议,我终于解决了我的问题;-)!

我只画在我的视野“相机”前面的模型。 仅在需要“编辑”、“新位置等..”时更新模型。

我的最后一步是仅绘制 6 个立方体面中的 3 个以提高性能。

仅绘制前面的模型的代码示例

public BoundingFrustum Frustum { get;私人套装; }

//Initialize 
Matrix viewProjection = View * Projection; 
Frustum = new BoundingFrustum(viewProjection); 

//Update 
Matrix viewProjection = View * Projection; 
Frustum.Matrix = viewProjection; 

//Check instance and make the instanceTransformation for draw 
foreach (var object in ListInstance.Where(m => Frustum.Contains(m.BoundingBox) != ContaintmentType.Disjoint) 
{
    instanceTransforms[index].Add(smallListInstance[i].Transform); 

} 
// And now draw ... 

【讨论】:

  • 您还可以使用 Select Extension 进一步改进:例如 foreach (var object in ListInstance.Where(m => Frustum.Contains(m.BoundingBox) != ContaintmentType.Disjoint),这样你只会遍历所有你需要的对象,而不是跳过一些你不需要的对象。它只会遍历 Frustum 视图中的对象。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-03
  • 1970-01-01
  • 2021-12-25
  • 2016-04-26
相关资源
最近更新 更多