如何在XNA中逐像素绘制2D图形?

7

我正在尝试使用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纹理命令、在着色器中实现)。最后一个显然是最优的,但需要了解着色器编程知识。你可能想执行相同的搜索并浏览一下相关内容。 - Tergiver
对于需要即时绘制2D像素级纹理的人,可以在这里找到答案:https://dev59.com/T2wMtIcB2Jgan1zn4x1b - atrefeu
3个回答

6
尝试在调用SetData之前设置GraphicsDevice.Textures[0] = null。根据您想要的效果,可能会有更高效的方法,您还可以考虑使用Silverlight的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。 - Curyous

3
你需要按照异常提示的要求进行操作: 为确保纹理未设置在图形设备上,请在Draw方法的结尾添加以下代码:
GraphicsDevice.Textures[0] = null;

为了确保您不在平铺括号内进行绘图,请完全不要在Draw中使用SetData。将调用SetData移动到Update中。
额外信息:您的内存不足错误是因为您没有释放Texture2D分配的非托管资源(垃圾回收器无法跟踪它们,因此它不知道您正在耗尽内存)。您需要在纹理上调用Dispose。但是每帧制作新纹理本身就不是一个好主意(无法避免它导致的性能和内存碎片化问题)。

1

绝不要在Draw() 函数中创建或修改纹理。

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

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

请重复使用在类级别上声明的 Texture2D 对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接