【问题标题】:How to draw 2D pixel-by-pixel in XNA?如何在 XNA 中逐像素绘制 2D?
【发布时间】:2011-06-17 18:31:50
【问题描述】:

我正在尝试使用 XNA 逐个像素地在屏幕上绘图,但我遇到了资源问题。我认为最好的方法是让每帧更新 1 个纹理,但我无法更新它。这是我目前得到的,只是作为一个测试:

Texture2D canvas;
Rectangle tracedSize;
UInt32[] pixels;

protected override void Initialize()
    {
        tracedSize = GraphicsDevice.PresentationParameters.Bounds;
        canvas = new Texture2D(GraphicsDevice, tracedSize.Width, tracedSize.Height, false, SurfaceFormat.Color);
        pixels = new UInt32[tracedSize.Width * tracedSize.Height];               

        base.Initialize();
    }

protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        pixels[100] = 0xFF00FF00;
        canvas.SetData<UInt32>(pixels, 0, tracedSize.Width * tracedSize.Height);

        spriteBatch.Begin();
        spriteBatch.Draw(canvas, new Rectangle(0, 0, tracedSize.Width, tracedSize.Height), Color.White);
        spriteBatch.End();

        base.Draw(gameTime);
    }

第二次调用 Draw() 时,出现以下错误:

“操作已中止。您不能修改已在设备上设置的资源,或在平铺括号内使用后的资源。”

如果我尝试在 Draw() 中创建一个新的 Texture2D,我很快就会遇到内存不足的错误。这适用于 Windows Phone。看来我正在尝试以错误的方式进行操作,我还有哪些其他选择才能使其正常工作?

【问题讨论】:

  • 我不知道答案,所以我用谷歌搜索了“xna 程序纹理”(这就是这种东西的名称)。似乎没有一种简单的内置方法可以做到这一点。至少有三个常见的建议(缓冲、GPU 纹理命令、在着色器中执行)。最后一个显然是最优的,但需要着色器编程知识。您可能希望执行相同的搜索并浏览那里的内容。
  • 对于希望逐个像素地动态绘制 2D 纹理的人,这里已经回答了 stackoverflow.com/questions/70022699/…

标签: c# xna 2d


【解决方案1】:

在调用 SetData 之前尝试设置 GraphicsDevice.Textures[0] = null。根据您所追求的效果,可能会有更高性能的方法,您还可以考虑 Silverlights WriteableBitmap。

编辑:这是我在模拟器中测试的代码:

Texture2D canvas;
Rectangle tracedSize;
UInt32[] pixels;

protected override void Initialize()
{
    tracedSize = GraphicsDevice.PresentationParameters.Bounds;
    canvas = new Texture2D(GraphicsDevice, tracedSize.Width, tracedSize.Height, false, SurfaceFormat.Color);
    pixels = new UInt32[tracedSize.Width * tracedSize.Height];

    base.Initialize();
}
Random rnd = new Random();
protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    GraphicsDevice.Textures[0] = null;
    pixels[rnd.Next(pixels.Length)] = 0xFF00FF00;
    canvas.SetData<UInt32>(pixels, 0, tracedSize.Width * tracedSize.Height);

    spriteBatch.Begin();
    spriteBatch.Draw(canvas, new Rectangle(0, 0, tracedSize.Width, tracedSize.Height), Color.White);
    spriteBatch.End();

    base.Draw(gameTime);
}

【讨论】:

  • 非常感谢,我从你的例子中得到了它。我想我第一次尝试时可能是在 Draw() 中新建一个 Texture2D。
【解决方案2】:

您基本上需要按照异常中的要求进行操作:

为确保图形设备上没有设置纹理,请将其放在Draw的末尾:

GraphicsDevice.Textures[0] = null;

为确保您没有在平铺支架内绘图,请不要在 Draw 内使用 SetData。将对SetData 的呼叫移至Update


额外信息:您的内存不足错误是因为您没有释放 Texture2D 分配的非托管资源(垃圾收集器无法跟踪它们,因此它不知道您的内存不足)。您需要在纹理上调用Dispose。然而,每帧制作一个新纹理无论如何都是一个坏主意(没有办法避免它导致的性能和内存碎片问题)。

【讨论】:

    【解决方案3】:

    永远不要在 Draw() ** 中创建或修改纹理。**

    SpriteBatch.Draw() 调用期望 GraphicsDevice 缓冲区包含所有纹理数据(通过 GPU 缓冲区扩展),此时仍在发生的任何更改(来自 Update 和 IsRunningSlowly == true)将导致撕裂渲染。

    GraphicsDevice.Textures[0] = null; 的解决方法会阻止 Draw 调用和游戏,直到传输到 GPU 完成,从而减慢整个游戏循环。

    重用在类级别声明的 Texure2D 对象。

    【讨论】:

      猜你喜欢
      • 2021-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-20
      相关资源
      最近更新 更多