c# - 更新 GeometryModel3D Material 的单点颜色而不是整个点系统

标签 c# wpf colors

我无法理解 C# WPF 项目的颜色/ Material 系统,目前我正在更新整个模型点系统的颜色,而我只想更新颜色单个点(添加时)。

聚合系统类

public class AggregateSystem {
    // stack to store each particle in aggregate
    private readonly Stack<AggregateParticle> particle_stack;
    private readonly GeometryModel3D particle_model;
    // positions, indices and texture co-ordinates for particles
    private readonly Point3DCollection particle_positions;
    private readonly Int32Collection triangle_indices;
    private readonly PointCollection text_coords;
    // brush to apply to particle_model.Material
    private RadialGradientBrush rad_brush;
    // ellipse for rendering
    private Ellipse ellipse;
    private RenderTargetBitmap render_bitmap;

    public AggregateSystem() {
        particle_stack = new Stack<AggregateParticle>();
        particle_model = new GeometryModel3D { Geometry = new MeshGeometry3D() };
        ellipse = new Ellipse {
            Width = 32.0,
            Height = 32.0
        };
        rad_brush = new RadialGradientBrush();
        // fill ellipse interior using rad_brush
        ellipse.Fill = rad_brush;
        ellipse.Measure(new Size(32,32));
        ellipse.Arrange(new Rect(0,0,32,32));
        render_bitmap = new RenderTargetBitmap(32,32,96,96,PixelFormats.Pbgra32));
        ImageBrush img_brush = new ImageBrush(render_bitmap);
        DiffuseMaterial diff_mat = new DiffuseMaterial(img_brush);
        particle_model.Material = diff_mat;
        particle_positions = new Point3DCollection();
        triangle_indices = new Int32Collection();
        tex_coords = new PointCollection();
    }

    public Model3D AggregateModel => particle_model;

    public void Update() {
        // get the most recently added particle
        AggregateParticle p = particle_stack.Peek();
        // compute position index for triangle index generation
        int position_index = particle_stack.Count * 4;
        // create points associated with particle for circle generation
        Point3D p1 = new Point3D(p.position.X, p.position.Y, p.position.Z);
        Point3D p2 = new Point3D(p.position.X, p.position.Y + p.size, p.position.Z);
        Point3D p3 = new Point3D(p.position.X + p.size, p.position.Y + p.size, p.position.Z);
        Point3D p4 = new Point3D(p.position.X + p.size, p.position.Y, p.position.Z);
        // add points to particle positions collection
        particle_positions.Add(p1);
        particle_positions.Add(p2);
        particle_positions.Add(p3);
        particle_positions.Add(p4);
        // create points for texture co-ords
        Point t1 = new Point(0.0, 0.0);
        Point t2 = new Point(0.0, 1.0);
        Point t3 = new Point(1.0, 1.0);
        Point t4 = new Point(1.0, 0.0);
        // add texture co-ords points to texcoords collection
        tex_coords.Add(t1);
        tex_coords.Add(t2);
        tex_coords.Add(t3);
        tex_coords.Add(t4);
        // add position indices to indices collection
        triangle_indices.Add(position_index);
        triangle_indices.Add(position_index + 2);
        triangle_indices.Add(position_index + 1);
        triangle_indices.Add(position_index);
        triangle_indices.Add(position_index + 3);
        triangle_indices.Add(position_index + 2);
        // update colour of points - **NOTE: UPDATES ENTIRE POINT SYSTEM** 
        // -> want to just apply colour to single particles added
        rad_brush.GradientStops.Add(new GradientStop(p.colour, 0.0));
        render_bitmap.Render(ellipse);
        // set particle_model Geometry model properties
        ((MeshGeometry3D)particle_model.Geometry).Positions = particle_positions;
        ((MeshGeometry3D)particle_model.Geometry).TriangleIndices = triangle_indices;
        ((MeshGeometry3D)particle_model.Geometry).TextureCoordinates = tex_coords;
    }

    public void SpawnParticle(Point3D _pos, Color _col, double _size) {
        AggregateParticle agg_particle = new AggregateParticle {
            position = _pos, colour = _col, size = _size;
        }
        // push most-recently-added particle to stack
        particle_stack.Push(agg_particle);
    }

}

其中 AggregateParticle 是一个 POD 类,由 Point3D positionColor colordouble size 字段组成,它们是不言自明。

