Question

I'm having some strange behaviour inside a WPF app I'm writing. When I run an animation on the camera position (a Point3DAnimation on PerspectiveCamera.PositionProperty), I get really bad flickering artifacts inside the application. The 3D-rendered object seems to disappear for certain frames and allows the background of the window to show through.

I've written a very simple sample application below to demonstrate the problem on my machine. To use it, just compile it and use the arrow up and down keys to zoom in and out. The problem is very repeatable on my machine: every time I try to zoom in or out, the object flickers during the animation and then becomes 'solid' again once the animation is complete.

I'm running Windows 7 32-bit and using an NVIDIA GeForce 8600GT. Here are a few interesting details:

1) It seems to be hardware-related. I placed a post in the WPF forums and one user responded saying everything looked fine to him. I've asked a few friends to try it, and one reported exactly the same flickering I was experiencing and the other said everything looked fine.

2) Forcing vertical sync and enabling triple buffering through the NVIDIA Control Panel doesn't fix the problem.

3) Reducing the desired FPS of the animation significantly improves the problem. At low desired framerate (say, 5FPS), the flickering disappears... but then the animations look terrible. The sample application I provide below is only showing a single image mapped onto a quad, so I don't believe it should be an issue of processing power!

4) The problem seems to be directly related to polygon vertices going outside of the viewable window. If I set the closeDist value in the program to 4 (such that even when 'zoomed in' the object still fits completely within the window), there is no flickering. However, as I increase the closeDist, as soon as I get to values wherein the 'zoomed in' state has the vertices going outside of the window, I get flicker occurring. The flicker appears to get progressively worse as I increase the closeDist. At a value of 9.8 (just before the camera's NearPlaneDistance would cut the object off completely), flickering is worst.

Without further ado, here's the sample code!

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);
    }
  }
}
Was it helpful?

Solution 5

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!

OTHER TIPS

It's old old question, but since I struggled with exact same problem and finally found a working solution, I thought a note here might be worth it:

As Abram already mentioned, NearPlaneDistance was the solution for me, but I didn't set it to small value at all. In fact, for me, I had to go all the way up to 25. In the model I'm drawing, everything is big. 1 unit is 1 millimeter, and no planes are drawn closer than 10 units from each other. As I kept increasing the NearPlaneDistance higher and higher, the tearing got less and less, until at 25 everything works great.

So, if anyone else is struggling with this, experiment with NearPlaneDistance.

I ran into a similar problem using PerspectiveCamera with dynamic camera positioning. It seems that the camera gets "confused" about whether or not to show an object (or parts of it) when it is too close to the camera and part of any object - even an object in the "background" - is partially obscured...

Try setting the "NearPlaneDistance" for your PerspectiveCamera to a low, but non-zero, value, such as 0.001.

I know this is an older question, but I ran into this bug at work and was up against the wall for about 10 hours before I finally found the solution. I hope this helps someone else out-

You need to manually set the height/width of the Viewport3d. It doesn't have to be hard-coded (you can bind it, hard-code it, place the viewport in a grid, etc). From my experience, the size of the viewport must be LESS THAN the window size.

The working assumption here is that WPF is having some trouble deciding whether the image in the viewport is within the viewable window.

Anyway, HTH

Your animation is triggered by a KeyDown event - if a user holds the key down, you may be overwhelming your application with BeginAnimation calls.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top