1

我有以下图像,我需要在 a 中显示Viewport3D

在此处输入图像描述

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

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

在此处输入图像描述

图像用蓝线表示,w是图像宽度(w=2)。

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

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

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

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

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不显示整个图像:

在此处输入图像描述

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

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

4

2 回答 2

1

You are on the right track. PerspectiveCamera field of view (fov) is based on the Viewport3D width and tan(fov/2) accepts fov in radians. So camera z is:

    double fieldOfViewInRadians = perspectiveCamera.FieldOfView * (Math.PI / 180.0);
    var z = (0.5 * _viewport3D.Width) / Math.Tan(0.5 * fieldOfViewInRadians);
    perspectiveCamera.Position = new Point3D(0.0, 0.0, z);

Ensure that Control Panel > Display is set to Small 100%. There's a section which allows you to set Small (100%), Medium, and Large scaling. That also has an impact.

于 2013-11-02T03:41:26.790 回答
0

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

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

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

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
于 2013-06-29T18:25:16.927 回答