.NET WPF 三维模型
文章目录
- .NET WPF 三维模型
- 1 Viewport3D
- 1.1 3D 坐标系
- 1.2 核心组件
- 1.2.1 相机 (Camera)
- 1.2.2 光源 (Light)
- 1.2.3 3D 模型(Model3D)
- 1.3 模型纹理(Material)
- 1.4 完整示例:创建坐标轴与立方体
- 1.5 转换模型
- 1.6 性能
- 1.6.1 性能影响:高
- 1.6.2 性能影响:中
- 1.6.3 性能影响:低
- 1.7 常见问题
- 1.8 扩展工具
.NET WPF 三维模型
1 Viewport3D
- 作用:用于在 WPF 2D 界面中渲染3D图形,结合了3D模型、光源和相机视角。
- 命名空间:
System.Windows.Controls.Viewport3D
- 依赖项:需添加
PresentationCore
和WindowsBase
程序集引用。
1.1 3D 坐标系
2D 图形的 WPF 坐标系将原点定位在呈现区域(通常是屏幕)的左上角。 在 2D 系统中,x 轴上的正值朝右,y 轴上的正值朝下。 但是,在 3D 坐标系中,原点位于呈现区域的中心,x 轴上的正值朝右,但是 y 轴上的正值朝上,z 轴上的正值从原点向外朝向观察者。
1.2 核心组件
1.2.1 相机 (Camera)
创建 3D 场景时,实际上是要创建 3D 对象的 2D 表示形式。 由于 3D 场景的外观会因观察者的观察位置而异,因此必须指定观察位置。 可以使用 Camera 类来为 3D 场景指定观察位置。
了解 3D 场景如何在 2D 图面上表示的另一种方法就是将场景描述为到观察图面上的投影。 使用 ProjectionCamera,可以指定不同的投影及其属性,以更改观察者查看 3D 模型的方式。 PerspectiveCamera 指定用来对场景进行透视收缩的投影。 换言之,PerspectiveCamera 提供消失点透视。 可以指定照相机在场景坐标系中的位置、照相机的方向和视野以及用来定义场景中“向上”方向的矢量。 下图阐释了 PerspectiveCamera 的投影。
ProjectionCamera 的 NearPlaneDistance 和 FarPlaneDistance 属性限制照相机的投影范围。 由于照相机可以位于场景中的任何位置,因此照相机实际上可能会位于模型内部或者紧靠模型,这使正确区分对象变得很困难。 通过 NearPlaneDistance,可以指定一个距离照相机的最小距离,超过该距离后即不绘制对象。 相反,使用 FarPlaneDistance,可以指定一个距离照相机的距离(超过该距离后即不绘制对象),从而确保因距离太远而无法识别的对象不包括在场景中。
OrthographicCamera 指定 3D 模型到 2D 可视图面上的正投影。 与其他照相机一样,它指定位置、观察方向和“向上”方向。 但是,与 PerspectiveCamera 不同的是,OrthographicCamera 描述了不包括透视收缩的投影。 换言之,OrthographicCamera 描述了一个侧面平行的取景框,而不是侧面汇集在照相机中一点的取景框。 下图演示使用 PerspectiveCamera 和 OrthographicCamera 查看同一模型时的情况。
Model3D 是表示泛型 3D 对象的抽象基类。 若要生成 3D 场景,需要一些要查看的对象,而且构成场景图的对象必须派生自 Model3D。 目前,WPF 支持用 GeometryModel3D 对几何图形进行建模。 此模型的 Geometry 属性采用网格基元。
若要生成模型,请首先生成一个基元或网格。 3D 基元是单个 3D 实体中的顶点的集合。 大多数 3D 系统都提供在最简单的闭合图(由三个顶点定义的三角形)上建模的基元。 由于三角形的三个点在一个平面上,因此可以继续添加三角形,以便对网格这样较为复杂的形状建模。
WPF 3D 系统目前提供 MeshGeometry3D 类,使用该类可以指定任何几何图形;它目前不支持预定义的 3D 基元(如球体和立方体)。 首先通过将三角形顶点的列表指定为 Positions 属性来创建 MeshGeometry3D。 每个顶点都指定为 Point3D。 (在 XAML 中,将该属性指定为三个一组的数字列表,每组中的三个数字表示每个顶点的坐标。)根据网格的几何形状,网格可能会由多个三角形组成,有些三角形共用相同的角(顶点)。 要正确绘制网格,WPF 需要有关哪些顶点由哪些三角形共用的信息。 可以通过指定具有 TriangleIndices 属性的三角形索引列表来提供此信息。 此列表指定在 Positions 列表中指定的点将按哪种顺序确定三角形。
控制观察3D场景的视角,常用类型:
<Viewport3D.Camera><!-- 透视相机(模拟人眼视角) --><PerspectiveCamera Position="0,0,5" LookDirection="0,0,-1" UpDirection="0,1,0" FieldOfView="60"/><!-- 或正交相机(无透视变形) --><OrthographicCamera Position="0,0,5" Width="10"/>
</Viewport3D.Camera>
1.2.2 光源 (Light)
与实际的光一样,3D 图形中的光能够使图面可见。 更确切地说,光确定了场景的哪个部分将包括在投影中。 WPF 中的光对象创建了各种光和阴影效果,并且按照各种实际光的行为进行了建模。 至少在场景中包括一个光源,否则模型将不可见。
下面的光源派生自基类 Light:
-
AmbientLight:它所提供的环境光以一致的方式照亮所有对象,这与对象的位置或方向无关。
-
DirectionalLight:像远处的光源那样照亮。 将定向光的 Direction 指定为 Vector3D,但是没有为定向光指定位置。
-
PointLight:像近处的光源那样照亮。 PointLights 具有一个位置并从该位置投射光。 场景中的对象根据对象相对于光源的位置和距离被照亮。 PointLightBase 公开了 Range 属性,该属性确定一个距离,超过该距离后模型将无法由光源照亮。 PointLight 还公开了多个衰减属性,这些属性确定光源的亮度如何随距离的增加而减小。 可以为光源的衰减指定恒定、线性或二次内插算法。
-
SpotLight:继承自 PointLight。 Spotlights 的照亮方式与 PointLight 类似,但是它既具有位置又具有方向。 它们在 InnerConeAngle 和 OuterConeAngle 属性所设置的锥形区域(以度为单位指定)中投射光。
光源是 Model3D 对象,因此可以转换光源属性并对光源属性(包括位置、颜色、方向和范围)进行动画处理。
<ModelVisual3D><ModelVisual3D.Content><DirectionalLight Color="White" Direction="-1,-1,-1" /></ModelVisual3D.Content>
</ModelVisual3D>
1.2.3 3D 模型(Model3D)
Model3D 是表示泛型 3D 对象的抽象基类。 若要生成 3D 场景,需要一些要查看的对象,而且构成场景图的对象必须派生自 Model3D。 目前,WPF 支持用 GeometryModel3D 对几何图形进行建模。 此模型的 Geometry 属性采用网格基元。
若要生成模型,请首先生成一个基元或网格。 3D 基元是单个 3D 实体中的顶点的集合。 大多数 3D 系统都提供在最简单的闭合图(由三个顶点定义的三角形)上建模的基元。 由于三角形的三个点在一个平面上,因此可以继续添加三角形,以便对网格这样较为复杂的形状建模。
WPF 3D 系统目前提供 MeshGeometry3D 类,使用该类可以指定任何几何图形;它目前不支持预定义的 3D 基元(如球体和立方体)。 首先通过将三角形顶点的列表指定为 Positions 属性来创建 MeshGeometry3D。 每个顶点都指定为 Point3D。 (在 XAML 中,将该属性指定为三个一组的数字列表,每组中的三个数字表示每个顶点的坐标。)根据网格的几何形状,网格可能会由多个三角形组成,有些三角形共用相同的角(顶点)。 要正确绘制网格,WPF 需要有关哪些顶点由哪些三角形共用的信息。 可以通过指定具有 TriangleIndices 属性的三角形索引列表来提供此信息。 此列表指定在 Positions 列表中指定的点将按哪种顺序确定三角形。
由 GeometryModel3D
定义,包含几何形状和材质:
<GeometryModel3D><GeometryModel3D.Geometry><!-- 定义立方体网格 --><MeshGeometry3D Positions="-1,-1,0 1,-1,0 -1,1,0 1,1,0"Normals="0,0,1 0,0,1 0,0,1 0,0,1"TextureCoordinates="0,1 1,1 0,0 1,0 "TriangleIndices="0,1,2 1,3,2" /></GeometryModel3D.Geometry><GeometryModel3D.Material><DiffuseMaterial><DiffuseMaterial.Brush><SolidColorBrush Color="Cyan" Opacity="0.3"/></DiffuseMaterial.Brush></DiffuseMaterial></GeometryModel3D.Material>
</GeometryModel3D>
在上面的示例中,Positions列表指定四个顶点以定义矩形网格。 TriangleIndices属性指定了包含两个组的列表,每组由三个索引组成。 列表中的每个数字指的是 Positions 列表中的偏移量。 例如,由Positions列表指定的第一组三个顶点为 (-1,-1,0)、(1,-1,0) 和 (-1,1,0)。 由TriangleIndices列表指定的第一组三个索引为 0、1 和 2,这与Positions列表中的第一个、第二个和第三个点相对应。 因此,构成立方体模型的第一个矩形将按照从 (-1,-1,0) 到 (1,-1,0) 再到 (-1,1,0) 的顺序组合而成,第二个三角形将按照类似方式确定。
可以通过为 Normals 和 TextureCoordinates 属性指定值来继续定义模型。 为了呈现模型的图面,图形系统需要有关曲面在任何给定三角形上的朝向信息。 图形系统使用此信息来针对该模型进行照明计算:正对光源的图面比偏离光源的图面显得更亮。 尽管 WPF 可以使用位置坐标来确定默认的法矢量,但是你还可以指定不同的法矢量来近似计算曲面的外观。
TextureCoordinates 属性指定 Point 的集合,用于通知图形系统如何将确定纹理绘制方式的坐标映射到网格的顶点。 TextureCoordinates 指定为 0 和 1 之间(包含这两个值)的值。 与 Normals 属性一样,图形系统可以计算默认纹理坐标,但你可以选择设置不同的纹理坐标来控制包括部分重复图案的纹理的映射。
1.3 模型纹理(Material)
为了使网格看上去像三维对象,必须向其应用纹理,以便覆盖由顶点和三角形定义的图面,从而使其可以由照相机照明和投影。 在 2D 中,可以使用 Brush 类向屏幕中的区域应用颜色、图案、渐变或其他视觉内容。 但是,3D 对象的外观取决于照明模型,而不仅仅是应用于它们的颜色或图案。 实际对象的图面质量不同,它们反射光的方式也会有所不同:光亮的图面与粗糙或不光滑的图面看上去不同,某些对象似乎可以吸收光,而某些对象似乎能够发光。 可以向 3D 对象应用与 2D 对象完全相同的画笔,但是不能直接应用它们。
WPF 使用 Material 抽象类来定义模型图面的特征。 Material 的具体子类用来确定模型图面的某些外观特征,每个子类还提供一个可以向其传递 SolidColorBrush、TileBrush 或 VisualBrush 的 Brush 属性。
-
DiffuseMaterial 指定将向模型应用画笔,就好像模型是使用漫射光来照亮的一样。 使用 DiffuseMaterial 与直接针对 2D 模型使用画笔非常相似;模型图面不反射光,就好像是自发光一样。
-
SpecularMaterial 指定将向模型应用画笔,就好像模型的表面坚硬或者光亮,能够反射光一样。 可以通过为 SpecularPower 属性指定一个值,来设置系统将为纹理的反射特质(或“发光”)建议的度数。
-
使用 EmissiveMaterial 可以指定将应用纹理,就好像模型所发出的光与画笔的颜色相同。 这不会使模型成为光源;但是它参与阴影设置的方式将不同于用 DiffuseMaterial 或 SpecularMaterial 设置纹理时的情况。
为进一步提高性能,可以从场景中精选 GeometryModel3D 的背面,由于它们相对于照相机位于模型的背面,你将看不到这些面。 若要指定要应用于模型(如飞机)背面的 Material,请设置模型的 BackMaterial 属性。
为了实现某些图面质量(如发光或发射效果),用户可能希望向模型连续应用几个不同的画笔。 可以使用 MaterialGroup 类来应用和重复使用多个 Material。 MaterialGroup 的子级在多个呈现过程中按照从头到尾的顺序来应用。
1.4 完整示例:创建坐标轴与立方体
<Viewport3D><!-- 相机设置 --><Viewport3D.Camera><PerspectiveCameraFarPlaneDistance="100"FieldOfView="45"LookDirection="-1,-1,-1"NearPlaneDistance="0.5"Position="3,3,3"UpDirection="0,1,0" /></Viewport3D.Camera><!-- 光源 --><ModelVisual3D><ModelVisual3D.Content><DirectionalLight Direction="-1,-1,-1" Color="White" /></ModelVisual3D.Content></ModelVisual3D><!-- 立方体模型 --><ModelVisual3D><ModelVisual3D.Content><GeometryModel3D><GeometryModel3D.Geometry><MeshGeometry3D Positions="0,0,0 1,0,0 1,1,0 0,1,0 0,0,-1 1,0,-1 1,1,-1 0,1,-1 " TriangleIndices=" 0,1,2 0,2,3 4,7,6 4,6,5 0,3,7 7,4,0 1,5,6 1,6,2 3,2,6 3,6,7 0,4,5 0,5,1 " /></GeometryModel3D.Geometry><GeometryModel3D.Material><DiffuseMaterial><DiffuseMaterial.Brush><SolidColorBrush Color="Yellow" /></DiffuseMaterial.Brush></DiffuseMaterial></GeometryModel3D.Material></GeometryModel3D></ModelVisual3D.Content></ModelVisual3D><!-- X轴(红色) --><ModelVisual3D><ModelVisual3D.Content><GeometryModel3D><GeometryModel3D.Geometry><MeshGeometry3D Positions="-1,-0.01,0.01 2,-0.01,0.01 2,0.01,0.01 -1,0.01,0.01 -1,-0.01,-0.01 2,-0.01,-0.01 2,0.01,-0.01 -1,0.01,-0.01 2,0.05,0.05 2,-0.05,0.05 2,-0.05,-0.05 2,0.05,-0.05 2.2,0,0" TriangleIndices=" 0,1,2 0,2,3 4,7,6 4,6,5 0,3,7 0,7,4 1,5,6 1,6,2 3,2,6 3,6,7 0,4,5 0,5,1 8,10,9 8,11,10 12,8,9 12,9,10 12,10,11 12,11,8" /></GeometryModel3D.Geometry><GeometryModel3D.Material><DiffuseMaterial Brush="Red" /></GeometryModel3D.Material></GeometryModel3D></ModelVisual3D.Content></ModelVisual3D><!-- Y轴(绿色) --><ModelVisual3D><ModelVisual3D.Content><GeometryModel3D><GeometryModel3D.Geometry><MeshGeometry3D Positions="-0.01,-1,0.01 0.01,-1,0.01 0.01,2,0.01 -0.01,2,0.01 -0.01,-1,-0.01 0.01,-1,-0.01 0.01,2,-0.01 -0.01,2,-0.01 0.05,2,0.05 0.05,2,-0.05 -0.05,2,-0.05 -0.05,2,0.05 0,2.2,0" TriangleIndices="0,1,2 0,2,3 4,7,6 4,6,5 0,3,7 0,7,4 1,5,6 1,6,2 3,2,6 3,6,7 0,4,5 0,5,1 8,10,9 8,11,10 12,8,9 12,9,10 12,10,11 12,11,8" /></GeometryModel3D.Geometry><GeometryModel3D.Material><DiffuseMaterial Brush="Green" /></GeometryModel3D.Material></GeometryModel3D></ModelVisual3D.Content></ModelVisual3D><!-- Y轴(蓝色) --><ModelVisual3D><ModelVisual3D.Content><GeometryModel3D><GeometryModel3D.Geometry><MeshGeometry3D Positions="-0.01,-0.01,1 0.01,-0.01,1 0.01,0.01,1 -0.01,0.01,1 -0.01,-0.01,-4 0.01,-0.01,-4 0.01,0.01,-4 -0.01,0.01,-4 0.05,0.05,1 -0.05,0.05,1 -0.05,-0.05,1 0.05,-0.05,1 0,0,1.2" TriangleIndices="0,1,2 0,2,3 4,7,6 4,6,5 0,3,7 0,7,4 1,5,6 1,6,2 3,2,6 3,6,7 0,4,5 0,5,1 8,10,9 8,11,10 12,8,9 12,9,10 12,10,11 12,11,8" /></GeometryModel3D.Geometry><GeometryModel3D.Material><DiffuseMaterial Brush="Blue" /></GeometryModel3D.Material></GeometryModel3D></ModelVisual3D.Content></ModelVisual3D>
</Viewport3D>
1.5 转换模型
创建模型时,它们在场景中具有特定位置。 若要在场景中移动这些模型、旋转模型或更改其大小,则更改定义模型本身的顶点并不实用。每个模型对象都有一个 Transform 属性,可以使用该属性移动、重新定位或调整模型的大小。 应用转换时,实际上是按照由转换功能指定的矢量或值(以适用者为准)来偏移模型的所有点。 换句话说,你已转换模型在其中定义的坐标空间(“模型空间”),但你尚未更改构成整个场景坐标系(“世界空间”)中模型几何图形的值。
3D 转换继承自抽象基类 Transform3D;这些包括仿射变换类 TranslateTransform3D、ScaleTransform3D和 RotateTransform3D。 Windows Presentation Foundation (WPF) 3D 系统还提供 MatrixTransform3D 类,使你可以在更简洁的矩阵操作中指定相同的转换。
TranslateTransform3D 按照您通过 OffsetX、OffsetY和 OffsetZ 属性指定的偏移向量方向移动 Model3D 中的所有点。 例如,给定立方体(2,2,2)的一个顶点时,偏移量为(0,1.6,1)会将该顶点(2,2,2,2)移动到(2,3.6,3)。 立方体的顶点在模型空间中仍然是(2,2,2),但现在模型空间与世界空间的关系已改变,因此在世界空间中,模型空间中的(2,2,2)对应为(2,3.6,3)。
1.6 性能
1.6.1 性能影响:高
属性 | 建议 |
---|---|
Brush | 画笔速度(最快至慢): SolidColorBrush > LinearGradientBrush > ImageBrush > DrawingBrush (缓存) > VisualBrush (缓存) > RadialGradientBrush > DrawingBrush (未缓存) > VisualBrush (未缓存) |
ClipToBoundsProperty | 如果不需要使 Windows Presentation Foundation (WPF) 显式地将 Viewport3D 的内容剪裁到 Viewport3D 的矩形,请将 Viewport3D.ClipToBounds 设置为 false。 Windows Presentation Foundation (WPF) 的抗锯齿剪裁可能非常慢,并且在 Viewport3D 上默认启用ClipToBounds (慢速)。 |
IsHitTestVisible | 如果在执行鼠标点击测试时不需要 Windows Presentation Foundation (WPF) 考虑 Viewport3D 的内容,请将 Viewport3D.IsHitTestVisible 设置为 false。 点击测试 3D 内容是在软件中完成的,并且对于大型网格可能很慢。 默认情况下,IsHitTestVisible 在 Viewport3D上被启用(慢)。 |
GeometryModel3D | 仅当它们需要不同的材料或转换时,才创建不同的模型。 否则,请尝试将许多具有相同材料和转换的 GeometryModel3D 实例合并为几个更大的 GeometryModel3D 和 MeshGeometry3D 实例。 |
MeshGeometry3D | 在 Windows Presentation Foundation(WPF)中,网格动画(每帧更改网格的各个顶点)并不总是高效的。 要在修改每个顶点时尽量减少更改通知的性能影响,请在执行每顶点修改之前将网格从可视树中分离出来。 修改网格后,将其重新附加到可视化树。 此外,尽量减小以这种方式进行动画处理的网格大小。 |
3D 抗锯齿 | 为了提高渲染速度,可通过将附加属性 EdgeMode 设置为 Aliased 来禁用 Viewport3D 上的多重采样。 默认情况下,3D 抗锯齿功能在 Windows 上启用,每个像素有 4 个样本。 |
文本 | 在 3D 场景中的实时文本(实时是因为它在 DrawingBrush 或 VisualBrush 中)可能会很慢。 尝试使用文本的图像(通过 RenderTargetBitmap),除非文本将更改。 |
TileBrush | 如果必须在 3D 场景中使用 VisualBrush 或 DrawingBrush,因为画笔的内容不是静态的,请尝试缓存画笔(将附加属性 CachingHint 设置为 Cache )。 设置最小和最大缩放无效阈值(使用附加属性 CacheInvalidationThresholdMinimum 和 CacheInvalidationThresholdMaximum),以便缓存的画笔不会太频繁地重新生成,同时仍然保持所需的质量级别。 默认情况下,不会缓存 DrawingBrush 和 VisualBrush,这意味着每次使用画笔绘制的内容都必须重新呈现时,必须首先将画笔的全部内容重新呈现到中间图面。 |
BitmapEffect | BitmapEffect 强制呈现所有受影响的内容,而无需硬件加速。 为了获得最佳性能,请勿使用 BitmapEffect。 |
1.6.2 性能影响:中
属性 | 建议 |
---|---|
MeshGeometry3D | 如果将网格定义为共享顶点且相邻的三角形,并且这些顶点具有相同的位置、法线和纹理坐标,则每个共享顶点仅需定义一次,然后使用TriangleIndices索引来指定三角形。 |
ImageBrush | 当你对大小有显式控制时(使用 RenderTargetBitmap 和/或 ImageBrush时),请尝试最小化纹理大小。 请注意,分辨率较低的纹理可以降低视觉质量,因此请尝试在质量和性能之间找到适当的平衡。 |
不透明度 | 呈现半透明 3D 内容(如反射)时,在画笔或材料(通过 Opacity 或 Color)上使用不透明度属性,而不是通过将 Viewport3D.Opacity 设置为小于 1 的值来创建单独的半透明 Viewport3D。 |
Viewport3D | 最大程度地减少你在场景中使用的 Viewport3D 对象数。 在同一 Viewport3D 中放置许多 3D 模型,而不是为每个模型创建单独的 Viewport3D 实例。 |
Freezable | 通常,重复使用 MeshGeometry3D、GeometryModel3D、画笔和材料是有益的。 它们都是多父的,因为它们派生自 Freezable 。 |
Freezable | 当“可冻结”的属性在应用程序中保持不变时,对其调用 Freeze 方法。 冻结可以减少工作集,提高速度。 |
Brush | 在画笔内容不会更改时,请使用 ImageBrush 而不是 VisualBrush 或 DrawingBrush。 2D 内容可以通过 RenderTargetBitmap 转换为 Image,然后在 ImageBrush 中使用。 |
BackMaterial | 除非你实际上需要看到 GeometryModel3D 的背面,否则不要使用 BackMaterial。 |
Light | 光速(最快至最慢): AmbientLight DirectionalLight PointLight SpotLight |
MeshGeometry3D | 尝试将网格大小保持在以下限制之下: Positions:20,001 个 Point3D 实例 TriangleIndices:60,003 个 Int32 实例 |
Material | 材料速度(最快至慢): EmissiveMaterial DiffuseMaterial SpecularMaterial |
Brush | Windows Presentation Foundation (WPF) 3D 不会以一致的方式选择退出不可见的画笔(黑色环境画笔和清除画笔等)。 请考虑从场景中省略这些内容。 |
MaterialGroup | MaterialGroup 中的每一个 Material 都会导致另一个呈现传递,因此包括许多材料,甚至是简单的材料,都可以显著增加 GPU 上的填充需求。 尽量减少 MaterialGroup 材料的数量。 |
1.6.3 性能影响:低
属性 | 建议 |
---|---|
Transform3DGroup | 如果不需要动画或数据绑定,与其使用包含多个转换的转换组,不如使用单个 MatrixTransform3D,将其设置为转换组中独立存在的所有转换的乘积。 |
Light | 最大程度地减少场景中的灯光数量。 场景中的灯光过多将迫使 Windows Presentation Foundation (WPF) 回退到软件渲染。 限制约为 110 个 DirectionalLight 对象、70 个 PointLight 对象或 40 个 SpotLight 对象。 |
ModelVisual3D | 通过将移动对象与静态对象置于不同的 ModelVisual3D 实例中,将它们分开。 ModelVisual3D 比 GeometryModel3D 更“负担重”,因为它缓存了转换后的边界。 GeometryModel3D 已优化为模型;ModelVisual3D 已优化为场景节点。 使用 ModelVisual3D 将 GeometryModel3D 的共享实例放入场景中。 |
Light | 最大程度地减少更改场景中的灯数的次数。 光计数的每个更改都强制着色器重新生成和重新编译,除非以前存在该配置(因此缓存了着色器)。 |
Light | 黑光灯不可见,但它们会增加渲染时间,可以考虑省略。 |
MeshGeometry3D | 若要最大程度地减少 Windows Presentation Foundation(WPF)中大型集合的构造时间,例如 MeshGeometry3D 的 Positions、Normals、TextureCoordinates和 TriangleIndices,在值填充之前预先调整集合的大小。 如果可能,请向集合的构造函数传递预填充的数据结构,例如数组或列表。 |
1.7 常见问题
-
模型不可见:
- 检查相机位置和观察方向
- 确认法向量方向是否正确(使用
Vector3D.CrossProduct
计算) - 验证光源是否设置
-
锯齿问题:启用抗锯齿
RenderOptions.SetEdgeMode(viewport3D, EdgeMode.Aliased); RenderOptions.SetBitmapScalingMode(viewport3D, BitmapScalingMode.HighQuality);
1.8 扩展工具
- Helix Toolkit:开源3D库(提供球体、圆柱等预制形状,支持直接从obj、3ds等模型文件加载为GeometryModel3D)
相关文章:
.NET WPF 三维模型
文章目录 .NET WPF 三维模型1 Viewport3D1.1 3D 坐标系1.2 核心组件1.2.1 相机 (Camera)1.2.2 光源 (Light)1.2.3 3D 模型(Model3D) 1.3 模型纹理(Material)1.4 完整示例:创建坐标轴与立方体1.5 转换模型1.6 性能1.6.1…...
iOS 中的虚拟内存 (理解为什么需要虚拟内存)
什么叫“虚拟地址空间”? 一句话:它是 CPU 看得见、App 以为自己独享,但实际上会被内核和硬件(MMU)动态翻译到真实 物理内存 的一整块“虚拟地图”。 1. 背景:为什么要“虚拟”? 需求虚拟地址空…...
算法之动态规划
动态规划 动态规划1. 核心思想2. 基本步骤3. 关键概念3.1 基本概念3.2 优化技巧 4. 常见应用场景5. 典型案例5.1 斐波那契数列5.2 背包问题5.2.1 0-1背包问题5.2.2 完全背包问题 5.3 最短路径——Floyd算法5.4 最长公共子序列(LCS)5.5 最长递增子序列&am…...
leetcode0130. 被围绕的区域- medium
1 题目:被围绕的区域 官方标定难度:中 给你一个 m x n 的矩阵 board ,由若干字符 ‘X’ 和 ‘O’ 组成,捕获 所有 被围绕的区域: 连接:一个单元格与水平或垂直方向上相邻的单元格连接。 区域:…...
衡石科技ChatBI--飞书数据问答机器人配置详解(附具体操作路径和截图)
先决条件 需要在衡石系统认证方式中配置好飞书认证方式,具体步骤详见认证方式中关于飞书的部分。先完成这部分配置后,再进行以下步骤。 飞书中创建机器人应用 1. 创建飞书应用 在飞书企业自建应用管理中创建应用,设置logoÿ…...
25.解决中医知识问答删除历史对话功能后端处理请求时抛出异常
ChatTest.vue:176 DELETE http://localhost:8080/api/chat/conversations/20 500 (Internal Server Error) deleteConversation ChatTest.vue:176 onClick ChatTest.vue:22 ChatTest.vue:185 删除失败 AxiosError {message: Request failed with status code 500, name: Axio…...
【解决方法】关于解决QGC地面站4.4.3中文BUG,无法标注航点的问题
GC以中文启动时无法标记航点,只有在英文状态下启动然后转换为中文才能标记航点。这个BUG源于中文翻译脚本里面以中文逗号作为多个选项的分隔符,导致编译器认为这个只是一个整体。所以翻译时数量不匹配,导致BUG。 解决方法:将所有…...
Flowith AI,解锁下一代「知识交易市场」
前言 最近几周自媒体号都在疯狂推Manus,看了几篇测评后,突然在某个时间节点,在特工的文章下,发现了很小众的Flowith。 被这段评论给心动到,于是先去注册了下账号。一翻探索过后,发现比我想象中要有趣的多&…...
【AI实战】基于DeepSeek构建个性化AI对话代理:从提示词工程到完整实现
作为开发者,我们经常需要与AI进行各种交互。本文将详细介绍如何通过提示词工程(prompt engineering)构建个性化的AI对话代理,并使用DeepSeek的API实现完整解决方案。 一、个性化AI代理的核心要素 1.1 角色设定(Role Setting) 角色设定是构建个性化AI的…...
基于ueditor编辑器的功能开发之重写ueditor的查找和替换功能,支持滚动定位
百度编辑器的查找和替换无法随着页面滚动定位,找到searchreplace.js,重写里面的方法 效果展示: 20250421173735 思路: 找到查找和替换的输入框,发现id名分别为findtxt和findtxt1,分别绑定change事件&…...
分布式数据库TiDB:架构、核心特性与生产实践(分库分表)
在云计算与大数据时代,传统单机数据库面临三大挑战:海量数据存储、高并发访问和实时分析需求。MySQL分库分表方案复杂、NoSQL缺乏ACID支持、MPP数仓难以处理OLTP... 在这样的背景下,TiDB应运而生。作为一款开源的分布式NewSQL数据库ÿ…...
用自然语言指令构建机器学习可视化编程流程:InstructPipe 的创新探索
想要掌握如何将大模型的力量发挥到极致吗?叶梓老师带您深入了解 Llama Factory —— 一款革命性的大模型微调工具(限时免费)。 1小时实战课程,您将学习到如何轻松上手并有效利用 Llama Factory 来微调您的模型,以发挥其…...
利用WSL2的镜像功能访问Windows下的所有网卡
目录 引言 镜像功能 如何设置 自动代理 结语 引言 我通常用PC上的LAN口去连接开发板,但是在WSL2中要访问LAN口连接的开发板有点麻烦。WSL2默认的网络模式为NAT,如果要访问Windows中网口需要设置桥接,比较繁琐。今天尝试了一下Windows 1…...
AI助理iOS开发:Copilot for Xcode 下载与安装全指南
引言 借助 Copilot for Xcode 也有两年了,如今已经变成了日常开发中的“默契搭档”。它能根据上下文补全代码,快速生成常用逻辑,甚至有时候在我还在思考怎么写的时候,它就已经给出了不错的建议。特别是在写一些重复性较高的代码&…...
Hadoop+Spark 笔记 2025/4/21
定义 1. 大数据(Big Data) - 指传统数据处理工具难以处理的海量、高速、多样的数据集合,通常具备3V特性(Volume体量大、Velocity速度快、Variety多样性)。扩展后还包括Veracity(真实性)和Va…...
模拟车辆变道 python 可视化
目录 车头朝向一起变化 车头朝向不变化,矩形框 车头朝向一起变化 import cv2 import numpy as npdef world_to_pixel(world_x, world_y, img_w=800, img_h=800):scale_x = img_w / 120 # 横向范围:0~120米scale_y = img_h / 80 # 纵向范围:0~80米pixel_x = int(world_x …...
国产仪器进化论:“鲁般号”基于无人机的天线测试系统
2025年4月14日,成都玖锦科技有限公司正式发布了新品:“鲁般号会飞的系统”系列,这是玖锦科技首款基于无人机的天线方向图测试系统。 在“振兴民族产业,打造民族品牌”的征途中,“鲁般号”系列是继“墨子”、“孔明”、…...
Linux学习笔记协议篇(六):SPI FLASH设备驱动
目录 一、设备树解析 二、SPI设备驱动代码分析 1、spi_nor_probe 2、spi_nor_scan (1)协议配置 (2)初始化Flash参数(核心步骤) (3)MTD子系统集成 (3)配置 SPI 通信参数 spi…...
Spring Boot 核心模块全解析:12 个模块详解及作用说明
在当今的微服务与云原生时代,Spring Boot 已成为构建现代 Java 应用的事实标准。它通过“约定优于配置”的理念,大大降低了 Spring 应用的开发门槛,帮助开发者快速启动和部署独立的、生产级别的项目。 本篇文章将系统梳理 Spring Boot 框架中…...
【无人机】无人机方向的设置,PX4飞控方向,QGC中设置飞控的方向/旋转角度。PX4使用手册飞行控制器/传感器方向
目录 #1、基本概念:计算方向 #2、详细步骤:设置方向 #3、微调 默认情况下,飞行控制器(和外部指南针,如果有)应放置在框架顶部朝上,方向应使箭头指向飞机前部。 如果板或外部指南针安装在任何…...
【Spring Boot基础】MyBatis的基础操作:日志、增删查改、列名和属性名匹配 -- 注解实现
MyBatis的基础操作 1.打印日志2. 参数传递2.1不传参2.2 固定参数 3. 增(Insert)3.1 用对象接参3.2 用param注解接收参数3.3 返回主键 4. 删(Delete)4.1 用Integer接参4.2 用对象接参 5. 改(Update)6. 查(Select)6.1 查6.2 拼接SQL语句6.3 列名和属性名匹配6.3.1 起别名 as6.3.2…...
泰迪智能科技大模型应用平台功能特色优势
1.平台概述 大模型应用平台是一款专为高校在大模型应用场景下的教学和科研需求设计的知识库问答系统。平台具备便捷性,支持上传常见格式的数据文件,如txt、doc、pdf、md等,并提供简洁明了的操作配置界面,使用户能够轻松搭建和训练…...
【NLP 69、KG - BERT】
人们总是在无能为力的时候喜欢说顺其自然 —— 25.4.21 一、KG-BERT:基于BERT的知识图谱补全模型 1.模型结构与设计 Ⅰ、核心思想: 将知识图谱中的三元组(头实体-关系-尾实体)转化为文本序列,利用BERT的上下文理解能…...
Spring解决循环依赖
Spring 通过 三级缓存机制 解决循环依赖问题,其核心思想是 提前暴露未完全初始化的 Bean,允许依赖方在 Bean 完全初始化前引用其早期版本。以下是详细解析: 一、三级缓存机制 Spring 在单例 Bean 的创建过程中维护了三级缓存,用于…...
深入解析 Spring 中的 @Value 注解(含源码级剖析 + 自定义实现)
深入解析 Spring 中的 Value 注解(含源码级剖析 自定义实现) 在 Spring 开发中,我们经常使用 Value 注解将配置文件中的值注入到 Bean 的属性中。本文将深入探讨 Value 的使用方式、默认值支持、底层原理以及自定义实现方式。 一、Value 的…...
【Flink SQL实战】 UTC 时区格式的 ISO 时间转东八区时间
文章目录 一、原始数据格式二、解决方案三、其他要求 在实际开发中,我们常常会遇到此类情况:数据源里的时间格式是类似 2025-04-21T09:23:16.025Z 这种带 TimeZone 标识的 ISO 8601 格式,而我们需要在 Flink SQL 中将其转换成北京时间显示。 …...
【论文阅读23】-地下水预测-TCN-LSTM-Attention(2024-11)
这篇论文主要围绕利用深度学习模型检测地下水位异常以识别地震前兆展开。 [1] Chen X, Yang L, Liao X, et al. Groundwater level prediction and earthquake precursor anomaly analysis based on TCN-LSTM-attention network[J]. IEEE Access, 2024, 12: 176696-176718. 期刊…...
/proc/sys/vm/下各参数含义
/proc/sys/vm/下各参数含义 admin_reserve_kbytes如何计算最小有效预留? compact_memorycompaction_proactivenesscompact_unevictable_alloweddirty_background_bytesdirty_background_ratiodirty_bytesdirty_expire_centisecsdirty_ratiodirtytime_expire_seconds…...
算法分析与设计——动态规划复习题(待更新
检测题: 组合优化问题的目标函数通常不包括以下哪种形式? A. 需最小化的代价函数 B. 需最大化的回报函数 C. 需满足的硬约束条件 D. 需最小化的能量函数 答案:C 关于约束条件的说法,以下哪项是正确的? A. 硬约束可以通…...
【EasyPan】项目常见问题解答(自用持续更新中…)
EasyPan 网盘项目介绍 一、项目概述 EasyPan 是一个基于 Vue3 SpringBoot 的网盘系统,支持文件存储、在线预览、分享协作及后台管理,技术栈涵盖主流前后端框架及中间件(MySQL、Redis、FFmpeg)。 二、核心功能模块 用户认证 注册…...
基于Java的不固定长度字符集在指定宽度和自适应模型下图片绘制生成实战
目录 前言 一、需求介绍 1、指定宽度生成 2、指定列自适应生成 二、Java生成实现 1、公共方法 2、指定宽度生成 3、指定列自适应生成 三、总结 前言 在当今数字化与信息化飞速发展的时代,图像的生成与处理技术正日益成为众多领域关注的焦点。从创意设计到数…...
电子电器架构 ---软件定义汽车的电子/电气(E/E)架构
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…...
Stable Diffusion 制作角色三视图
对于漫画创作,DPM 2M Karras和UniPC是高效且稳定的首选采样方法,结合Karras噪声调度可显著提升画面质量。若需进一步优化,可参考具体场景调整步数并辅以ControlNet等工具。避免使用随机性强的采样器(如Euler a)&#x…...
C++--负载均衡在线OJ
这是本人写的第二个项目,相比第一个代码量更少一些,但是此项目涉及linux中的内容更多,同样是干货满满,实现了 类似 leetcode 的题⽬列表在线编程功能,地址仓库:xwy/C学习项目 1. 所用技术与开发环境 C11和…...
【数字图像处理】彩色图像处理(1)
研究彩色图像处理的原因 1:利用颜色信息,可以简化目标物的区分,以及从场景中提取出目标物 2:人眼对颜色非常敏感,可以分辨出来几千种颜色色调和亮度,却只能分别出几十种灰度 彩色图像分类 伪彩色图像处理&…...
【Easylive】consumes = MediaType.MULTIPART_FORM_DATA_VALUE 与 @RequestPart
【Easylive】项目常见问题解答(自用&持续更新中…) 汇总版 consumes MediaType.MULTIPART_FORM_DATA_VALUE 的作用 1. 定义请求的数据格式 • 作用:告诉 Feign 和 HTTP 客户端,这个接口 接收的是 multipart/form-data 格式的…...
【python】copy deepcopy 赋值= 对比
上结论 写法是否独立是否安全修改copy() (用于一维列表)✅ 是独立副本✅ 安全deepcopy() (多层结构时用)✅ 是完全副本✅ 安全直接赋值()❌ 是引用❌ 改一个会影响另一个 一、.copy() 和 deepcopy() 有什…...
环形缓冲区容量耗尽解决方案
以下是针对环形缓冲区在时间窗口统计场景中容量耗尽问题的解决方案设计及优劣分析,结合搜索结果中的技术原理和工程实践: 一、核心问题定位 当环形缓冲区容量耗尽时,新数据覆盖旧数据会导致: 时间窗口统计失真:无法准…...
蓝桥杯 17.发现环
发现环 原题目链接 题目描述 小明的实验室有 N 台电脑,编号 1 ⋯ N。 原本这 N 台电脑之间有 N−1 条数据链接相连,恰好构成一个树形网络。 在树形网络上,任意两台电脑之间有唯一的路径相连。 不过在最近一次维护网络时,管理…...
数据库服务器架构
ORM ORM(Object Relational Mapping):对象与关系数据之间的映射 映射关系表: 类(class)—— 数据库的表(table) 对象(object)——记录(record…...
Netty前置基础知识之BIO、NIO以及AIO理论详细解析和实战案例
前言 Netty是什么? Netty 是一个基于 Java 的 高性能异步事件驱动网络应用框架,主要用于快速开发可维护的协议服务器和客户端。它简化了网络编程的复杂性,特别适合构建需要处理海量并发连接、低延迟和高吞吐量的分布式系统。 1)Netty 是…...
职坐标IT培训:人工智能职业跃迁路径
随着人工智能时代全面来临,职业发展格局正经历颠覆性重构。政策端,《新一代人工智能发展规划》与《生成式AI服务管理办法》双轨并行,既为行业注入动能,也划定了技术应用的合规边界。在此背景下,从业者需构建覆盖基础理…...
Redis 的单线程模型对微服务意味着什么?需要注意哪些潜在瓶颈?
Redis 的单线程模型是其高性能的关键因素之一,但这在微服务场景下既是优势,也可能带来潜在的瓶颈。理解这一点有助于我们在微服务架构中更好的使用Redis。 Redis 单线程模型的核心: 命令处理是单线程的: Redis 使用了一个主线程来接收客户端…...
Redis 有序集合(Sorted Set)
Redis 有序集合(Sorted Set) 以下从基础命令、内部编码和使用场景三个维度对 Redis 有序集合进行详细解析: 一、基础命令 命令时间复杂度命令含义zadd key score member [score member …] O ( k l o g ( n ) ) O(klog(n)) O(klog(n))&…...
C语言中联合体(Union)和结构体(Struct)的嵌套用法
联合体和结构体是C语言中两种重要的复合数据类型,它们可以相互嵌套使用,为复杂数据的表示提供了灵活的方式。 1. 联合体(Union)基础 联合体是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。联合体的所有成员共享同一块内存空…...
Rust: 从内存地址信息看内存布局
内存布局其实有几个:address(地址)、size(大小)、alignment(对齐位数,2 的自然数次幂,2,4,8…)。 今天主要从address来看内存的布局。 下面以Str…...
分类算法中one-vs-rest策略和one-vs-one 策略的区别是什么?
LGBMClassifier 参数中,常使用objective: 这个参数定义了模型的目标函数。 而对于多分类问题,通常使用 multiclass 或者 multiclassova。multiclass 表示 one-vs-rest 策略,而 multiclassova 则是 one-vs-one 策略。 在机器学习领域&#x…...
新能源汽车充电桩运营模式的发展与优化路径探析
摘要:以民用新能源汽车充电桩为研究对象,在分析政府主导型、电网企业主导型及汽车厂商主导型三种运营模式特点的基础上,结合我国新能源汽车发展现状,提出汽车厂商与电网企业协同共建的联盟模式。通过构建涵盖政府补贴、建设成本与…...
【前端样式】用 aspect-ratio 实现等比容器:视频封面与图片占位的终极解决方案
在网页开发中,处理视频封面、图片卡片等需要固定比例的容器一直是前端工程师的必修课。本文将以 aspect-ratio 属性为核心,深入探讨如何优雅实现等比容器,并通过完整代码示例和常见问题解析,助你彻底掌握这一现代布局利器。 目录…...
redis常用的五种数据类型
redis常用的五种数据类型 文档 redis单机安装redis数据类型-位图bitmap 说明 官网操作命令指南页面:https://redis.io/docs/latest/commands/?nameget&groupstring 常用命令 keys *:查看所有键exists k1 k2:键存在个数type k1&…...