WPF绘制深度不同颜色的3D模型填充图和线框图

简介: 原文:WPF绘制深度不同颜色的3D模型填充图和线框图 在机械测量过程中,测量的数据需要进行软件处理。通常测量一个零件之后,需要重建零件的3D模型,便于观察测量结果是否与所测工件一致。
原文: WPF绘制深度不同颜色的3D模型填充图和线框图


在机械测量过程中,测量的数据需要进行软件处理。通常测量一个零件之后,需要重建零件的3D模型,便于观察测量结果是否与所测工件一致。
重建的3D模型需要以填充图和线框图两种方式切换显示,其中填充图的材质需要根据不同深度进行着色,线框图需要消隐(不能透视)。以圆柱为例,如下图:

由于WPF对DirectX进行了封装,并构建出一套简单的3D绘图框架,因此我们可以快速的创建所需要的3D模型,便于像我这样的对三维计算机图形学不太了解的人进行开发。
关于WPF 3D绘图的基础,大家可以参考《Practical WPF Charts and Graphics》一书,里面也讲到一些3D显示的计算机图形学和数学基础。
下面我们开始3D模型的构建。
在WPF中,我们可以按如下步骤进行操作:
1、创建一个Viewport3D对象来host 三维模型;
2、继承ModelVisual3D或者ModelUIElement3D来定义3D对象;
3、指定3D场景中的光源,在WPF中光源也是一种ModelVisual3D;
4、创建一个相机来进行3D图形到2D显示器的映射。

根据工程中的经验,我们考虑传感器采集的数据是圆柱体工件的五个截面(实际可能更多或者更少),每个截面包含8192(或者更多)个点,并且是极坐标格式数据。
这里我们使用ModelVisual3D来定义三维物体。其中重要的是三角剖分和材质贴图的部分。由于每一层数据点个数都是一致的,因此我们可以将圆柱体分成三个部分,上下底面和侧面。参考《Practical WPF Charts and Graphics》,我们可以把圆柱的侧面分成多个四边形,每个四边形分成两个三角形,并将上下底面分成多个三角形(类似扇形)。
剖分方式大致如下图:

由于数据点坐标是极坐标,每一层有不同的高度(即三维坐标系中的Y),因此需要将极坐标转化为三维坐标系中的坐标,即Point3D结构,坐标系如下图:

需要注意的是,我们绘制的3D模型是三个曲面构成,每个曲面没有封闭,这样看上去会在首尾相接处出现缺口,因此需要多加一些三角形将首尾进行缝合。未缝合的模型如下:

在三维模型创建结束之后,需要贴上材质,由于工程中需要体现出不同深度(即不同半径 )的差别,需要使用渐变色进行呈现,类似Matlab中的效果

