WPF 3D:拟合整个图像以使用 PerspectiveCamera 查看 [英] WPF 3D: Fitting entire image to view with PerspectiveCamera

查看:20
本文介绍了WPF 3D:拟合整个图像以使用 PerspectiveCamera 查看的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在 Viewport3D 中显示以下图像:

I have the following image which I need to display in a Viewport3D:

图像纹理以(0,0)为中心,其角坐标为(-1,-1,0),(1,-1,0),(-1,1,0),(1,1,0).

The image texture is centered at (0,0) and its corner coordinates are (-1,-1,0), (1,-1,0),(-1,1,0),(1,1,0).

由于我使用具有固定视野的 PerspectiveCamera,我必须计算足够的距离才能将整个图像放入视野中:

Since I am using PerspectiveCamera with a fixed field of view, I have to compute sufficient distance to put entire image into view:

图片用蓝线表示,w是图片宽度(w=2).

Image is represented by a blue line, w is image width (w=2).

相机位置是 (0,0,d),因此形成了一个三角形:

Camera position is (0,0,d) and hence a triangle is formed:

tan(fov/2) = (w/2) / d

d = (w/2) / tan(fov/2)

现在我将用于 3D 模型的 XAML 代码和用于计算相机距离的代码放在一起:

Now I put together XAML code for the 3D model and code-behing for computing camera distance:

XAML

<Window x:Class="Render3DTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="256" Width="256" Loaded="Window_Loaded">
    <Grid>

        <Viewport3D Name="viewport">

            <Viewport3D.Camera>
                <PerspectiveCamera Position="0,0,1" LookDirection="0,0,-1" FieldOfView="90" />
            </Viewport3D.Camera>

            <ModelVisual3D>
                <ModelVisual3D.Content>
                    <AmbientLight/>
                </ModelVisual3D.Content>
            </ModelVisual3D>

            <ModelVisual3D>
                <ModelVisual3D.Content>
                    <GeometryModel3D>
                        <GeometryModel3D.Material>
                            <DiffuseMaterial>
                                <DiffuseMaterial.Brush>
                                    <ImageBrush ImageSource="image.jpg"/>
                                </DiffuseMaterial.Brush>
                            </DiffuseMaterial>
                        </GeometryModel3D.Material>
                        <GeometryModel3D.Geometry>
                            <MeshGeometry3D Positions="-1,1,0 -1,-1,0 1,-1,0 1,1,0" TextureCoordinates="0,0 0,1 1,1 1,0" TriangleIndices="0 1 2, 0 2 3" />
                        </GeometryModel3D.Geometry>
                    </GeometryModel3D>
                </ModelVisual3D.Content>
            </ModelVisual3D>

        </Viewport3D>

    </Grid>
</Window>

代码隐藏

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    PerspectiveCamera camera = (PerspectiveCamera)this.viewport.Camera;

    double d = (1.0 / Math.Tan(camera.FieldOfView / 2.0));

    camera.Position = new Point3D(0.0, 0.0, d);
}

但是,Viewport3D 不会显示整个图像:

However, the Viewport3D does not display entire image:

还有其他因素在起作用吗?我不想在我的计算中使用任何调整或捏造因素.

Is there some other factor playing role? I don't want to use any tweaks or fudge factors in my computation.

请注意,调整窗口大小不会影响视图的水平范围,因为这是由相机 FOV 及其距离决定的,因此问题与控件大小无关 - 它与 WPF 如何投影点有关从 3D 到 2D.

Please note that resizing the window does not have effect on horizontal range of view as this is determined by camera FOV and its distance, so the problem does not have anything to do with control size - it is related to how WPF projects points from 3D to 2D.

推荐答案

我想出了一种利用透视相机线性度的可能解决方案.

I figured out one possible solution making use of the linearity of the perspective camera.

  1. 将相机设置为已知距离(即 1.0)
  2. 使用 Visual3D.TransformToAncestor
  3. 计算模型的 2D 边界框
  4. 计算比例因子(边界框大小/视口大小)
  5. 将相机距离乘以比例因子

换句话说,如果相机再远两倍,图像就会小两倍......

In other words, if the camera is twice further, the image is twice smaller...

PerspectiveCamera camera = (PerspectiveCamera)this.viewport.Camera;

// set camera to a known distance
camera.Position = new Point3D(0.0, 0.0, 100.0);

Point3D[] points3D = new[]
{
    new Point3D(-1.0, -1.0, 0.0),
    new Point3D(1.0, -1.0, 0.0),
    new Point3D(-1.0, 1.0, 0.0),
    new Point3D(1.0, 1.0, 0.0)
};

double minX = Double.MaxValue;
double maxX = Double.MinValue;
double minY = Double.MaxValue;
double maxY = Double.MinValue;

GeneralTransform3DTo2D transform = this.viewport.Children[1].TransformToAncestor(this.viewport);

foreach (var point3D in points3D)
{
    Point point2D = transform.Transform(point3D);

    minX = Math.Min(minX, point2D.X);
    maxX = Math.Max(maxX, point2D.X);

    minY = Math.Min(minY, point2D.Y);
    maxY = Math.Max(maxY, point2D.Y);
}

Size currentSize = new Size(maxX - minX, maxY - minY);
Size desiredSize = new Size(this.viewport.ActualWidth, this.viewport.ActualHeight);

double scaleFactor = Math.Max(
    currentSize.Width / desiredSize.Width,
    currentSize.Height / desiredSize.Height);

camera.Position = new Point3D(0.0, 0.0, 100.0 * scaleFactor); // the known distance of 100.0 is multiplied by scaleFactor

这篇关于WPF 3D:拟合整个图像以使用 PerspectiveCamera 查看的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