【问题标题】:Drawing massive amounts of textured cubes in XNA 4.0在 XNA 4.0 中绘制大量纹理立方体
【发布时间】:2011-01-15 20:00:56
【问题描述】:

我正在尝试在 XNA 4.0 中编写一个自定义的 Minecraft Classic 多人游戏客户端,但在实际绘制游戏中的世界时我完全被难住了。每个块都是 3D 空间中的一个立方体,它的每一面都可能有不同的纹理。我一直在互联网上阅读,发现对于一个立方体的每一面都有不同的纹理,每个面都需要自己的一组顶点。每个立方体总共有 24 个顶点,如果你有一个由 64*64*64 立方体(甚至可能更多!)组成的世界,那么就会产生很多顶点。

在我的原始代码中,我将我拥有的纹理贴图拆分为单独的纹理,并在绘制每个立方体的每一侧之前应用这些纹理。有人告诉我这是一种非常昂贵的方法,我应该将纹理保存在同一个文件中,并简单地使用 UV 坐标将某些子纹理映射到立方体上。不过,这对性能并没有多大帮助,因为顶点的数量实在是太多了。我还被告知要在 VertexBuffer 中收集顶点并一次绘制它们,但这也没有太大帮助,并且当顶点数超过缓冲区的最大大小时偶尔会导致异常。我尝试让立方体共享顶点的任何尝试也都失败了,导致速度大幅下降和立方体出现故障。

我不知道该怎么办。总的来说,我很擅长编程,但任何类型的 3D 编程或游戏开发都完全让我无法理解。

这是我用来绘制立方体的方法。我有两个全局列表List<VertexPositionTexture> 和List<int>,一个用于顶点,一个用于索引。绘制时,我会遍历世界上所有的立方体,并对那些不为空的立方体(如 Air)进行 RenderShape。我的形状类粘贴在下面。 AddVertices 方法中的注释代码是尝试使多维数据集共享顶点。当所有立方体的顶点都添加到列表中时,数据被粘贴到 VertexBuffer 和 IndexBuffer 中,并调用 DrawIndexedPrimitives。

说实话,我可能完全做错了,但我真的不知道该怎么做,也没有教程真正描述如何绘制大量对象,只有非常简单的。我必须弄清楚如何重做 BasicShape 才能自己拥有多个纹理。

形状: http://pastebin.com/zNUFPygP

【问题讨论】:

  • 请问您想出了什么解决方案?我也对此感兴趣。
  • @HuseyinUslu - 抱歉,我从来没有真正得出结论。我尝试了一大堆东西,但从来没有让它正常工作。修复它的最佳方法是使用八叉树,但我很难弄清楚如何以有利于渲染的方式实现它们。希望对您有所帮助。

标签: c# xna


【解决方案1】:

您可以获取我与其他一些名为 TechCraft 的开发人员编写的代码的副本:

http://techcraft.codeplex.com

它是免费和开源的。它应该向您展示如何创建类似于 Minecraft 的引擎。

【讨论】:

    【解决方案2】:

    您可以做很多事情来加快速度:

    您要做的是将一个立方体区域烘焙到顶点缓冲区中。我的意思是把所有的立方体放在一个小区域,然后把它们都放到一个顶点缓冲区中。仅在多维数据集更改时更新此缓冲区。

    在像我的世界这样的世界中,很多面都相互遮挡。您可以做的最重要的事情是隐藏两个立方体之间共享的面。想象一下两个相邻的立方体,你不需要在中间画脸,因为无论如何它永远都看不到。在我们的引擎中,这导致顶点数量减少了 20 倍。

     _ _      _ _
    |_|_| == |_ _|
    

    至于您的纹理,正如您所说,使用纹理图集是个好主意。这大大减少了您的绘图调用。

    祝你好运!如果您想作弊,请查看 Infiniminer。 Infiniminer 是基于我的世界的游戏。它是用 XNA 编写的,并且是开源的!

    【讨论】:

    • 我要做的是检查一个立方体是否被其他不透明的立方体包围,然后跳过那个立方体,因为它无论如何都不可见。也许我应该做同样的事情,除了面孔吗?另外,我应该如何使用 VertexBuffers?我现在要做的是每次渲染立方体时都重新创建缓冲区,这似乎是错误的。
    • 是的,测试个人面孔。非常值得付出额外的努力。重新创建缓冲区可能是所有帧速率的去向!不必在每次渲染时都重新创建缓冲区,只需保持相同的缓冲区直到用户更改立方体。
    【解决方案3】:

    您需要考虑缩小问题的规模。如何通过更少的工作来制作相同的图像?

    如果您的立方体以固定间隔隔开并且大小都相同,则您可能根本不需要存储顶点 - 您的着色器可能能够在运行时计算顶点位置。如果它们的大小不同并且没有定期间隔,那么您仍然可以将一些用于 onoff 实例化(您将立方体的位置和大小提供给着色器,它会计算出在哪里渲染顶点以制作立方体出现在那个位置)

    如果您的立方体遮挡了它们后面的任何东西,那么您只需要绘制最前面的立方体 - 它们后面的任何东西都是不可见的。一种自然的方法是使用八叉树数据结构,它将 3D 空间划分为体素(立方体)。使用八叉树,您可以快速确定哪些立方体是可见的,然后只绘制这些立方体 - 因此,您可能会发现每帧只需绘制几百个,而不是绘制 64x64x64 的立方体。您还会发现,随着相机的移动,可见立方体的集合并不会发生太大变化,因此您也许可以使用这种“时间一致性”来更新您的数据结构,以最大限度地减少决定哪些立方体所需完成的工作可见。

    【讨论】:

      【解决方案4】:

      我不认为 Minecraft 会一直绘制所有的立方体。它们中的大多数是内部的,您只需要绘制表面上的那些。所以基本上,你需要一个高效的voxel 渲染器。

      【讨论】:

        【解决方案5】:

        我最近在 XNA 中编写了一个引擎来执行此操作,您想要研究的技术称为硬件实例化,它允许您将一个模型传递到具有世界位置流的着色器中,以“实例化”该模型数百个(甚至数千次)遍布您的游戏世界。

        我在这个例子的基础上构建了我的引擎,用我自己的替换了实例化模型。

        http://xbox.create.msdn.com/en-US/education/catalog/sample/mesh_instancing

        一旦你把它变成一个可重用的类,它和它附带的着色器就会变得非常有用,可以渲染成千上万个你想要的东西(灌木、树木、立方体、鸟群等)。

        一旦你有了一个基础模型(可能是方块的一个面),它的网格就会有一个相关的纹理,然后你可以用任何你想要的东西来替换它,让你可以动态地改变每一面和不同类型的方块纹理块。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多