4

我正在编写的 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);
    }
  }
}
4

5 回答 5

2

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

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

因此,如果其他人为此苦苦挣扎,请尝试 NearPlaneDistance。

于 2016-07-07T10:40:35.543 回答
1

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

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

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

无论如何,HTH

于 2012-01-04T17:31:30.103 回答
1

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

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

于 2011-02-23T14:56:44.573 回答
0

I'm going to mark this as resolved even though there was no actual solution found. As long the models were kept within the region of the screen and didn't extend past it, the flickering didn't occur. Sorry!

于 2011-08-24T19:55:49.680 回答
0

你的动画是由一个事件触发的KeyDown——如果用户按住键,你可能会用 BeginAnimation 调用来压倒你的应用程序。

于 2011-02-15T16:06:07.223 回答