我正在 WPF 應用程式中創建彩色網格圖,請參閱下面的簡化示例專案。

由于我希望將其擴展到 3D,因此我目前使用 Viewport3D 作為基礎。但是現在我注意到它變得非常慢并且在增加色點數量時會使用大量記憶體。
適用于 500x500 點,適用于 1000x1000,但需要能夠處理更多。
非常感謝有關如何提高性能或在 WPF 中執行此操作以提高性能的替代工具(包括 2D)的建議!
下面包括我的簡化示例的三個檔案。
MainWindow.xaml:
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!--#region Settings-->
<GroupBox Header="Settings" >
<WrapPanel Orientation="Horizontal">
<WrapPanel Margin="10,0">
<TextBlock Text="Number of Horizontal Positions:" />
<TextBox Text="{Binding NoOfHorizontalPoints, UpdateSourceTrigger=LostFocus}" Width="60" Margin="5,0,10,0"/>
</WrapPanel>
<WrapPanel Margin="10,0" >
<TextBlock Text="Number of Vertical Positions:" />
<TextBox Text="{Binding NoOfVerticalPoints, UpdateSourceTrigger=LostFocus}" Width="60" Margin="5,0,10,0"/>
</WrapPanel>
</WrapPanel>
</GroupBox>
<!--#endregion Settings-->
<!--#region Graphics-->
<Grid Grid.Row="1">
<local:Viewport2DCamera x:Name="viewport" Grid.Column="1" Grid.Row="1">
<local:Viewport2DCamera.Camera>
<PerspectiveCamera
LookDirection="0,0,1"
UpDirection="0,1,0"
Position="0,0,-3"
FieldOfView="45" />
</local:Viewport2DCamera.Camera>
<local:Viewport2DCamera.Children>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup >
<Model3DGroup.Children>
<AmbientLight Color="White" />
<GeometryModel3D Geometry="{Binding Geometry}" Material="{Binding Material}"/>
</Model3DGroup.Children>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</local:Viewport2DCamera.Children>
</local:Viewport2DCamera >
</Grid>
<!--#endregion Graphics-->
</Grid>
MainViewModel.cs:
public class MainViewModel : INotifyPropertyChanged
{
#region ---------------- Fields ----------------
private MeshGeometry3D _geometry;
private readonly Material _material;
private int _noOfHorizontalPoints;
private int _noOfVerticalPoints;
#endregion ------------- Fields ----------------
#region -------------- Properties --------------
public MeshGeometry3D Geometry
{
get { return _geometry; }
set
{
if (_geometry != value)
{
_geometry = value;
this.OnPropertyChanged();
}
}
}
public Material Material
{
get { return _material; }
}
public int NoOfHorizontalPoints
{
get { return _noOfHorizontalPoints; }
set
{
if (_noOfHorizontalPoints != value)
{
_noOfHorizontalPoints = value;
this.OnPropertyChanged();
}
}
}
public int NoOfVerticalPoints
{
get { return _noOfVerticalPoints; }
set
{
if (_noOfVerticalPoints != value)
{
_noOfVerticalPoints = value;
this.OnPropertyChanged();
}
}
}
#endregion ----------- Properties --------------
#region ------------- Constructors -------------
public MainViewModel()
{
_geometry = new MeshGeometry3D();
_material = new DiffuseMaterial();
_noOfHorizontalPoints = 500;
_noOfVerticalPoints = 500;
defineMaterial();
defineGeometry();
PropertyChanged = onPropertyChanged;
}
#endregion ---------- Constructors -------------
#region --------------- Methods ----------------
private void onPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(NoOfHorizontalPoints) || e.PropertyName == nameof(NoOfVerticalPoints))
{
defineGeometry();
}
}
private void defineMaterial()
{
#region Create color scheme
var gradient = new GradientStopCollection();
gradient.Add(new GradientStop(Colors.Red, 0));
gradient.Add(new GradientStop(Colors.Orange, 0.25));
gradient.Add(new GradientStop(Colors.Yellow, 0.5));
gradient.Add(new GradientStop(Colors.YellowGreen, 0.75));
gradient.Add(new GradientStop(Colors.Green, 1));
var linearGradient = new LinearGradientBrush(gradient, new Point(0, 1), new Point(1, 1));
#endregion Create color scheme
((DiffuseMaterial)_material).Brush = linearGradient;
}
private void defineGeometry()
{
double totalHeight = 1.0;
double totalWidth = 1.0;
int noOfRows = NoOfVerticalPoints;
int noOfColumns = NoOfHorizontalPoints;
double heightSizeStep = totalHeight / noOfRows;
double widthSizeStep = totalWidth / noOfColumns;
double startHeightPos = -totalHeight / 2.0;
double startWidthPos = -totalWidth / 2.0;
var geometry = new MeshGeometry3D();
var colorRandomizer = new Random();
for (int row = 0; row < noOfRows; row )
{
for (int col = 0; col < noOfColumns; col )
{
var x1 = startWidthPos col * widthSizeStep;
var y1 = startHeightPos row * heightSizeStep;
var z1 = 0.0;
var x2 = startWidthPos (1 col) * widthSizeStep;
var y2 = startHeightPos (1 row) * heightSizeStep;
var z2 = 0.0;
geometry.Positions.Add(new Point3D(x1, y1, z1));
geometry.Positions.Add(new Point3D(x2, y1, z2));
geometry.Positions.Add(new Point3D(x1, y2, z1));
geometry.Positions.Add(new Point3D(x2, y2, z2));
var lastPoint = geometry.Positions.Count - 1;
geometry.TriangleIndices.Add(lastPoint - 3);
geometry.TriangleIndices.Add(lastPoint - 1);
geometry.TriangleIndices.Add(lastPoint - 2);
geometry.TriangleIndices.Add(lastPoint - 2);
geometry.TriangleIndices.Add(lastPoint - 1);
geometry.TriangleIndices.Add(lastPoint);
#region Set color for the current points
// Colors are randomized for example
var colorPoint = new Point(colorRandomizer.NextDouble(), 1);
for (int j = 0; j < 4; j )
{
geometry.TextureCoordinates.Add(colorPoint);
}
#endregion Set color for the current points
}
}
Geometry = geometry;
}
#region INotifyPropertyChanged
/// <summary>
/// Raises the PropertyChange event for the property specified
/// </summary>
/// <param name="propertyName">Property name to update. Is case-sensitive.</param>
public virtual void RaisePropertyChanged(string propertyName)
{
OnPropertyChanged(propertyName);
}
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
OnPropertyChangedExplicit(propertyName);
}
protected void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> projection)
{
var memberExpression = (MemberExpression)projection.Body;
OnPropertyChangedExplicit(memberExpression.Member.Name);
}
void OnPropertyChangedExplicit(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion INotifyPropertyChanged
#endregion ------------ Methods ----------------
}
簡化的自定義視口物件以允許使用滑鼠左鍵進行平移:
public class Viewport2DCamera : Viewport3D
{
#region ---------------- Fields ----------------
private Point _previousMousePosition;
#endregion ------------- Fields ----------------
#region --------------- Methods ----------------
protected override void OnPreviewMouseMove(MouseEventArgs e)
{
var newPos = e.GetPosition(this as IInputElement);
double dx = newPos.X - _previousMousePosition.X;
double dy = newPos.Y - _previousMousePosition.Y;
_previousMousePosition = newPos;
if (e.MouseDevice.LeftButton is MouseButtonState.Pressed)
{
double scale = ActualWidth * ((ProjectionCamera)Camera).Position.Z *0.2;
((ProjectionCamera)Camera).Position -= new Vector3D(dx / scale, dy / scale, 0.0);
}
}
#endregion ------------ Methods ----------------
}
uj5u.com熱心網友回復:
好的,我意識到這個問題最有可能的明顯解決方案是什么。
我現在不是用代表每種顏色的矩形創建一個巨大的幾何圖形,而是創建一個每像素一種顏色的位圖影像,并將它放在一個 VisaulBrush 中,該 VisaulBrush 應用于一個只有一個矩形的簡單幾何圖形。
編輯:我第一次使用 ImageBrush,但無法讓 RenderOptions.SetBitmapScalingMode = NearestNeighbor 為它作業,因此更改為 VisualBrush,它可以在 Image 物件上設定。這用于單獨顯示每個像素,而不是平滑像素之間的顏色。
更新 MainViewModel.cs:
public class MainViewModel : INotifyPropertyChanged
{
#region ---------------- Fields ----------------
private MeshGeometry3D _geometry;
private Material _material;
private int _noOfHorizontalPoints;
private int _noOfVerticalPoints;
#endregion ------------- Fields ----------------
#region -------------- Properties --------------
public MeshGeometry3D Geometry
{
get { return _geometry; }
set
{
if (_geometry != value)
{
_geometry = value;
this.OnPropertyChanged();
}
}
}
public Material Material
{
get { return _material; }
set
{
if (_material != value)
{
_material = value;
this.OnPropertyChanged();
}
}
}
public int NoOfHorizontalPoints
{
get { return _noOfHorizontalPoints; }
set
{
if (_noOfHorizontalPoints != value)
{
_noOfHorizontalPoints = value;
this.OnPropertyChanged();
}
}
}
public int NoOfVerticalPoints
{
get { return _noOfVerticalPoints; }
set
{
if (_noOfVerticalPoints != value)
{
_noOfVerticalPoints = value;
this.OnPropertyChanged();
}
}
}
#endregion ----------- Properties --------------
#region ------------- Constructors -------------
public MainViewModel()
{
_geometry = new MeshGeometry3D();
_material = new DiffuseMaterial();
_noOfHorizontalPoints = 500;
_noOfVerticalPoints = 500;
defineMaterial();
defineGeometry();
PropertyChanged = onPropertyChanged;
}
#endregion ---------- Constructors -------------
#region --------------- Methods ----------------
private void onPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(NoOfHorizontalPoints) || e.PropertyName == nameof(NoOfVerticalPoints))
{
defineMaterial();
defineGeometry();
}
}
private void defineMaterial()
{
var colorRandomizer = new Random();
var pic = new System.Drawing.Bitmap(NoOfHorizontalPoints, NoOfVerticalPoints);
for (int row = 0; row < NoOfVerticalPoints; row )
{
for (int col = 0; col < NoOfHorizontalPoints; col )
{
var tmp = colorRandomizer.NextDouble();
System.Drawing.Color color;
if (tmp < 0.2)
color = System.Drawing.Color.Green;
else if (tmp < 0.4)
color = System.Drawing.Color.YellowGreen;
else if (tmp < 0.6)
color = System.Drawing.Color.Yellow;
else if (tmp < 0.8)
color = System.Drawing.Color.Orange;
else
color = System.Drawing.Color.Red;
pic.SetPixel(col, row, color);
}
}
BitmapSource imageSource = Imaging.CreateBitmapSourceFromHBitmap(pic.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
var image = new Image();
image.Source = imageSource;
var visualBrush = new VisualBrush(image);
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.NearestNeighbor); // Used to avoid smooth transition between pixels
((DiffuseMaterial)Material).Brush = visualBrush;
}
private void defineGeometry()
{
double totalHeight = 1.0;
double totalWidth = 1.0;
double startHeightPos = -totalHeight / 2.0;
double startWidthPos = -totalWidth / 2.0;
var geometry = new MeshGeometry3D();
var x1 = startWidthPos;
var y1 = startHeightPos;
var z1 = 0.0;
var x2 = startWidthPos totalWidth;
var y2 = startHeightPos totalHeight;
var z2 = 0.0;
geometry.Positions.Add(new Point3D(x1, y1, z1));
geometry.Positions.Add(new Point3D(x2, y1, z2));
geometry.Positions.Add(new Point3D(x1, y2, z1));
geometry.Positions.Add(new Point3D(x2, y2, z2));
geometry.TriangleIndices.Add(0);
geometry.TriangleIndices.Add(2);
geometry.TriangleIndices.Add(1);
geometry.TriangleIndices.Add(1);
geometry.TriangleIndices.Add(2);
geometry.TriangleIndices.Add(3);
geometry.TextureCoordinates.Add(new Point(NoOfHorizontalPoints, NoOfVerticalPoints));
geometry.TextureCoordinates.Add(new Point(0, NoOfVerticalPoints));
geometry.TextureCoordinates.Add(new Point(NoOfHorizontalPoints, 0));
geometry.TextureCoordinates.Add(new Point(0, 0));
Geometry = geometry;
}
#region INotifyPropertyChanged
/// <summary>
/// Raises the PropertyChange event for the property specified
/// </summary>
/// <param name="propertyName">Property name to update. Is case-sensitive.</param>
public virtual void RaisePropertyChanged(string propertyName)
{
OnPropertyChanged(propertyName);
}
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
OnPropertyChangedExplicit(propertyName);
}
protected void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> projection)
{
var memberExpression = (MemberExpression)projection.Body;
OnPropertyChangedExplicit(memberExpression.Member.Name);
}
void OnPropertyChangedExplicit(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion INotifyPropertyChanged
#endregion ------------ Methods ----------------
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/486024.html
