WPF 精确计算三维元素的投影范围

WPF 精确计算三维元素的投影范围

在 WPF 程序中,二维元素可以使用 VisualTreeHelper.GetDescendantBounds(Visual reference) 来获取其内容的范围,三维元素可以使用 GetContentBounds(Visual3D reference) 来获取其在二维平面的投影范围。然而,当三维元素中的模型非正向(存在旋转或偏移)时,上面方法获取的范围并不准确,本文将介绍一种准确计算 WPF 三维投影范围的方法。

GetContentBounds

首先,我们来看看如何通过 GetContentBounds() 方法获取投影范围。如下所示,使用 XAML 标记语言在界面上生成一个立方体和一个矩形,我们将用矩形来标记计算得到的立方体投影范围。先来看看模型未旋转情况下的结果:

Code-xaml:

<Grid Background="LightGray">
    <Viewport3D x:Name="MyViewport">
        <Viewport3D.Camera>
            <OrthographicCamera Position="3 3 5" LookDirection="-3 -3 -5" Width="3"/>
        </Viewport3D.Camera>
        <Viewport3D.Children>
            <ModelVisual3D>
                <ModelVisual3D.Content>
                    <DirectionalLight Color="White" Direction="-1 -1 -1"/>
                </ModelVisual3D.Content>
            </ModelVisual3D>
            <ModelVisual3D x:Name="MyVisual">
                <ModelVisual3D.Content>
                    <GeometryModel3D>
                        <GeometryModel3D.Geometry>
                            <MeshGeometry3D Positions="0,0,0 1,0,0 0,1,0 1,1,0 0,0,1 1,0,1 0,1,1 1,1,1"
                                            TriangleIndices="0,2,1 1,2,3 0,4,2 2,4,6 0,1,4 1,5,4 1,7,5 1,3,7 4,5,6 7,6,5 2,6,3 3,6,7"/>
                        </GeometryModel3D.Geometry>
                        <GeometryModel3D.Material>
                            <DiffuseMaterial Brush="Red"/>
                        </GeometryModel3D.Material>
                        <!--<GeometryModel3D.Transform>
                            <RotateTransform3D>
                                <RotateTransform3D.Rotation>
                                    <AxisAngleRotation3D Axis="1 1 0" Angle="5"/>
                                </RotateTransform3D.Rotation>
                            </RotateTransform3D>
                        </GeometryModel3D.Transform>-->
                    </GeometryModel3D>
                </ModelVisual3D.Content>
            </ModelVisual3D>
        </Viewport3D.Children>
    </Viewport3D>
    <Rectangle x:Name="MyRegion" Stroke="Blue" StrokeThickness="1" VerticalAlignment="Top" HorizontalAlignment="Left"/>
</Grid>

Code-behing:

var bounds = VisualTreeHelper.GetDescendantBounds(MyViewport);

MyRegion.Width = bounds.Width;
MyRegion.Height = bounds.Height;

// 使用计算得到的投影范围来设定长方形的位置和大小
MyRegion.Margin = new Thickness(bounds.Left, bounds.Top, 0, 0);

如上图所示,在模型未旋转的情况下,范围计算准确,矩形与三维模型的边界完全重合。我们取消 XAML 中注释掉的代码,即给模型加上旋转,再看看效果:

<GeometryModel3D.Transform>
    <RotateTransform3D>
        <RotateTransform3D.Rotation>
            <AxisAngleRotation3D Axis="1 1 0" Angle="5"/>
        </RotateTransform3D.Rotation>
    </RotateTransform3D>
</GeometryModel3D.Transform>

如上图所示,当模型带了 Transform 后,GetContentBounds() 方法得到的范围会比立方体的实际投影范围大一些,这个较大的范围应该是旋转后的三维范围直接乘以投影矩阵的结果。

逐点计算投影范围

为了解决 后,GetContentBounds() 计算不准确的问题,我们可以首先精确地计算出各个顶点的投影坐标,然后取坐标合集,即可得到整个三维的投影区域。

对于一个三维元素(Visual3D),我们需要先找到其二维投影平面(Viewport3DVisual),然后求得三维到二维投影平面之间的投影矩阵(GeneralTransform3DTo2D),最后计算三维元素中各个三维顶点经过投影矩阵变换后的二维坐标,将所有的二维坐标求合集变得到整个三维的投影范围。需要注意的是,Visual3D 有多种类型,需要分类处理;并且三维模型存在嵌套、本身还可能存在变换(Transform3D),这些都需要叠加到整体的投影变换中。

