【问题标题】:WPF flickering during animation in Viewport3D在 Viewport3D 中的动画期间 WPF 闪烁
【发布时间】:2011-06-27 17:20:53
【问题描述】:

我正在编写的 WPF 应用程序中有一些奇怪的行为。当我在相机位置上运行动画时(PerspectiveCamera.PositionProperty 上的 Point3DAnimation),我在应用程序中得到了非常糟糕的闪烁伪影。对于某些帧,3D 渲染的对象似乎消失了,并允许窗口的背景显示出来。

我在下面编写了一个非常简单的示例应用程序来演示我机器上的问题。要使用它,只需编译它并使用向上和向下箭头键来放大和缩小。这个问题在我的机器上很容易重复:每次我尝试放大或缩小时,对象都会在动画期间闪烁,然后在动画完成后再次变为“实心”。

我正在运行 Windows 7 32 位并使用 NVIDIA GeForce 8600GT。以下是一些有趣的细节:

1) 似乎与硬件有关。我在 WPF 论坛上发了一个帖子,一位用户回复说一切对他来说都很好。我已经请了几个朋友尝试一下,其中一位报告的闪烁与我遇到的完全相同,另一位则说一切正常。

2) 通过 NVIDIA 控制面板强制垂直同步和启用三重缓冲并不能解决问题。

3) 降低动画所需的 FPS 会显着改善问题。在所需的低帧速率(例如 5FPS)下,闪烁消失了……但是动画看起来很糟糕。我在下面提供的示例应用程序仅显示了映射到四边形的单个图像,因此我认为这不应该是处理能力的问题!

4) 问题似乎与可见窗口之外的多边形顶点直接相关。如果我在程序中将 closeDist 值设置为 4(这样即使“放大”对象仍然完全适合窗口),则 没有闪烁。但是,当我增加 closeDist 时,一旦达到“放大”状态的顶点超出窗口的值,就会发生闪烁。随着我增加 closeDist,闪烁似乎逐渐更糟。值为 9.8(就在相机的 NearPlaneDistance 完全切断对象之前),闪烁最严重。

事不宜迟,下面是示例代码!

MainWindow.xaml:

<Window x:Class="WPFFlickerTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        KeyDown="Window_KeyDown">
    <Grid>
        <Viewport3D Name="Viewport">
            <Viewport3D.Camera>
                <PerspectiveCamera LookDirection="0,0,1" FieldOfView="70" x:Name="viewportCam" />
            </Viewport3D.Camera>

            <ModelVisual3D>
                <ModelVisual3D.Content>
                    <AmbientLight />
                </ModelVisual3D.Content>
            </ModelVisual3D>
        </Viewport3D>
    </Grid>
</Window>

MainWindow.xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Media3D;
using System.Windows.Media.Animation;

namespace WPFFlickerTest
{
  public partial class MainWindow : Window
  {
    // time the camera animation takes to complete
    private const double animTime = 0.25;

    // path to an image to use (assuming it's 1920x1200 or 1.6 aspect ratio)
    private const string imagePath = "C:/Windows/Web/Wallpaper/Windows/img0.jpg";

    // far and close camera distances
    private const double closeDist = 8, farDist = 10;

    // chosen to align with aspect ratio of the image
    private const double halfW = 4.8, halfH = 3;

    public MainWindow()
    {
      InitializeComponent();

      Model3DGroup modelGroup = new Model3DGroup();

      // set up the mesh
      MeshGeometry3D mesh = new MeshGeometry3D();
      mesh.Positions.Add(new Point3D(-halfW, halfH, farDist));
      mesh.Positions.Add(new Point3D(halfW, halfH, farDist));
      mesh.Positions.Add(new Point3D(halfW, -halfH, farDist));
      mesh.Positions.Add(new Point3D(-halfW, -halfH, farDist));

      // set up triangle indices
      mesh.TriangleIndices = (Int32Collection)new Int32CollectionConverter().ConvertFromString(
        "0,1,2 2,3,0");

      // set up texture coords
      mesh.TextureCoordinates = (PointCollection)new PointCollectionConverter().ConvertFromString(
        "1,0 0,0 0,1 1,1");

      // set up the brush
      ImageBrush brush = new ImageBrush(new BitmapImage(new Uri(imagePath, UriKind.Relative)));

      // create a geometry model based on the mesh and give it a material based on an image
      GeometryModel3D geom = new GeometryModel3D(mesh, new DiffuseMaterial(brush));

      // add the object
      modelGroup.Children.Add(geom);

      // we should have filled in our objects now
      // so we'll just add them to the viewport
      ModelVisual3D modelVisual = new ModelVisual3D();
      modelVisual.Content = modelGroup;
      Viewport.Children.Add(modelVisual);
    }

    // react to keypresses
    private void Window_KeyDown(object sender, KeyEventArgs e)
    {
      switch (e.Key)
      {
        // move the camera to the centre
        case Key.Down: AnimateTo(new Point3D(0, 0, 0)); break;

        // move the camera to the currently targeted image
        case Key.Up: AnimateTo(new Point3D(0, 0, closeDist)); break;
      }
    }

