WPF 3D:使用PerspectiveCamera调整整个图像

时间:2013-06-29 17:26:23

标签: wpf 3d perspectivecamera

我需要在Viewport3D显示以下图片:

enter image description here

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

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

enter image description here

图像用蓝线表示, 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不会显示整个图像:

enter image description here

还有其他因素在起作用吗?我不想在计算中使用任何调整或软糖因素。

请注意,调整窗口大小对水平视野范围没有影响,因为这是由相机FOV及其距离决定的,因此问题与控制尺寸无关 - 它与WPF项目指向的方式有关从3D到2D。

2 个答案:

答案 0 :(得分:1)

你走在正确的轨道上。 PerspectiveCamera视野(fov)基于Viewport3D宽度,tan(fov / 2)以弧度接受fov。所以相机z是:

    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);

确保控制面板&gt;显示设置为小100%。有一个部分允许您设置小(100%),中等和大缩放。这也有影响。

答案 1 :(得分:0)

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

  1. 将相机设置为已知距离(即1.0)
  2. 使用Visual3D.TransformToAncestor
  3. 计算模型的2D边界框
  4. 计算比例因子(边界框大小/视口大小)
  5. 按比例因子乘以相机距离
  6. 换句话说,如果相机再两次,图像会小两倍......

    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