/// <summary>
/// 计算三维的投影区域。
/// </summary>
/// <param name="visual"></param>
/// <returns></returns>
public static Rect CalculateBounds(Visual3D visual)
{
    // 三维到二维的投影矩阵
    var transform = visual.TransformToAncestor(GetViewport3DVisual(visual));
    if (transform == null)
    {
        return Rect.Empty;
    }

    var bounds = Rect.Empty;

    // 计算各个子模型的投影区域,然后取合集
    var modelVisual3D = visual as ModelVisual3D;
    if (modelVisual3D != null)
    {
        bounds.Union(CalculateBounds(transform, modelVisual3D.Content, Matrix3D.Identity));

        // Union the bounds of Children
        foreach (var child in modelVisual3D.Children)
        {
            bounds.Union(CalculateBounds(child));
        }
    }
    else
    {
        // UIElement3D or Viewport2DVisual3D 
        bounds.Union(transform.TransformBounds(VisualTreeHelper.GetDescendantBounds(visual)));
    }

    return bounds;
}

/// <summary>
/// 计算指定 <see cref="Model3D"/> 中各个顶点的投影坐标集合。
/// </summary>
public static Rect CalculateBounds(GeneralTransform3DTo2D transform, Model3D model, Matrix3D rootMatrix)
{
    var region = Rect.Empty;
    var matrix = Matrix3D.Identity;

    matrix.Prepend(rootMatrix);
    if (model.Transform != null)
    {
        matrix.Prepend(model.Transform.Value);
    }

    var geometryModel3D = model as GeometryModel3D;
    if (geometryModel3D != null)
    {
        var meshGeometry3D = geometryModel3D.Geometry as MeshGeometry3D;
        if (meshGeometry3D != null)
        {
            var innerTransform = new MatrixTransform3D(matrix);
            foreach (var position in meshGeometry3D.Positions)
            {
                region.Union(transform.Transform(innerTransform.Transform(position)));
            }
        }
    }
    else
    {
        var model3DGroup = model as Model3DGroup;
        if (model3DGroup != null)
        {
            foreach (var child in model3DGroup.Children)
            {
                region.Union(CalculateBounds(transform, child, matrix));
            }
        }
    }

    return region;
}

/// <summary>
/// 获取三维的投影平面。
/// </summary>
public static Viewport3DVisual GetViewport3DVisual(Visual3D visual3D)
{
    DependencyObject obj = visual3D;

    while (obj != null)
    {
        var visual = obj as Viewport3DVisual;
        if (visual != null)
        {
            return visual;
        }

        obj = VisualTreeHelper.GetParent(obj);
    }

    return null;
}
private void Usage()
{
    var bounds = CalculateBounds(MyVisual);
}

参考资料

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
WPF(Windows Presentation Foundation)开发三维设计软件一种利用Microsoft的WPF技术来创建具有三维功能的设计应用程序的方法。WPF是一种用于构建Windows桌面应用程序的UI框架,它提供了强大的图形渲染和用户界面控件。 下面是一些实现基于WPF三维设计软件的步骤: 1. 项目设置:创建一个WPF项目,并设置合适的项目属性和引用。确保项目包含所需的3D渲染和图形库。 2. 界面设计:设计一个用户友好的界面,包括工具栏、视图窗口、属性面板等元素。用户可以通过界面进行三维模型的创建、编辑和查看。 3. 3D模型创建:使用WPF提供的3D图形库创建三维模型。WPF支持基本的几何体(如立方体、圆柱体、球体)和复杂的网格模型。可以使用编程方式或XAML来定义和构建模型。 4. 模型编辑:实现对三维模型的编辑功能,例如平移、旋转、缩放等操作。通过WPF的输入事件和变换操作,可以实现对模型的交互式编辑。 5. 材质和纹理:为三维模型应用适当的材质和纹理。WPF提供了多种材质类型(如颜色、纹理、渐变等),可以根据需求进行设置。 6. 光照和阴影:添加光源来照亮三维模型,并生成逼真的阴影效果。WPF支持不同类型的光源(如定向光、点光源、聚光灯),可以通过设置光源参数来调整光照效果。 7. 动画和交互:实现模型的动画效果和用户交互。WPF提供了动画和触发器功能,可以创建模型的变换动画或响应用户操作的交互效果。 8. 导出和保存:实现将三维模型导出为文件或保存到数据库的功能。这样用户可以在其他应用程序中使用或分享他们的设计。 需要注意的是,基于WPF开发三维设计软件需要有一定的前端开发知识和对WPF框架的理解。同时,考虑到性能和用户体验,需要进行优化和测试,以确保软件在各种Windows设备上的良好运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ironyho

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值