    // animate to a given position
    void AnimateTo(Point3D position)
    {
      Point3DAnimation camPosAnim = new Point3DAnimation(position, TimeSpan.FromSeconds(animTime));
      viewportCam.BeginAnimation(PerspectiveCamera.PositionProperty, camPosAnim);
    }
  }
}

【问题讨论】:

  • 试一试,我觉得它看起来不错......所以你的预感确实与硬件相关听起来很准确。我在笔记本电脑上...没什么特别的...运行 .NET 3.5 SP1;你更新你的 DirectX/Direct3D 了吗?
  • @Aaron 很好的建议。我尝试获取最新的 DirectX 运行时,但被告知我已经有了更新或等效的版本。 dxdiag 显示我正在运行 DirectX 11。我注意到另一个有趣的花絮,我将在下面发布。
  • @Aaron 有趣。我可能会尝试降级以查看是否可以解决问题。我刚刚添加了“有趣的细节 #4”。它似乎与超出窗口边界的多边形顶点有关。也许要让问题在您的机器上显示出来,请尝试将 closeDist 值更改为 9.8 以增加缩放因子以查看是否有任何闪烁。感谢您的帮助!
  • @aardvarkk 我在 closeDist = 8, farDist = 10 运行它,我注意到动画中的过渡,但无论如何我都不会认为它们不好;所以我很确定事情的动画效果仍然很好
  • @Aaron 看来你已经幸免于难了!在我的机器上效果非常很明显。感谢您调查它。我会看看我能不能抓住更多有趣的花絮……

标签: wpf animation 3d flicker perspectivecamera


【解决方案1】:

您的动画由 KeyDown 事件触发 - 如果用户按住该键,您的应用程序可能会因 BeginAnimation 调用而不堪重负。

【讨论】:

  • 好建议,但我认为这不是问题所在。我尝试了两件事来测试这个(两者都不能解决问题):1)在程序第一次启动时只动画一次,2)在 keydown 事件触发时添加控制台输出。它只显示一个输出,所以我假设如果我点击键它只会被调用一次。不过感谢您的帮助!
【解决方案2】:

我在使用带有动态相机定位的 PerspectiveCamera 时遇到了类似的问题。当相机离相机太近并且任何对象的一部分(甚至是“背景”中的对象)被部分遮挡时,相机似乎会“混淆”是否显示对象(或部分对象)。 ..

尝试将 PerspectiveCamera 的“NearPlaneDistance”设置为较低但非零的值,例如 0.001。

【讨论】:

  • 感谢您的建议!我相信我尝试将相机上的 NearPlaneDistance 设置为一个非常小的值,但我明天会再试一次,看看是否能解决问题。我们当前的解决方法是不要将对象“放大”到足够远,以至于其任何顶点都超出了窗口的范围。当顶点停留在窗口内时,动画不会闪烁。
  • 刚刚用 0.01 的值再次尝试,我可以确保有问题的对象永远不会那么接近。闪烁不是固定的。好主意!
  • 感谢您的建议。对我来说,我根据移动相机的 Y 位置将 NearPlaneDistance 设置为不同的值。
【解决方案3】:

即使没有找到实际的解决方案,我也会将此标记为已解决。只要模型保持在屏幕区域内并且没有超出它,就不会发生闪烁。对不起!

【讨论】:

    【解决方案4】:

    我知道这是一个较老的问题,但我在工作中遇到了这个错误,并且在我最终找到解决方案之前,我已经在墙上苦苦挣扎了大约 10 个小时。我希望这对其他人有所帮助-

    您需要手动设置 Viewport3d 的高度/宽度。它不必是硬编码的(你可以绑定它,硬编码它,将视口放在网格中,等等)。根据我的经验,视口的大小必须小于窗口大小。

    这里的工作假设是 WPF 在确定视口中的图像是否在可视窗口内时遇到了一些问题。

    不管怎样,HTH

    【讨论】:

      【解决方案5】:

      这是一个老问题,但由于我一直在努力解决完全相同的问题并最终找到了一个可行的解决方案,所以我认为这里可能值得一提:

      正如 Abram 已经提到的,NearPlaneDistance 是我的解决方案,但我根本没有将它设置为小值。事实上,对我来说,我必须一直到 25 岁。在我绘制的模型中,一切都很大。 1 个单位是 1 毫米,没有平面之间的距离小于 10 个单位。随着我不断增加 NearPlaneDistance 越来越高,撕裂越来越少,直到 25 一切正常。

      因此,如果其他人对此感到困扰,请尝试 NearPlaneDistance。

      【讨论】:

      • 感谢您的建议。我加载了一张非常大的地图,当相机走远时,闪烁的渲染开始了。通过相机移动时的Y位置,我为每个范围设置了不同的NearPlaneDistance,我得到了很好的结果,没有闪烁。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-02
      • 2015-08-10
      • 2011-08-13
      • 2016-07-02
      • 1970-01-01
      • 2019-05-18
      • 2011-08-08
      相关资源
      最近更新 更多