参考我转载的一篇文章(http://blog.csdn.net/congduan/article/details/21092341),就可以得出基本思路:在计算模型的时候,根据不同的R值,生成一张RGB color的映射表,并将其作为材质应用在3D模型上。
参考Codeproject上http://www.codeproject.com/KB/WPF/WPFChart3D.aspx这篇文章的源码,其中的TextureMapping.cs文件便是进行材质颜色映射的类,可以根据需要将多种颜色改写成2种颜色等等,做出适合自己的定制。当然,这个类使用了C#指针(unsafe关键字),需要在项目属性生成一项中开启不安全代码支持。以下是我修改例子,做出的两种颜色的渐变图:

以下便是参考该文例子写出的本文圆柱的代码:

for (int i = 0; i < drawPointCount; i++)
{ 
for (int j = 0; j < PointCollectionList.Count; j++)
{
//从极坐标构造笛卡尔坐标系数据 
points[i, j] = MathHelper.GetPosition(baseRadius +
ZoomContourValue * (PointCollectionList[j][i * interval].Y - fittedCircles[j].Radius) / (maxR - minR),
PointCollectionList[j][i * interval].X,
(j - halfPointCollectionCount) * h,
(Vector3D)Center); 

//根据表面半径差值,生成RGB颜色纹理坐标
double u = (PointCollectionList[j][i * interval].Y - minR) / (maxR - minR);
Color color = TextureMapping.PseudoColor(u);
Point mapPt = m_mapping.GetMappingPosition(color);
texcoords[i, j] = new Point(mapPt.X, mapPt.Y);
}
}


注意其中半径都是归一化之后才生成颜色值的。
最后将TextureMapping生成的材质贴到模型上。
//填充表面材质
surfaceModel = new GeometryModel3D(surfaceMeshBuilder.ToMesh(), m_mapping.m_material);

======================================================================
对于线框图(WireFrame),WPF支持的不好,因为WPF中没有3D线段的类,好在微软的人意识到了这一点,开发出开源的3D Tools for WPF(http://3dtools.codeplex.com/)作为补充,其中就有ScreenSpaceLines3D类用于3D线段绘制。这样前面的填充图也可以加上轮廓了。
但是,问题又来了。绘制出的线框图是下面这样的:

由于没有消隐,很多线段交叉重叠在一起,影响判断。《Practical WPF Charts and Graphics》一文中的例子是这样处理的,在前面填充图的基础上加上线框,但是填充的材质改成纯白色,并将线框线条加粗。效果还是不错的,如下:

但是我这样试过之后,发现一些问题,偶尔线条会被填充白色的三角形遮挡住,为了解决这个问题,我找到OpenGL的C#开源封装库,比如OpenTK和SharpGL,其中SharpGL中做如下处理之后会得到较好的效果:
//突出边框,设置多边形偏移
gl.Enable(OpenGL.GL_POLYGON_OFFSET_FILL);
gl.PolygonOffset(1.0f, 1.0f);
//开启反走样
gl.Enable(OpenGL.GL_BLEND);
gl.Enable(OpenGL.GL_LINE_SMOOTH);
gl.Enable(OpenGL.GL_POLYGON_SMOOTH);
gl.Hint(OpenGL.GL_POINT_SMOOTH_HINT, OpenGL.GL_NICEST); // Make round points, not square points
gl.Hint(OpenGL.GL_LINE_SMOOTH_HINT, OpenGL.GL_NICEST); // Antialias the lines
gl.BlendFunc(OpenGL.GL_SRC_ALPHA, OpenGL.GL_ONE_MINUS_SRC_ALPHA);


效果:

可以SharpGL未能很好地利用GPU,将数据全部拷贝到内存中,导致内存暴涨,甚至有时候内存泄露。因此最后放弃了这个库。而OpenTK并没有WPF的控件,只能使用WindowsFormsHost来host WinForm的控件,不是很方便。
后来使用Helix 3D Toolkit,它将WPF和SharpDX进行了再次封装,并提供了一些比较好的3D算法。并使用其中的MeshBuilder更方便地创建三角形和3D线段,最终出现本文开始的效果。

最后,重点提一下线框图中的线段分布的算法。
显示线框图的时候,会绘制一部分竖线,开始考虑的是均匀间隔绘制,但是竖线绘制多少,是一个需要仔细思考的问题。线段多了,损耗性能,线段少了,轮廓的还原度不会搞,尤其是一些凹槽和凸出的部分会因为过于稀疏的线段无法显示出来,如下图的部分:

思考了很久,想到一种比较满意的算法:计算每一层所有数据点的陡变程度,提取前100个(可以根据不同疏密需要的情况进行选取)陡变最大的点绘制竖线。
由于数据点是离散的,可以使用离散曲率刻画点的陡变程度,离散曲率的计算公式如下:


参考论文《一种度量图像像素陡变程度的方法》可以知道,这种连续曲线曲率直接离散化的结果并不是很好,并且该论文提出了一种更好的算法,具体算法大家可以去阅读论文原文。公式如下:

有了这样的评判标准,实现之后变成功将需要的轮廓比较完整地显示了出来。由于数据点很多,计算公式也比较复杂,最好是放入后台线程异步处理,避免UI线程阻塞。
最后,注意大量数据点会造成三角形过多(这里会达到20W个左右),导致构建性能问题突出,最好在保证3D模型还原度高的情况下,进行稀疏处理,减少三角形数量。

目录
相关文章
|
8月前
WPF-样式问题-处理ListBox、ListView子项内容全填充问题
WPF-样式问题-处理ListBox、ListView子项内容全填充问题
114 0
|
8月前
WPF-样式问题-ListBox或ListView中子项全填充去除边线问题
WPF-样式问题-ListBox或ListView中子项全填充去除边线问题
65 0
|
前端开发 C#
WPF使用Canvas绘制可变矩形
原文:WPF使用Canvas绘制可变矩形 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/WANGYAN9110/article/details/38130661 1、问题以及解决办法 最近因为项目需要,需要实现一个位置校对的功能,大致的需求如下:有一个图片,有一些位置信息,但是位置信息可能和实际有些偏差,需要做简单调整,后面会对这张图片进行切割等,做些处理。
1639 0
|
C#
WPF中让TextBlock每一个字符显示不同的颜色
原文:WPF中让TextBlock每一个字符显示不同的颜色 XAML代码: R G B ...
1634 0
|
C#
WPF圆角按钮与触发颜色变化
原文:WPF圆角按钮与触发颜色变化 ...
1540 0
|
C# Windows 图形学
优化WPF 3D性能
原文:优化WPF 3D性能 Maximize WPF 3D Performance .NET Framework 4.5   As you use the Windows Presentation Foundation (WPF) to build 3D contr...
1316 0
|
C#
WPF 颜色转换
原文:WPF 颜色转换 从字符串到画刷: var converter = new System.Windows.Media.BrushConverter(); var brush = (Brush)converter.
894 0
|
C# 小程序
WPF 3D变换应用
原文:WPF 3D变换应用  WPF可以提供的3D模型使我们可以轻松地创建3D实体,虽然目前来看还很有一些性能上的问题,不过对于一些简单的3D应用应该是可取的,毕竟其开发效率高,而且也容易上手。         下面给大家演示的是使用在WPF 3D上实现视角变换,通过鼠标拖动来变换观察视角,通过滚轮来放缩视距。
702 0
|
C#
WPF特效-实现3D足球效果
原文:WPF特效-实现3D足球效果 WPF 实现 3D足球效果,效果图如下:  每个面加载不同贴图。                                                          ...
859 0
|
C# C++ 计算机视觉
WPF特效-绘制实时2D激光雷达图
原文:WPF特效-绘制实时2D激光雷达图 接前两篇: https://blog.csdn.net/u013224722/article/details/80738619 https://blog.csdn.net/u013224722/article/details/80738995 除了c# GDI 、Opencv(c++)、 c# Emgu绘图外,其实c#  WPF绘图功能也很强大。
1816 0