Update 方法中添加时,是否有任何简单有效的方法来更新单个粒子的颜色,而不是更新整个粒子系统?或者我是否需要为系统中的每个粒子创建一个 List(或类似的数据结构)DiffuseMaterial 实例,并为每个粒子应用必要的颜色?

[后者是我想不惜一切代价避免的事情,部分原因是它需要对我的代码进行大量结构更改,而且我确信有比那更好的方法来解决这个问题 - 即 必须有一些简单的方法可以将颜色应用于一组纹理坐标,对吧?!。]

更多详情

  • AggregateModel 是一个单独的 Model3D 实例,对应于添加到 Model3DGroup 的字段 particle_model > 的 MainWindow

  • 我应该指出我正在努力实现的目标,具体来说,这里是聚合结构中每个粒子的颜色“梯度”,其中粒子在“temperature-gradient”(在程序的其他地方计算)取决于它的生成顺序 - 即,如果较早生成,粒子具有较冷的颜色,如果较晚生成,则具有较暖的颜色。如上所示,此颜色列表已预先计算并传递给 Update 方法中的每个粒子。

  • 我尝试的一个解决方案涉及为每个粒子创建一个单独的 AggregateComponent 实例,其中每个对象都有一个关联的 Model3D,因此有一个相应的画笔。然后创建了一个 AggregateComponentManager 类,其中包含每个 AggregateComponentList。这个解决方案有效,但是它非常慢,因为每次添加粒子时都必须更新每个组件,因此内存使用量激增 - 有没有办法适应这个我可以缓存已经渲染的 AggregateComponent 没有每次添加粒子时都必须调用它们的 Update 方法吗?

完整的源代码(DLAProject 目录中的 C# 代码)可以在 GitHub 上找到:https://github.com/SJR276/DLAProject

最佳答案

我们为较小的点云(+/- 100 k 点)创建 WPF 3D 模型,其中每个点作为八面体(8 个三角形)添加到 MeshGeometry3D。

为了允许在这样的点云中的不同点(我们用它来选择一个点或点的子集)使用不同的颜色,我们从一个小位图中分配纹理坐标。

在高层次上,我们有一些这样的代码:

BitmapSource bm = GetColorsBitmap(new List<Color> { BaseColor, SelectedColor });
ImageBrush ib = new ImageBrush(bm) 
{ 
    ViewportUnits = BrushMappingMode.Absolute, 
    Viewport = new Rect(0, 0, 1, 1) // Matches the pixels in the bitmap.
};  
GeometryModel3D model = new GeometryModel3D { Material = new DiffuseMaterial(ib) };

现在纹理坐标只是

new Point(0, 0);
new Point(1, 0);

...等等

Bitmap 的颜色来自:

// Creates a bitmap that has a single row containing single pixels with the given colors.
// At most 256 colors.
public static BitmapSource GetColorsBitmap(IList<Color> colors)
{
    if (colors == null) throw new ArgumentNullException("colors");
    if (colors.Count > 256) throw new ArgumentOutOfRangeException("colors", "More than 256 colors");

    int size = colors.Count;
    for (int j = colors.Count; j < 256; j++)
    {
        colors.Add(Colors.White);
    }

    var palette = new BitmapPalette(colors);
    byte[] pixels = new byte[size];
    for (int i = 0; i < size; i++)
    {
        pixels[i] = (byte)i;
    }

    var bm = BitmapSource.Create(size, 1, 96, 96, PixelFormats.Indexed8, palette, pixels, 1 * size);
    bm.Freeze();
    return bm;
}

我们还努力在更新点云时缓存和重用内部几何结构。

最后我们用很棒的 Helix Toolkit 来显示它.

关于c# - 更新 GeometryModel3D Material 的单点颜色而不是整个点系统,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37667681/

相关文章:

c# - Razor 的文本显示太长

android - 如何在 setBackgroundColor 函数中设置参数

android - Android 5.1 上的 setTextColor

C# 圆形颜色到列表中的颜色

c# - DataTable 中的# 是什么选择查询

c# - 删除连接表,如何插入一行而不在数据库中查询该行引用的实体?

c# - 将字符串匹配到枚举?

c# - 使用 InvokeCommandAction 传递 Source 和 Property,而不使用 MultiBindings

c# - 自承载 WCF 服务 : How to access the object(s) implementing the service contract from the hosting application?

c# - 如何删除所有 Click 事件处理程序?