【问题标题】:How to eliminate tearing from animation?如何消除动画中的撕裂?
【发布时间】:2010-03-16 21:12:33
【问题描述】:

我在 WinForms 应用程序中以每秒 18.66666... 帧的速度运行动画(它以 140 BPM 的速度与音乐同步,这就是帧速率奇怪的原因)。动画的每个 cel 都是预先计算好的,动画由高分辨率的多媒体定时器驱动。动画本身很流畅,但我看到了大量的“撕裂”,或者是由于屏幕刷新中途捕捉到的细胞而导致的伪影。

当我将程序渲染的一组 cel 写入 AVI 文件,然后在 Windows Media Player 中播放 AVI 文件时,我根本看不到任何撕裂。我假设 WMP 可以流畅地播放文件,因为它使用 DirectX(或其他东西)并且能够将渲染与屏幕的刷新活动同步。它不会改变帧速率,因为动画与音频保持同步。

这就是为什么 WMP 能够在不撕裂的情况下渲染动画,还是我遗漏了什么?有什么方法可以使用 DirectX(或其他东西)以使我的程序知道当前扫描线的位置,如果是这样,有什么方法可以在不实际使用 DirectX 的情况下使用该信息来消除撕裂用于显示细胞?还是我必须完全使用 DirectX 进行渲染才能解决这个问题?

更新:忘记了一个细节。我的应用程序使用 Graphics.DrawImage 将每个单元格呈现到 PictureBox 上。这是否比使用 BitBlt 慢得多,这样我可以通过使用 BitBlt 至少消除一些撕裂?

更新2:我看到的效果肯定不是闪烁(和撕裂不同)。我的面板是双缓冲的,为 AllPaintingInWmPaint、UserPaint、OptimizedDoubleBuffer 等设置控件样式,覆盖 onPaintBackGround 等。所有这些都是消除闪烁所必需的,但撕裂问题仍然存在。当动画具有非常快速移动的对象或从亮到暗非常迅速地变化的对象时,这一点尤其明显。当物体移动缓慢且不快速改变颜色时,撕裂效果就不太明显(因为连续的细胞总是彼此非常相似)。

【问题讨论】:

  • 您是否在单独的线程上运行渲染?
  • @Ardman:是的,渲染是在单独的线程上完成的(与 UI 线程分开)。

标签: c# .net winforms animation directx


【解决方案1】:

当您的图像更新与显示器的刷新率不同步时,就会出现撕裂现象。监视器显示前一个图像的一部分,新图像的一部分。当图像中的物体快速移动时,效果非常明显。

它在 Windows 窗体中无法修复,您无法获取视频适配器的垂直同步信号。你可以在DirectX app

【讨论】:

    【解决方案2】:

    我在我目前正在进行的项目中尝试了双缓冲的想法,但我没有得到很好的结果。最后,我创建了以下内容:

    1. 我的屏幕外缓冲区的 System.Drawing.Bitmap。将动画解码为该位图。
    2. 与 (1) 中的图像大小相同且 OnPaintBackground 方法为空(不绘图,不调用基类)并且 OnPaint 执行 Graphics.DrawImage 以将屏幕外图像复制到屏幕的 UserControl。

    现在,您的动画速度很奇怪,所以撕裂几乎肯定与屏幕更新率和屏幕刷新率之间的不匹配有关。您正在屏幕刷新中途更新屏幕,因此屏幕在屏幕顶部绘制旧框架,在屏幕底部绘制新框架。如果您可以将帧率与显示刷新率同步,则撕裂应该会消失。

    【讨论】:

    • 您所描述的基本上就是我正在做的事情,尽管我使用两个位图作为屏幕外缓冲区,这样我可以在另一个被绘制到屏幕时更新一个。我将尝试将我的动画与我的刷新率 (60Hz) 同步,看看这对我有什么影响。
    【解决方案3】:

    您最好使用 directx(或 opengl)来执行此类任务。但是,如果您只想使用 winforms,请使用 DoubleBuffered 属性。

    【讨论】:

    • 我使用DoubleBuffered来消除闪烁,但对撕裂没有帮助。
    • 另外,我正在尝试使用 Mono 使我的应用程序跨平台,所以我想尽可能避免使用 DirectX 等特定于平台的东西。
    • 所以使用opengl!它是directx 的跨平台模拟。好吧,我认为winforms不是跨pl的。
    • 顺便说一句,这些天对 Windows.Forms 的 Mono 支持是完全可以接受的。
    • 好消息!仍然对于多媒体任务,您应该使用 opengl/directx。
    【解决方案4】:

    双缓冲。

    您可以使用 Windows 样式启用双缓冲,或者更简单的方法是从屏幕外绘制到图片,然后交换它们。

    如果这不起作用,那么最好的办法是 bitblit 和双缓冲区。

    基本上是一样的。

    有两个位图的引用,一个是屏幕,另一个是缓冲区。 您首先绘制到缓冲区,然后将整个内容传送到屏幕上。 这样,您只需要将实时数据写入缓冲区。画面只是显示了你之前制作的东西(蓝色彼得风格)

    【讨论】:

    • 对不起,我的问题应该更清楚。双缓冲(我已经在使用)对撕裂效果没有帮助。
    • 好吧,不用担心。然后不要使用图片框,直接写入屏幕(双缓冲)
    【解决方案5】:

    撕裂是一个框架被绘制在另一个框架之上的工件。避免它的唯一安全方法是 a) 从 vsync 等待或 b) 在光束后面绘制(这相当棘手)。单独的双缓冲并不能保证不会撕裂,因为您可以拥有双缓冲但仍然可以在关闭垂直同步的情况下进行绘制。某些卡可能还具有“强制关闭”的 vsync 等待选项。您需要查看有关 vsync 的文档以及如何检查它的位置。这是唯一安全的方法。另外,请记住,这会锁定您的帧率。

    【讨论】:

    • 必须有某种方法可以使用我选择的帧率(帧率受到限制,因为动画必须与音乐同步)对其进行动画处理而不会撕裂,因为 WMP 能够很好地渲染它而不会撕裂。也许 WMP 会进行某种帧间插值?
    猜你喜欢
    • 2011-07-10
    • 2011-04-09
    • 2011-01-08
    • 2011-08-13
    • 2012-12-09
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    相关资源
    最近更新 更多