WPF在Viewport3D中的动画期间闪烁

时间:2011-02-15 16:01:43

标签: wpf animation 3d flicker perspectivecamera

我在写作的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);
    }
  }
}

5 个答案:

答案 0 :(得分:2)

这是一个古老的问题,但由于我遇到了完全相同的问题并最终找到了一个有效的解决方案,我认为这里的注释可能是值得的:

正如亚伯兰已经提到的,NearPlaneDistance对我来说是解决方案,但我根本没有把它设置为小值。事实上,对我来说,我必须一直到25岁。在我画的模型中,一切都很大。 1个单位是1毫米,并且没有平面彼此拉近10个单位。随着我不断增加NearPlaneDistance越来越高,撕裂越来越少,直到25岁时一切都很好。

所以,如果其他人正在努力解决这个问题,请尝试使用NearPlaneDistance。

答案 1 :(得分:1)

我使用带有动态相机定位的PerspectiveCamera遇到了类似的问题。当相机太靠近相机并且任何物体的一部分 - 甚至是“背景”中的物体 - 被部分遮挡时,相机似乎对于是否显示物体(或部分物体)感到“困惑”。 ..

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

答案 2 :(得分:1)

我知道这是一个较老的问题,但我在工作中碰到了这个错误,并且在我终于找到解决方案之前已经在墙上撞了大约10个小时。我希望这可以帮助其他人 -

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

这里的工作假设是WPF在判断视口中的图像是否在可视窗口内时遇到了一些麻烦。

无论如何,HTH

答案 3 :(得分:0)

您的动画由KeyDown事件触发 - 如果用户按住该键,您可能会使用BeginAnimation呼叫压倒您的应用程序。

答案 4 :(得分:0)

即使找不到实际的解决方案,我也会将其标记为已解决。只要模型保持在屏幕区域内并且没有延伸过屏幕,就不会发生闪烁。遗憾!