WPF 3D: Fitting entire image to view with PerspectiveCamera

无人久伴 提交于 2019-12-06 16:10:55

问题


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

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

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

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

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

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

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

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>

code-behind

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

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.

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.


回答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.




回答2:


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

  1. Set camera to a known distance (i.e. 1.0)
  2. Compute 2D bounding box of the model using Visual3D.TransformToAncestor
  3. Compute scale factor (bounding box size / viewport size)
  4. Multiply camera distance by the scale factor

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


来源:https://stackoverflow.com/questions/17382853/wpf-3d-fitting-entire-image-to-view-with-perspectivecamera

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!