【问题标题】:Drawing 2D Sprite (Bitmap) TIles Efficiently高效绘制 2D Sprite(位图)平铺
【发布时间】:2010-08-21 01:03:16
【问题描述】:

我正在编写一个基于 2d 瓦片的引擎。目前,我的绘图例程使用 C# 绘图库在每次屏幕刷新时重绘每个可见图块。我开始滚动和缩放工作,一切看起来都像我想要的那样。但例行程序很慢。现在我正在努力改进它。我有几个问题:

首先,我认为没有必要在每次刷新时重新绘制图块(因为它们永远不会改变)。现在我正在尝试更改算法,以便在初始化时将整个地图写入单个位图,然后在绘制时剪切位图的正确部分。你认为这是正确的方法吗? (我也考虑过将图像留在背景中并滚动它。但后来我决定我不想绘制视野之外的东西。但是,也许这比剪切/粘贴便宜?内存与时间的问题?)

其次,据我了解,C# 绘图例程不使用 GPU 的全部功能。我想我应该尝试在 OpenGL(或 DirectX,但我更喜欢前者,因为它是多平台的)中进行绘图。那会有帮助吗?你知道 OpenGL 的任何平铺(或一般像素绘图)教程吗?参考书籍也有帮助。

我现在也不做多线程(事实上我对那是什么只有一个模糊的概念)。我应该尝试多线程抽屉吗?或者 OpenGL 会让图形的多线程变得多余?

谢谢。

【问题讨论】:

  • XNA4.0 在我提出问题两周后问世。我试过了,喜欢它,然后切换到它。 XNA 隐式管理 DirectX,因此可以让自己的精灵四处飘荡,而无需担心任何事情。非常好。我的一个朋友使用 WPF。这也很好用,但我认为您需要了解一点 DirectX(问题不大)。再次感谢您的精彩回答,伙计们。

标签: c# graphics c#-4.0 tiles sprite


【解决方案1】:

您打算使用什么应用程序框架? WinForms (Win32) 和 WPF 之间的高效绘图技术非常不同。

您说得对,.NET 绘图例程没有充分利用 GPU。使用 DirectX 或 OpenGL,一个直接的优化是使用图像列表或显示列表将所有图像块(或至少是即时视图区域所需的所有块加上更多)预加载到 GPU 内存中。然后,您将按索引在表面上绘制瓷砖 - 在 x,y 处绘制瓷砖 N。这通常比使用存储在主系统内存中的位图在 GPU 表面上绘图要快得多,因为必须为每个绘制的图块将位图像素复制到 GPU,这会占用大量时间。只要您可以在输出中的多个位置使用相同的图像块,按索引绘制也会使用更少的 GPU 内存。

OpenGL 与 DirectX 是您的选择,但 IMO DirectX 一直在以更快的速度发展,提供比 OpenGL 更多的硬件加速功能。 Windows 上的 OpenGL 驱动程序也因被硬件供应商忽视而闻名。他们主要关注 DirectX 驱动程序。

考虑一下您的 2D 平铺应用程序是否真的需要 OpenGL 或 DirectX。要求 OpenGL 或 DirectX 将减少可以运行您的应用程序的机器数量,尤其是较旧的机器。 OpenGL 和 DirectX 可能是矫枉过正。如果你很聪明的话,你可以用普通的旧 GDI 做很多事情。

在你有充分的理由去那里并且你有一些线程方面的经验之前,请远离多线程。多线程为一些计算情况提供了一些性能提升的回报,但也带来了新的麻烦和新的性能问题。在注册所有这些新难题之前,请确保收益显着。

一般来说,在屏幕上移动像素通常不适合多线程。您只有一个显示设备(在大多数情况下),因此使用多个线程尝试同时更改像素并不能很好地工作。借用项目管理的一句话:如果一个女人9个月能生一个宝宝,你能用9个女人1个月生1个宝宝吗? ;>

努力识别不需要访问相同资源或设备的系统部分。这些是在并行线程中运行的更好的候选者,而不是块块到屏幕上。

最好的优化是发现不需要完成的工作 - 例如减少重绘图块的次数,或更改为索引模型,以便可以从 GPU 内存而不是系统内存中绘制图块。

