c# - WPF 中的快速二维图形

标签 c# wpf performance drawing drawingvisual

我需要在 WPF 中绘制大量的二维元素,例如直线和多边形。他们的位置也需要不断更新。

我看过这里的许多答案,其中大部分建议使用 DrawingVisual 或重写 OnRender 函数。为了测试这些方法,我实现了一个简单的粒子系统渲染 10000 个椭圆,我发现使用这两种方法的绘图性能仍然非常糟糕。在我的电脑上,我每秒不能超过 5-10 帧。当你认为我使用其他技术轻松平滑地绘制 1/2 百万个粒子时,这是完全不能接受的。

所以我的问题是,我是否遇到了 WPF 的技术限制,或者我是否遗漏了什么?还有什么我可以用的吗?欢迎任何建议。

这里是我试过的代码

MainWindow.xaml 的内容:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="500" Width="500" Loaded="Window_Loaded">
    <Grid Name="xamlGrid">

    </Grid>
</Window>

MainWindow.xaml.cs 的内容:

using System.Windows.Threading;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }


        EllipseBounce[]     _particles;
        DispatcherTimer     _timer = new DispatcherTimer();

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {

            //particles with Ellipse Geometry
            _particles = new EllipseBounce[10000];

            //define area particles can bounce around in
            Rect stage = new Rect(0, 0, 500, 500);

            //seed particles with random velocity and position
            Random rand = new Random();

            //populate
            for (int i = 0; i < _particles.Length; i++)
            {
               Point pos = new Point((float)(rand.NextDouble() * stage.Width + stage.X), (float)(rand.NextDouble() * stage.Height + stage.Y));
               Point vel = new Point((float)(rand.NextDouble() * 5 - 2.5), (float)(rand.NextDouble() * 5 - 2.5));
                _particles[i] = new EllipseBounce(stage, pos, vel, 2);
            }

            //add to particle system - this will draw particles via onrender method
            ParticleSystem ps = new ParticleSystem(_particles);


            //at this element to the grid (assumes we have a Grid in xaml named 'xmalGrid'
            xamlGrid.Children.Add(ps);

            //set up and update function for the particle position
            _timer.Tick += _timer_Tick;
            _timer.Interval = new TimeSpan(0, 0, 0, 0, 1000 / 60); //update at 60 fps
            _timer.Start();

        }

        void _timer_Tick(object sender, EventArgs e)
        {
            for (int i = 0; i < _particles.Length; i++)
            {
                _particles[i].Update();
            }
        }
    }

    /// <summary>
    /// Framework elements that draws particles
    /// </summary>
    public class ParticleSystem : FrameworkElement
    {
        private DrawingGroup _drawingGroup;

        public ParticleSystem(EllipseBounce[] particles)
        {
            _drawingGroup = new DrawingGroup();

            for (int i = 0; i < particles.Length; i++)
            {
                EllipseGeometry eg = particles[i].EllipseGeometry;

                Brush col = Brushes.Black;
                col.Freeze();

                GeometryDrawing gd = new GeometryDrawing(col, null, eg);

                _drawingGroup.Children.Add(gd);
            }

        }


        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            drawingContext.DrawDrawing(_drawingGroup);
        }
    }

    /// <summary>
    /// simple class that implements 2d particle movements that bounce from walls
    /// </summary>
    public class SimpleBounce2D
    {
        protected Point     _position;
        protected Point     _velocity;
        protected Rect     _stage;

        public SimpleBounce2D(Rect stage, Point pos,Point vel)
        {
            _stage = stage;

            _position = pos;
            _velocity = vel;
        }

        public double X
        {
            get
            {
                return _position.X;
            }
        }


        public double Y
        {
            get
            {
                return _position.Y;
            }
        }

        public virtual void Update()
        {
            UpdatePosition();
            BoundaryCheck();
        }

        private void UpdatePosition()
        {
            _position.X += _velocity.X;
            _position.Y += _velocity.Y;
        }

        private void BoundaryCheck()
        {
            if (_position.X > _stage.Width + _stage.X)
            {
                _velocity.X = -_velocity.X;
                _position.X = _stage.Width + _stage.X;
            }

            if (_position.X < _stage.X)
            {
                _velocity.X = -_velocity.X;
                _position.X = _stage.X;
            }

            if (_position.Y > _stage.Height + _stage.Y)
            {
                _velocity.Y = -_velocity.Y;
                _position.Y = _stage.Height + _stage.Y;
            }

            if (_position.Y < _stage.Y)
            {
                _velocity.Y = -_velocity.Y;
                _position.Y = _stage.Y;
            }
        }
    }


    /// <summary>
    /// extend simplebounce2d to add ellipse geometry and update position in the WPF construct
    /// </summary>
    public class EllipseBounce : SimpleBounce2D
    {
        protected EllipseGeometry _ellipse;

        public EllipseBounce(Rect stage,Point pos, Point vel, float radius)
            : base(stage, pos, vel)
        {
            _ellipse = new EllipseGeometry(pos, radius, radius);
        }

        public EllipseGeometry EllipseGeometry
        {
            get
            {
                return _ellipse;
            }
        }

        public override void Update()
        {
            base.Update();
            _ellipse.Center = _position;
        }
    }
}

最佳答案

我相信所提供的示例代码已经很不错了,并且展示了框架的局限性。在我的测量中,我分析了 15-25 毫秒的平均成本归因于渲染开销。本质上,我们在这里谈论的只是中心(依赖)属性的修改,这是非常昂贵的。我认为它很昂贵,因为它会将更改直接传播到 mil-core。

一个重要的注意事项是,开销成本与模拟中位置发生变化的对象数量成正比。当大多数对象在时间上是一致的,即不改变位置时,在自身上渲染大量对象不是问题。

对于这种情况,最好的替代方法是求助于 D3DImage ,这是 Windows Presentation Foundation 的一个元素,用于呈现使用 DirectX 呈现的信息。一般来说,这种方法应该是有效的,性能明智的。

关于c# - WPF 中的快速二维图形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16107877/

相关文章:

c# - WPF - 如何向标签添加效果(如阴影)

Azure Blob 存储流性能问题

android - android中的通用缩放 subview

performance - 如何用动画过滤来展示大数据集?

c# - finally 在哪里是必要的?

c# - 从数据表中删除特定行

c# - WPF 列表框项目消耗太多内存

c# - 我应该创建什么而不是 Bootstrap ? Prism 6.1.0

c# - 有关在 .NET 中使用消息队列服务总线的问题

c# - 返回值的方法不能传递给异常处理程序