如何消除动画撕裂?

我在WinForms应用程序中以18.66666运行动画…每秒帧数(它与140 BPM的音乐同步,这就是为什么帧速率很奇怪)。 动画的每个单元都是预先计算的,动画由高分辨率多媒体计时器驱动。 动画本身很流畅,但是我看到了大量的“撕裂”,或者是因为屏幕刷新中途被cels捕获而导致的文物。

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

这是为什么WMP能够渲染动画而不会撕裂,或者我错过了什么? 有没有什么方法可以使用DirectX(或其他东西),以使我的程序能够知道当前扫描线的位置,如果是,有什么方法可以使用该信息消除撕裂而不实际使用DirectX用于显示cels? 或者我是否必须完全使用DirectX进行渲染才能解决此问题?

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

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

当您的映像更新与监视器的刷新率不同步时,会发生撕裂。 显示器显示上一张图像的一部分,即新图像的一部分。 当图像中的对象快速移动时,效果非常明显。

它无法在Windows窗体中修复,您无法访问video适配器的v-sync信号。 你可以在DirectX应用程序中 。

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

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

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

你最好使用directx(或opengl)来完成这些任务。 但是如果你只想使用winforms使用DoubleBuffered属性。

双缓冲它。

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

如果这不起作用,那么最好的办法是bitblit和double buffer。

基本上它是一样的。

有一个参考两个位图,一个是屏幕,另一个是缓冲区。 你首先绘制到缓冲区,然后将整个事物blit到屏幕上。 这样您只需将实时数据写入缓冲区。 这个场景简单地展示了你之前制作的东西(蓝色彼得风格)

撕裂是从另一个上面绘制的框架中的人工制品。 避免它的唯一安全方法是a)从vsync等待或b)在梁后面画(这是相当棘手的)。 单独使用双缓冲区并不能保证不会撕裂,因为你可以使用双缓冲区,但仍然可以关闭vsync。 某些卡也可能具有“强制关闭”的vsync等待选项。 您需要查看有关vsync的文档以及如何检查它的位置。 这是唯一安全的方法。 此外,请记住,这将锁定您的帧率。