【问题标题】:Is there a faster alternative to using textures in XNA?在 XNA 中使用纹理是否有更快的替代方法?
【发布时间】:2010-05-20 12:16:34
【问题描述】:

我正在使用 XNA 为 2D 游戏编写地图编辑程序。为地图所需的所有图块创建 Texture2D 需要的时间太长。

除了使用纹理使用 XNA 进行绘图之外,还有其他选择吗?

我尝试为每个图块集创建一个纹理,而不是为图块集中的每个图块创建一个纹理,但是纹理的大小有限制,我无法将图块集的所有图块放入一个纹理.

目前该程序包含内存中所有可能的纹理作为位图对象。有没有办法在 XNA 中简单地将 Bitmap 对象绘制到屏幕上?我已经搜索过,但我找不到任何关于此的信息。这种方法可以避免完全创建纹理,但是我必须直接对位图进行任何着色或效果。

【问题讨论】:

    标签: c# xna textures


    【解决方案1】:

    您是否有任何理由不考虑加载一次图像,然后将 x 和 y 偏移坐标传递给像素着色器?

    你基本上会像这样设置 C#:

    myGraphicsDevice.Textures[0] = whateverYourGiantMapTextureIs;
    
    foreach(MapChunk chunk in mapChunks) {
        myShader.Effect.Parameters["xOffset"] = chunk.XOffset;
        myShader.Effect.Parameters["yOffset"] = chunk.YOffset;
    
        myGraphicsDevice.DrawIndexedPrimitives( your chunk drawing code here );
    }
    

    然后是这样的着色器代码:

    float4x4 World; 
    float4x4 View; 
    float4x4 Projection;
    
    float xOffset;
    float yOffset;
    
    sampler TextureSampler; 
    
    struct VS_INPUT { 
        float4 Position : POSITION0; 
        float4 Color    : COLOR0;
    };
    
    VS_INPUT Transform(VS_INPUT Input) { 
        VS_INPUT Output; 
    
        float4 worldPosition = mul(Input.Position, World); 
        float4 viewPosition = mul(worldPosition, View); 
        Output.Position = mul(viewPosition, Projection); 
        Output.Color = Input.Color;
    
        return Output; 
    } 
    
    float4 ColorTexture(VS_INPUT Input) : COLOR0{ 
        return Input.Color.rgba * tex2D(TextureSampler, float2(xOffset, yOffset));
    } 
    
    technique TransformColorTexture { 
        pass P0 { 
            VertexShader = compile vs_2_0 Transform(); 
            PixelShader = compile ps_2_0 ColorTexture(); 
        } 
    }
    

    着色器代码可能需要适合您现有的代码,否则它应该可以解决问题。

    【讨论】:

      【解决方案2】:

      每个图块使用一个纹理不是很有效。特别是因为这意味着你不能进行批处理(在任何真正意义上)。

      如果您出于某种原因需要在内容项目中将它们作为单独的纹理(更容易编辑一个图块等),您可以在加载后很容易地将它们编译成图块地图。 基本上你是怎么做的:

      1:加载多个图块(现在假设为 40 个 32*32 图块)

      2:为 tilemap 找出一个不错的纹理大小: 40 的平方根是 6.something,所以我们四舍五入到 7。7*32 是 224,这很好,但 256 更好,所以让纹理 256x256。 (你可以编写代码来即时计算)

      3:创建所需大小的 Rendertarget2D。

      4:激活渲染目标。

      5:在 rendertarget 上渲染图块:

      int x, y = 0;
      foreach (var tile in allTiles)
      {
          RenderTile(tile, x*32, y*32);
          x++;
      
          if (x >= 8)
          {
              x = 0;
              y++;
          }
      }
      

      要进行betch-render,您有一个具有4 * 40 个顶点的顶点缓冲区。每组 4 都有一个值,指示它所属的四边形的索引(0、1、2 等...)。在您的着色器中,您有一个矩阵数组[40] 用于图块的位置,还有一个 tileIndex (int[40]) 数组用于了解要从 tilemap 渲染哪个图块。

      很抱歉,我现在没有时间编写所有着色器代码:s

      我在游戏中使用的另一个技巧是将关卡预渲染到大图块 (640x360) 上,这大大减少了绘制调用的数量,尤其是在处理来自不同图块集的 5 层以上图块时.唯一的问题是它不适用于动态图块(动画图块等),但您可以标记它们并根据需要正常渲染它们......

      【讨论】:

        【解决方案3】:

        由于您需要使用自定义图像格式,如果您想要(为了速度),您可以尝试为 XNA (http://msdn.microsoft.com/en-us/library/bb447754.aspx) 编写自定义内容管道导入器和处理器,但这对于您需要做的事情来说可能是多余的.

        我知道您希望尽可能轻松地设计 GUI,即使这些问题迫使您使用 C++ 之类的语言,以便您可以使用 DirectX。在 Visual C++ 中,如果您使用的是 Visual Studio,您应该仍然能够利用 visual Windows Forms layout

        【讨论】:

          【解决方案4】:

          不幸的是,据我所知,在 XNA 中没有办法直接将位图绘制到屏幕上;它要求所有内容都映射到 Texture 对象,这些对象默认缓冲到显卡。听起来你在谈论很多瓷砖,但是,如果它们不适合最大允许的纹理(我不记得那是 1024 还是 4096 平方......) - 你试过有一个出于速度目的的无缓冲纹理?另一种选择是将您的图块集延迟加载到纹理中,这样用户就不必等待它们全部加载 - 对于编辑器,在加载时使用亮粉色后备颜色通常是可以接受的。

          另外,在 XNA 中编写您的工具有什么固有的要求吗?由于听起来您是在独立于游戏引擎本身编写工具,因此您可能会在 WPF 中找到一些非常可接受的解决方案,包括直接从 BitmapSources 绘图和一些用于您的 UI 的实用程序。

          【讨论】:

          • 感谢您的回复,没有什么需要我使用 XNA,没有。现在我已经回到了 C++ 和 DirectX 版本的工具上。我的问题是 XNA 中的纹理似​​乎非常慢。使用 XNA Texture2Ds 需要 100 多秒的时间使用 DirectX 纹理需要 0.3 秒。感谢您的直截了当-主要我想知道的是没有办法直接将位图绘制到屏幕上。
          【解决方案5】:

          假设地图需要现有图像的所有图块,这些图块只是放置在屏幕上以布置图块地图?或者您是在这个地图编辑器中制作图像并想要保存它们吗?

          我阅读了这篇文章,以及您关于纹理的另一篇文章 - 如果它们都是您想在游戏中使用的已定义图像,您就不能将它们包含在内容文件夹中并在那里引用它们吗?这允许 XNA 以 xnb 格式编译纹理,您不应该遇到您正在解释的问题。

          但是,如果我们谈论的是完全动态的 - 或者您正在从图块的布局中制作一个大图像(而不是做类似图块地图之类的东西),那么我不确定如何解决这个问题。

          【讨论】:

          • 您好,感谢您的回复。简单地将图像文件添加到内容文件夹的问题在于它们是自定义格式的图像 - 我的地图编辑器适用于使用这些图像的现有游戏。我希望能够使用 C# 和 XNA 对编辑器进行编码,但是现在看起来我可能不得不重新开始并使用 C++ 和 DirectX 来完成它,因为我知道位图的纹理在 DirectX 中非常快。我使用 C# 的主要原因是因为我想要设计 UI 的简易性,但是如果我不能使用 C#,那么我会考虑使用 Qt 之类的东西。
          • 那是我一开始犹豫的原因,我想一定有一个原因你不能在你的游戏和编辑器的内容目录中使用它们 - 抱歉,我没有更好的答案给你!
          【解决方案6】:

          如果多个图块使用相同的纹理,只需加载一次就足够了。

          如果用户包含某种文件格式的纹理,您可以自动将其转换为更快满足您需求的纹理/图像格式。

          【讨论】:

          • 感谢您的回复。不幸的是,我只加载每个所需的纹理一次。我发现只需添加 Texture2D tx = new Texture2D(device, width, height);我的代码需要的每个纹理将地图的加载时间从 2.7 秒增加到 74 秒!请注意,2.7 秒包括创建包含所需纹理的位图。 Texture2D 对于动态创建纹理似乎太慢了。
          【解决方案7】:

          您是否尝试使用单个执行线程将它们全部绘制出来?

          尝试多线程您的游戏。 C# 中的线程非常简单,并且受 XNA 支持。

          与其查看整个屏幕,不如尝试将其分成两个(或更多)部分。您可能需要重新编写用于 Draw 的函数(我当然希望您不要直接在 Draw() 方法中转储所有内容!!)以获取设置绘图区域边界的坐标。

          【讨论】:

          • 我的程序已经是多线程的了。一个线程负责处理窗口消息,而一个子线程尝试加载图块并从中创建纹理。在写这个问题的时候,我还没有添加 Draw 方法,因为我首先需要一个在合理的时间范围内加载纹理的方法。
          猜你喜欢
          • 2021-05-31
          • 2011-02-21
          • 2017-03-18
          • 1970-01-01
          • 2021-12-05
          • 1970-01-01
          • 1970-01-01
          • 2020-10-04
          • 1970-01-01
          相关资源
          最近更新 更多