【讨论】:

  • 我正在使用 Windows 窗体。在你提到它之前,我什至不知道 WPF 的存在。我看到它类似于 Forms,除了它使用 DirectX。也许我会尝试将我的应用程序移植到 WPF 以查看会发生什么。 *** 我确实使用 GDI 将位图加载到主内存中 - 我将阅读并尝试将它们加载到 GPU 中。在你和 Blam 的回复之间,我想我会选择 DirectX,除非 GDI 中可以加载内存。我暂时避免使用多线程。 *** 感谢您如此迅速地回复!
  • 因为 WinForms 应用程序只是 Win32 应用程序,当窗口区域被遮盖然后再次显示时,它们将被提示重新绘制窗口区域。大多数窗口绘制代码采用简单的方法,只是重绘窗口中的所有内容。为了获得更好的性能,尤其是在大绘图负载的情况下,您应该注意剪切矩形,并且只重绘窗口中实际上无效的部分。这对滚动也很有帮助,因为向上滚动 10 个像素只会使窗口底部的 10 个像素无效,而不是整个窗口。
  • WPF 使用完全不同的绘图范例,需要非常不同的优化技术。 WPF 缓存绘制的图像,以便在未发现时快速重绘,而无需调用您的代码。 WPF 应用程序中没有 WM_PAINT 循环(您参与其中)。 WPF 可能在底层使用 DirectX 进行绘图,因此与 WinForms 相比,使用普通 WPF 控件和对象可能有更好的机会利用 GPU 资源。
  • "为了获得更好的性能,尤其是在大绘图负载下,您应该注意剪切矩形,并且只重绘窗口中实际上无效的部分。"
【解决方案2】:

如果你想使用 OpenGL,你最好的 2d 选择是SDL。 将 OpenGL 与 C# 一起使用永远不会那么便携,因为它会使用 .NET 包装器。

XNA 是用 C# 编写游戏的好工具,它应该比 SDL(尤其是 .net 端口)提供更多的速度和灵活性以及更多的功能(但体积更大)。

对于您的剪切或滚动问题,最好的方法是滚动。 当您使用 GDI+(System.Drawing 使用的)绘图时,内存比 CPU 的问题要小得多。您总是可以将地图分成多个部分并滚动它们,然后在必要时加载(如果它那么大)。

【讨论】:

  • 你是对的!我设法让切割算法工作,它甚至比前一个慢。然后我让滚动的工作(它比我想象的要容易),它似乎更快(虽然它搞砸了缩放 - 现在我在缩放时重新绘制大位图,这会导致动作暂停一秒钟)。感谢您提供 SDL 链接,我会调查一下。
  • 我听说过 XNA。几天前我尝试安装它,安装失败,我忘记了。是不是真的值得吗?对我来说,这看起来像是另一个“游戏制作者”类型的程序。
  • Nononono,XNA 更多用户友好类型的框架,但这并不意味着它是任何类似的游戏制作者。查看一些教程,以便您了解它的工作原理,然后自行决定。正如我所说,它应该比 SDL 快得多,并提供更多功能。 Plus XNA 是为 C# 制作的 :)
  • 我明白了!昨天尝试再次安装它 - 意识到问题是我使用的是 VS2010 而 XNA3.1 是针对 VS2008 的。不过,我想我在另一台机器上安装了 VS2008。我会在那里看看 XNA。
【解决方案3】:

我不熟悉 OpenGL,但我在 ManagedDX(后来移植到 XNA)中编写了一个基于 tile 的引擎。 ManagedDX 已弃用,但 SlimDX 项目仍在积极开发中。

使用 DX,您可以将每个单独的图块加载到 Texture。 (例如使用Texture.FromFile()Texture.FromStream()),并让一个Sprite 实例绘制它们。这表现相当不错。我将纹理分组在一个简单的类中,并使用 Point 或 Vector2 作为它们的位置,仅在位置更改时设置它们,而不是在每次调用 draw 方法时设置它们。我只在内存中缓存当前屏幕和一两个图块之外的图块,不需要更多,因为文件 IO 足够快,可以在滚动时获取新图块。

struct Tile
{
    public Point Location;
    public Texture Texture;
}

Device device;
Sprite sprite;
List<Tile> tiles = new List<Tile>();

.ctor() {
    this.device = new Device(...)
    this.sprite = new Sprite(this.device);
}

void Draw() {
    this.device.Clear(ClearFlags.Target, Color.CornflowerBlue, 1.0f, 0);
    this.device.BeginScene();
    this.sprite.Begin(SpriteFlags.AlphaBlend);
    foreach (Tile tile in this.tiles) {
        this.sprite.Draw2D(tile.Texture, 
            new Point(0, 0), 0.0f, tile.Location, Color.White);
    }
    this.sprite.End();
    this.device.EndScene();
    this.device.Present();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-08-23
    • 1970-01-01
    • 2015-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-28
    相关资源
    最近更新 更多