如何在Matrix3D中设置这种透视变换?

发布于 2024-09-10 02:30:57 字数 1302 浏览 4 评论 0原文

我有一个图像,并且有一些值可以使其成为 Silverlight 中的透视图,但无法完全弄清楚我需要在数学上做什么才能实现它。最重要的是我有一个称为“视野”(FOV)的角度。

这是正常的图片: normal

例如:

X =   30°             X =   30°             X   =  30°
FOV = 30°             FOV = 60°             FOV = 120°
        

X =   60°             X =   60°               X =  60°
FOV = 30°             FOV = 60°             FOV = 120°
                       



如果有任何帮助,请帮助我完成数学计算在 Silverlight 中重现这些。

I have an image with and have a few values to make it a perspective in Silverlight, but can't quite figure out what I need to do mathmatically to make it happen. The most important thing is I have an angle called a "Field of View" (FOV).

This is the normal picture:
normal

For example:

X =   30°             X =   30°             X   =  30°
FOV = 30°             FOV = 60°             FOV = 120°
        

X =   60°             X =   60°               X =  60°
FOV = 30°             FOV = 60°             FOV = 120°
                       



Any help would be appreciated to walk me through the math to reproduce these in Silverlight.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

秋心╮凉 2024-09-17 02:30:57

我认为每个人都遇到的问题是随着透视变换需要视口移动。

尝试一下:

private void ApplyProjection()
{
    double fovY = FOV * Math.PI / 180 / 2.0;
    double translationZ = -image1.ActualHeight / Math.Tan(fovY / 2.0);
    double theta = YourAngleX * Math.PI / 180.0;

    Matrix3D centerImageAtOrigin = TranslationTransform(
             -image1.ActualWidth / 2.0,
             -image1.ActualHeight / 2.0, 0);
    Matrix3D invertYAxis = CreateScaleTransform(1.0, -1.0, 1.0);
    Matrix3D rotateAboutY = RotateYTransform(theta);
    Matrix3D translateAwayFromCamera = TranslationTransform(0, 0, translationZ);
    Matrix3D perspective = PerspectiveTransformFovRH(fovY,
            image1.ActualWidth / image1.ActualHeight,   // aspect ratio
            1.0,                                                // near plane
            1000.0);                                            // far plane
    Matrix3D viewport = ViewportTransform(image1.ActualWidth, image1.ActualHeight);

    Matrix3D m = centerImageAtOrigin * invertYAxis;
    m = m * rotateAboutY;
    m = m * translateAwayFromCamera;
    m = m * perspective;
    m = m * viewport;

    Matrix3DProjection m3dProjection = new Matrix3DProjection();
    m3dProjection.ProjectionMatrix = m;

    image1.Projection = m3dProjection;
}

private Matrix3D TranslationTransform(double tx, double ty, double tz)
{
    Matrix3D m = new Matrix3D();

    m.M11 = 1.0; m.M12 = 0.0; m.M13 = 0.0; m.M14 = 0.0;
    m.M21 = 0.0; m.M22 = 1.0; m.M23 = 0.0; m.M24 = 0.0;
    m.M31 = 0.0; m.M32 = 0.0; m.M33 = 1.0; m.M34 = 0.0;
    m.OffsetX = tx; m.OffsetY = ty; m.OffsetZ = tz; m.M44 = 1.0;

    return m;
}

private Matrix3D CreateScaleTransform(double sx, double sy, double sz)
{
    Matrix3D m = new Matrix3D();

    m.M11 = sx; m.M12 = 0.0; m.M13 = 0.0; m.M14 = 0.0;
    m.M21 = 0.0; m.M22 = sy; m.M23 = 0.0; m.M24 = 0.0;
    m.M31 = 0.0; m.M32 = 0.0; m.M33 = sz; m.M34 = 0.0;
    m.OffsetX = 0.0; m.OffsetY = 0.0; m.OffsetZ = 0.0; m.M44 = 1.0;

    return m;
}

private Matrix3D RotateYTransform(double theta)
{
    double sin = Math.Sin(theta);
    double cos = Math.Cos(theta);

    Matrix3D m = new Matrix3D();

    m.M11 = cos; m.M12 = 0.0; m.M13 = -sin; m.M14 = 0.0;
    m.M21 = 0.0; m.M22 = 1.0; m.M23 = 0.0; m.M24 = 0.0;
    m.M31 = sin; m.M32 = 0.0; m.M33 = cos; m.M34 = 0.0;
    m.OffsetX = 0.0; m.OffsetY = 0.0; m.OffsetZ = 0.0; m.M44 = 1.0;

    return m;
}

private Matrix3D RotateZTransform(double theta)
{
    double cos = Math.Cos(theta);
    double sin = Math.Sin(theta);

    Matrix3D m = new Matrix3D();
    m.M11 = cos; m.M12 = sin; m.M13 = 0.0; m.M14 = 0.0;
    m.M21 = -sin; m.M22 = cos; m.M23 = 0.0; m.M24 = 0.0;
    m.M31 = 0.0; m.M32 = 0.0; m.M33 = 1.0; m.M34 = 0.0;
    m.OffsetX = 0.0; m.OffsetY = 0.0; m.OffsetZ = 0.0; m.M44 = 1.0;
    return m;
}

private Matrix3D PerspectiveTransformFovRH(double fieldOfViewY, double aspectRatio, double zNearPlane, double zFarPlane)
{
    double height = 1.0 / Math.Tan(fieldOfViewY / 2.0);
    double width = height / aspectRatio;
    double d = zNearPlane - zFarPlane;

    Matrix3D m = new Matrix3D();
    m.M11 = width; m.M12 = 0; m.M13 = 0; m.M14 = 0;
    m.M21 = 0; m.M22 = height; m.M23 = 0; m.M24 = 0;
    m.M31 = 0; m.M32 = 0; m.M33 = zFarPlane / d; m.M34 = -1;
    m.OffsetX = 0; m.OffsetY = 0; m.OffsetZ = zNearPlane * zFarPlane / d; m.M44 = 0;

    return m;
}

private Matrix3D ViewportTransform(double width, double height)
{
    Matrix3D m = new Matrix3D();

    m.M11 = width / 2.0; m.M12 = 0.0; m.M13 = 0.0; m.M14 = 0.0;
    m.M21 = 0.0; m.M22 = -height / 2.0; m.M23 = 0.0; m.M24 = 0.0;
    m.M31 = 0.0; m.M32 = 0.0; m.M33 = 1.0; m.M34 = 0.0;
    m.OffsetX = width / 2.0; m.OffsetY = height / 2.0; m.OffsetZ = 0.0; m.M44 = 1.0;

    return m;
}

这将创建适当的视角转换并与 PowerPoint 生成的内容相匹配。

此代码改编自 MSDN

I think the problem everyone is encountering is that there needs to be a viewport shift along with the perspective transformation.

Try this out:

private void ApplyProjection()
{
    double fovY = FOV * Math.PI / 180 / 2.0;
    double translationZ = -image1.ActualHeight / Math.Tan(fovY / 2.0);
    double theta = YourAngleX * Math.PI / 180.0;

    Matrix3D centerImageAtOrigin = TranslationTransform(
             -image1.ActualWidth / 2.0,
             -image1.ActualHeight / 2.0, 0);
    Matrix3D invertYAxis = CreateScaleTransform(1.0, -1.0, 1.0);
    Matrix3D rotateAboutY = RotateYTransform(theta);
    Matrix3D translateAwayFromCamera = TranslationTransform(0, 0, translationZ);
    Matrix3D perspective = PerspectiveTransformFovRH(fovY,
            image1.ActualWidth / image1.ActualHeight,   // aspect ratio
            1.0,                                                // near plane
            1000.0);                                            // far plane
    Matrix3D viewport = ViewportTransform(image1.ActualWidth, image1.ActualHeight);

    Matrix3D m = centerImageAtOrigin * invertYAxis;
    m = m * rotateAboutY;
    m = m * translateAwayFromCamera;
    m = m * perspective;
    m = m * viewport;

    Matrix3DProjection m3dProjection = new Matrix3DProjection();
    m3dProjection.ProjectionMatrix = m;

    image1.Projection = m3dProjection;
}

private Matrix3D TranslationTransform(double tx, double ty, double tz)
{
    Matrix3D m = new Matrix3D();

    m.M11 = 1.0; m.M12 = 0.0; m.M13 = 0.0; m.M14 = 0.0;
    m.M21 = 0.0; m.M22 = 1.0; m.M23 = 0.0; m.M24 = 0.0;
    m.M31 = 0.0; m.M32 = 0.0; m.M33 = 1.0; m.M34 = 0.0;
    m.OffsetX = tx; m.OffsetY = ty; m.OffsetZ = tz; m.M44 = 1.0;

    return m;
}

private Matrix3D CreateScaleTransform(double sx, double sy, double sz)
{
    Matrix3D m = new Matrix3D();

    m.M11 = sx; m.M12 = 0.0; m.M13 = 0.0; m.M14 = 0.0;
    m.M21 = 0.0; m.M22 = sy; m.M23 = 0.0; m.M24 = 0.0;
    m.M31 = 0.0; m.M32 = 0.0; m.M33 = sz; m.M34 = 0.0;
    m.OffsetX = 0.0; m.OffsetY = 0.0; m.OffsetZ = 0.0; m.M44 = 1.0;

    return m;
}

private Matrix3D RotateYTransform(double theta)
{
    double sin = Math.Sin(theta);
    double cos = Math.Cos(theta);

    Matrix3D m = new Matrix3D();

    m.M11 = cos; m.M12 = 0.0; m.M13 = -sin; m.M14 = 0.0;
    m.M21 = 0.0; m.M22 = 1.0; m.M23 = 0.0; m.M24 = 0.0;
    m.M31 = sin; m.M32 = 0.0; m.M33 = cos; m.M34 = 0.0;
    m.OffsetX = 0.0; m.OffsetY = 0.0; m.OffsetZ = 0.0; m.M44 = 1.0;

    return m;
}

private Matrix3D RotateZTransform(double theta)
{
    double cos = Math.Cos(theta);
    double sin = Math.Sin(theta);

    Matrix3D m = new Matrix3D();
    m.M11 = cos; m.M12 = sin; m.M13 = 0.0; m.M14 = 0.0;
    m.M21 = -sin; m.M22 = cos; m.M23 = 0.0; m.M24 = 0.0;
    m.M31 = 0.0; m.M32 = 0.0; m.M33 = 1.0; m.M34 = 0.0;
    m.OffsetX = 0.0; m.OffsetY = 0.0; m.OffsetZ = 0.0; m.M44 = 1.0;
    return m;
}

private Matrix3D PerspectiveTransformFovRH(double fieldOfViewY, double aspectRatio, double zNearPlane, double zFarPlane)
{
    double height = 1.0 / Math.Tan(fieldOfViewY / 2.0);
    double width = height / aspectRatio;
    double d = zNearPlane - zFarPlane;

    Matrix3D m = new Matrix3D();
    m.M11 = width; m.M12 = 0; m.M13 = 0; m.M14 = 0;
    m.M21 = 0; m.M22 = height; m.M23 = 0; m.M24 = 0;
    m.M31 = 0; m.M32 = 0; m.M33 = zFarPlane / d; m.M34 = -1;
    m.OffsetX = 0; m.OffsetY = 0; m.OffsetZ = zNearPlane * zFarPlane / d; m.M44 = 0;

    return m;
}

private Matrix3D ViewportTransform(double width, double height)
{
    Matrix3D m = new Matrix3D();

    m.M11 = width / 2.0; m.M12 = 0.0; m.M13 = 0.0; m.M14 = 0.0;
    m.M21 = 0.0; m.M22 = -height / 2.0; m.M23 = 0.0; m.M24 = 0.0;
    m.M31 = 0.0; m.M32 = 0.0; m.M33 = 1.0; m.M34 = 0.0;
    m.OffsetX = width / 2.0; m.OffsetY = height / 2.0; m.OffsetZ = 0.0; m.M44 = 1.0;

    return m;
}

This will create the appropriate perspective shift and match what PowerPoint is producing.

This code was adapted from MSDN.

晨与橙与城 2024-09-17 02:30:57

经过大量的尝试后,我实际上同意“Ladislav Mrnka”的矩阵答案作为最简单的解决方案,并且对他们的答案进行了投票。

只需留下下面的示例即可为您提供一些可以使用的东西,但您需要通过 Matrix3DProjection 来更新它。

看起来您正在将源图片视为具有几种可能的视野之一,例如,就像使用 120° 的广角镜头或 30° 的变焦镜头拍摄的一样。然后,您尝试在显示时再现原始场景的纵横比。这是正确的吗?

如果是这样,您实际上希望在使用透视变换旋转图片之前水平拉伸图片以恢复隐式宽度。这意味着您实际上正在尝试解决两个独立的(更简单的)数学问题,例如:

  • 根据 FOV、长宽比和宽度计算图像的显示宽度。宽度(使用 X 缩放)。
  • 计算将给定宽度的透视变换拟合到所需显示宽度内所需的旋转(绕 Y 轴的投影旋转)。

我遇到的困难是示例照片没有表明有关显示的任何具体规则。显示宽度各不相同,因此我无法计算出您的最终结果是什么。如果您可以提供更多信息,我应该能够提供具体的计算。

好的,根据您对 PowerPoint 中透视设置的使用,所需的 2 个步骤确实是:

  • 缩放水平尺寸(根据您的“X”角度)
  • 应用投影变换来模拟 PowerPoint 中的透视角度

第一个计算非常简单简单的。您需要将比例设置为余弦(X 角度)。
第二个是估计值,因为 Powerpoint 透视角度似乎与旋转无关。

我在下面提供了完整的示例 XAML 和代码隐藏来生成所示的应用程序*。

alt text

***注意:存在一个严重缺陷,即投影变换无法将图像扭曲到一定程度你需要。我正在尝试 Matrix3DProjection,解决方案将遵循 **

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    x:Class="PerspectivePhotosTest.PerspectivePhotos"
    d:DesignWidth="640" d:DesignHeight="480">

    <Grid x:Name="LayoutRoot">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <Image x:Name="SampleImage" Height="101" Source="Image1.png" Stretch="Fill" VerticalAlignment="Center" HorizontalAlignment="Center" Width="128" RenderTransformOrigin="0.5,0.5">
                <Image.Projection>
                    <PlaneProjection x:Name="Rotation" RotationY="0"/>
                </Image.Projection>
                <Image.RenderTransform>
                    <CompositeTransform x:Name="Scale" ScaleX="1"/>
                </Image.RenderTransform>
            </Image>
            <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <TextBlock Text=""X-Angle"" VerticalAlignment="Top" HorizontalAlignment="Right"/>
                <TextBox x:Name="XAngleTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=XValueSlider}" TextChanged="XAngleTextBox_TextChanged"/>
                <Slider x:Name="XValueSlider" Grid.Row="1" Grid.ColumnSpan="2" LargeChange="10" Maximum="80" SmallChange="1"/>
                <TextBlock Text="Perspective Angle" VerticalAlignment="Top" Grid.Row="2" HorizontalAlignment="Right"/>
                <TextBox x:Name="PerspectiveAngleTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=PerspectiveSlider}" TextChanged="PerspectiveAngleTextBox_TextChanged"/>
                <Slider x:Name="PerspectiveSlider" Grid.Row="3" Grid.ColumnSpan="2" Maximum="120" SmallChange="1" LargeChange="10"/>
            </Grid>
        </StackPanel>
    </Grid>
</UserControl>

后面的代码:

using System;
using System.Windows.Controls;

namespace PerspectivePhotosTest
{
    public partial class PerspectivePhotos : UserControl
    {
        public PerspectivePhotos()
        {
            InitializeComponent();
        }

        private void XAngleTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            Scale.ScaleX = CalcScale(DegreeToRadian(double.Parse(XAngleTextBox.Text)));
        }

        private void PerspectiveAngleTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            Rotation.RotationY = CalcTransform(double.Parse(PerspectiveAngleTextBox.Text));
        }

        private double CalcScale(double angleInRadians)
        {
            return Math.Cos(angleInRadians) * 2 + 0.3;
        }

        private double CalcTransform(double angleInDegrees)
        {
            return angleInDegrees / 2;
        }

        private double DegreeToRadian(double angle)
        {
            return Math.PI * angle / 180.0;
        }
    }
}

这应该为您提供一个处理测试框架来尝试变化。我重新考虑了计算步骤,使其更加明显。

After lots of playing with this I actually concur with "Ladislav Mrnka"'s matrix answer as being the simplest solution and have up-voted their answer.

Just leaving the sample below to give you something to play with but you will need to update it via a Matrix3DProjection.

It looks like you are treating your source picture as having one of several possible fields of view, e.g. as if taken with a wide-angle lens for the 120° or a zoom lens for the 30°. You are then trying to reproduce the aspect ratio of the original scene when displayed. Is this correct?

If so the procedure you actually want to stretch the picture horizontally to restore the implicit width, before rotating it with the perspective transform. That would mean you are actually trying to solve 2 separate (simpler) maths problems here e.g.:

  • Calculate the display width of the image based on FOV, aspect ratio & width (using X-Scaling).
  • Calculate the rotation desired to fit a perspective transform of a given width within a desired display width (projection rotation about Y axis).

The difficulty I have is that the example photos do not indicate any specific rules about the display. The display widths all vary so I cannot work out what your end result is intended to be. If you can provide more information I should be able to provide specific calculations.

Ok, based on your use of the Perspective settings in PowerPoint, the 2 required steps are indeed:

  • Scale the horizontal size (according to your "X"-angle)
  • Apply a projection transform to emulate the perspective angle in PowerPoint

The first calculation is very simple. You need to set the scale to Cosine(X-angle).
The second is an estimate as the Powerpoint perspective angle does not seem to relate to rotation.

I have provided a full sample XAML and code-behind below to generate the app shown*.

alt text

***Note: there is a serious flaw in that Projection Transforms are not able to distort the image to the degree you require. I am trying Matrix3DProjection instead, solution will follow **

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    x:Class="PerspectivePhotosTest.PerspectivePhotos"
    d:DesignWidth="640" d:DesignHeight="480">

    <Grid x:Name="LayoutRoot">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <Image x:Name="SampleImage" Height="101" Source="Image1.png" Stretch="Fill" VerticalAlignment="Center" HorizontalAlignment="Center" Width="128" RenderTransformOrigin="0.5,0.5">
                <Image.Projection>
                    <PlaneProjection x:Name="Rotation" RotationY="0"/>
                </Image.Projection>
                <Image.RenderTransform>
                    <CompositeTransform x:Name="Scale" ScaleX="1"/>
                </Image.RenderTransform>
            </Image>
            <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <TextBlock Text=""X-Angle"" VerticalAlignment="Top" HorizontalAlignment="Right"/>
                <TextBox x:Name="XAngleTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=XValueSlider}" TextChanged="XAngleTextBox_TextChanged"/>
                <Slider x:Name="XValueSlider" Grid.Row="1" Grid.ColumnSpan="2" LargeChange="10" Maximum="80" SmallChange="1"/>
                <TextBlock Text="Perspective Angle" VerticalAlignment="Top" Grid.Row="2" HorizontalAlignment="Right"/>
                <TextBox x:Name="PerspectiveAngleTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=PerspectiveSlider}" TextChanged="PerspectiveAngleTextBox_TextChanged"/>
                <Slider x:Name="PerspectiveSlider" Grid.Row="3" Grid.ColumnSpan="2" Maximum="120" SmallChange="1" LargeChange="10"/>
            </Grid>
        </StackPanel>
    </Grid>
</UserControl>

Code behind:

using System;
using System.Windows.Controls;

namespace PerspectivePhotosTest
{
    public partial class PerspectivePhotos : UserControl
    {
        public PerspectivePhotos()
        {
            InitializeComponent();
        }

        private void XAngleTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            Scale.ScaleX = CalcScale(DegreeToRadian(double.Parse(XAngleTextBox.Text)));
        }

        private void PerspectiveAngleTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            Rotation.RotationY = CalcTransform(double.Parse(PerspectiveAngleTextBox.Text));
        }

        private double CalcScale(double angleInRadians)
        {
            return Math.Cos(angleInRadians) * 2 + 0.3;
        }

        private double CalcTransform(double angleInDegrees)
        {
            return angleInDegrees / 2;
        }

        private double DegreeToRadian(double angle)
        {
            return Math.PI * angle / 180.0;
        }
    }
}

This should give you a handle test framework to try out variations. I re-factored the calculation steps to make it more obvious.

一枫情书 2024-09-17 02:30:57

使用 Matrix3DProjection 并根据 此函数来自 Direct3D。您需要以弧度为单位的视场、屏幕的长宽比和两个剪辑距离(您正在定义有限平截头体)。如果您正在寻找进一步的解释为什么这样设置,您应该阅读一些有关计算机图形学的书。通常,投影变换矩阵仅设置视锥体。绕 X 轴旋转对象是通过单独的转换来执行的,但这是计算机图形学的一般做法,我不确定它在 Silverlight 中是否同样有效。

编辑:

如果您需要同时使用旋转和
单矩阵投影尝试使用
这个:

<前><代码>xScale 0 0 0
0 cos(X)*yScale -sin(X)*z*zf/(zf-zn) -sin(X)
0 sin(X)*yScale cox(X)*z*zf/(zf-zn) cos(X)
0 0 (-zn*zf)/(zf-zn) 0

其中 cos(X) 和 sin(X) 中的 X 是您的
绕 X 轴旋转(以弧度为单位)

z 是 Z 方向的平移
因为你将不得不移动
您的图像可以看到整个内容。

yScale = cot(FOV/2) FOV 位于
弧度

xScale = yScale/aspectRatio 宽高比
比率由高度和宽度定义
用于图像渲染的面板数量

zn = Z 附近 - 在此之前的所有内容
被剪裁。 zf = Z 远 - 一切
剪裁后。请注意,z
图像的坐标必须介于
那两个。

自从我上次这样做以来已经很久了
时间所以我希望我已经计算过
正确转换。矩阵
乘法应该是正确的,但是
我有机会乘以它
顺序错误。

编辑2:

我之前的建议不起作用。用于计算的第一个矩阵不正确,因为 Silverlight 使用转置版本。不使用第二个平移图像到中心和视口转换。我合并了艾莉森提出的代码(也可以找到此处)进行修改以拥有 FovX 和 HiTech Magic 的 Silverlight 应用程序。我以前从未编写过 Silverlight 应用程序...这是工作示例:

<Grid x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <Grid x:Name="Canvas" Background="White" Height="150" Width="200">
            <Image x:Name="SampleImage" Source="Penguins.jpg" Stretch="Fill" VerticalAlignment="Center" HorizontalAlignment="Center" Height="120" Width="160" RenderTransformOrigin="0.5,0.5" >
                <Image.Projection>
                    <Matrix3DProjection x:Name="Matrix" ProjectionMatrix="1, 0, 0, 0, 
                                                                      0, 1, 0, 0, 
                                                                      0, 0, 1, 0, 
                                                                      0, 0, 0, 1"/>
                </Image.Projection>
            </Image>
        </Grid>
        <Grid HorizontalAlignment="Center" VerticalAlignment="Top">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <TextBlock Text=""X-Angle"" VerticalAlignment="Top" HorizontalAlignment="Right"/>
            <TextBox x:Name="XAngleTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=XValueSlider}" TextChanged="XAngleTextBox_TextChanged"/>
            <Slider x:Name="XValueSlider" Grid.Row="1" Grid.ColumnSpan="2" LargeChange="10" Maximum="80" SmallChange="1"/>
            <TextBlock Text="Perspective Angle" VerticalAlignment="Top" Grid.Row="2" HorizontalAlignment="Right"/>
            <TextBox x:Name="PerspectiveAngleTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=PerspectiveSlider}" TextChanged="PerspectiveAngleTextBox_TextChanged" />
            <Slider x:Name="PerspectiveSlider" Grid.Row="3" Grid.ColumnSpan="2" Maximum="120" SmallChange="1" LargeChange="10" Value="60"/>
        </Grid>
    </StackPanel>
</Grid>



public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();
    }

    private void UpdateMatrix()
    {
        double val;


        double X = double.TryParse(XAngleTextBox.Text, NumberStyles.Any, 
            CultureInfo.InvariantCulture, out val) ? val : 0.0;
        double FOV = double.TryParse(PerspectiveAngleTextBox.Text, NumberStyles.Any, 
            CultureInfo.InvariantCulture, out val) ? val : 0.0;

        ApplyProjection(FOV, X);
    }

    private double DegreeToRadian(double angle)
    {
        return Math.PI * angle / 180.0;
    }

    private void XAngleTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
    {
        UpdateMatrix();
    }

    private void PerspectiveAngleTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
    {
        UpdateMatrix();
    }

    private void ApplyProjection(double FOV, double X)
    {
        // Translate the image along the negative Z-axis such that it occupies 50% of the
        // vertical field of view.
        double fov = DegreeToRadian(FOV);
        double translationZ = -SampleImage.Width / Math.Tan(fov / 2.0); 
        double theta = DegreeToRadian(X);

        // You can create a 3D effect by creating a number of simple 
        // tranformation Matrix3D matrixes and then multiply them together.
        Matrix3D centerImageAtOrigin = TranslationTransform(
                 -SampleImage.Width / 2.0,
                 -SampleImage.Height / 2.0, 0);
        Matrix3D invertYAxis = CreateScaleTransform(1.0, -1.0, 1.0);
        Matrix3D rotateAboutY = RotateYTransform(theta);
        Matrix3D translateAwayFromCamera = TranslationTransform(0, 0, translationZ);
        Matrix3D perspective = PerspectiveTransformFovRH(fov,
                Canvas.ActualWidth / Canvas.ActualHeight,   
                1.0,                                                // near plane
                1000.0);                                            // far plane
        Matrix3D viewport = ViewportTransform(Canvas.ActualWidth, Canvas.ActualHeight);

        Matrix3D m = centerImageAtOrigin * invertYAxis;
        m = m * rotateAboutY;
        m = m * translateAwayFromCamera;
        m = m * perspective;
        m = m * viewport;

        Matrix3DProjection m3dProjection = new Matrix3DProjection();
        m3dProjection.ProjectionMatrix = m;

        SampleImage.Projection = m3dProjection;
    }

    private Matrix3D TranslationTransform(double tx, double ty, double tz)
    {
        Matrix3D m = new Matrix3D();

        m.M11 = 1.0; m.M12 = 0.0; m.M13 = 0.0; m.M14 = 0.0;
        m.M21 = 0.0; m.M22 = 1.0; m.M23 = 0.0; m.M24 = 0.0;
        m.M31 = 0.0; m.M32 = 0.0; m.M33 = 1.0; m.M34 = 0.0;
        m.OffsetX = tx; m.OffsetY = ty; m.OffsetZ = tz; m.M44 = 1.0;

        return m;
    }

    private Matrix3D CreateScaleTransform(double sx, double sy, double sz)
    {
        Matrix3D m = new Matrix3D();

        m.M11 = sx; m.M12 = 0.0; m.M13 = 0.0; m.M14 = 0.0;
        m.M21 = 0.0; m.M22 = sy; m.M23 = 0.0; m.M24 = 0.0;
        m.M31 = 0.0; m.M32 = 0.0; m.M33 = sz; m.M34 = 0.0;
        m.OffsetX = 0.0; m.OffsetY = 0.0; m.OffsetZ = 0.0; m.M44 = 1.0;

        return m;
    }

    private Matrix3D RotateYTransform(double theta)
    {
        double sin = Math.Sin(theta);
        double cos = Math.Cos(theta);

        Matrix3D m = new Matrix3D();

        m.M11 = cos; m.M12 = 0.0; m.M13 = -sin; m.M14 = 0.0;
        m.M21 = 0.0; m.M22 = 1.0; m.M23 = 0.0; m.M24 = 0.0;
        m.M31 = sin; m.M32 = 0.0; m.M33 = cos; m.M34 = 0.0;
        m.OffsetX = 0.0; m.OffsetY = 0.0; m.OffsetZ = 0.0; m.M44 = 1.0;

        return m;
    }

    private Matrix3D RotateZTransform(double theta)
    {
        double cos = Math.Cos(theta);
        double sin = Math.Sin(theta);

        Matrix3D m = new Matrix3D();
        m.M11 = cos; m.M12 = sin; m.M13 = 0.0; m.M14 = 0.0;
        m.M21 = -sin; m.M22 = cos; m.M23 = 0.0; m.M24 = 0.0;
        m.M31 = 0.0; m.M32 = 0.0; m.M33 = 1.0; m.M34 = 0.0;
        m.OffsetX = 0.0; m.OffsetY = 0.0; m.OffsetZ = 0.0; m.M44 = 1.0;
        return m;
    }

    private Matrix3D PerspectiveTransformFovRH(double fieldOfView, double aspectRatio, double zNearPlane, double zFarPlane)
    {
        double width = 1.0 / Math.Tan(fieldOfView / 2.0);
        double height = width * aspectRatio;
        double d = zNearPlane - zFarPlane;

        Matrix3D m = new Matrix3D();
        m.M11 = width; m.M12 = 0; m.M13 = 0; m.M14 = 0;
        m.M21 = 0; m.M22 = height; m.M23 = 0; m.M24 = 0;
        m.M31 = 0; m.M32 = 0; m.M33 = zFarPlane / d; m.M34 = -1;
        m.OffsetX = 0; m.OffsetY = 0; m.OffsetZ = zNearPlane * zFarPlane / d; m.M44 = 0;

        return m;
    }

    private Matrix3D ViewportTransform(double width, double height)
    {
        Matrix3D m = new Matrix3D();

        m.M11 = width / 2.0; m.M12 = 0.0; m.M13 = 0.0; m.M14 = 0.0;
        m.M21 = 0.0; m.M22 = -height / 2.0; m.M23 = 0.0; m.M24 = 0.0;
        m.M31 = 0.0; m.M32 = 0.0; m.M33 = 1.0; m.M34 = 0.0;
        m.OffsetX = width / 2.0; m.OffsetY = height / 2.0; m.OffsetZ = 0.0; m.M44 = 1.0;

        return m;
    }

    private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
    {
        UpdateMatrix();
    }

}

Use Matrix3DProjection and set transformation based on matrix provided at the end of this function from Direct3D. You need your FOV in radians, Aspect ratio for screen and two distances for clipping (you are defining finite frustrum). If are you looking for futher explanation why it is set this way you should get some book about computer graphics. Also it is usual that matrix for projection transformation sets only view frustrum. Rotatin objects around X axis is performed by separate transformation but it is general practice from computer graphics and I'm not sure if it works same in Silverlight.

Edit:

If you need to use both rotation and
projection in single matrix try to use
this one:

xScale  0              0                     0
0       cos(X)*yScale  -sin(X)*z*zf/(zf-zn)  -sin(X)
0       sin(X)*yScale  cox(X)*z*zf/(zf-zn)   cos(X)
0       0              (-zn*zf)/(zf-zn)      0

Where X in cos(X) and sin(X) is your
rotation around X asis in radians

z is translation in Z direction
because you will have to move with
your image to see it whole.

yScale = cot(FOV/2) FOV is in
radians

xScale = yScale/aspectRatio Aspect
ratio is defined by height and width
of panel used for image rendering

zn = Z near - everything before this
is clipped. zf = Z far - evrything
after this is clipped. Be aware that z
coordinate of image has to be between
those two.

It is long time since I did this last
time so I hope I have computed
transformation correctly. Matrix
multiplication should be correct but
there is a chance that I multiplyed it
in wrong order.

Edit2:

My previous suggestion doesn't work. First matrices used for computation are incorrect because Silverlight uses transposed versions. Second translation image to center and viewport transformation is not used. I have combined Alison's proposed code (also can be found here) with modification to have FovX and HiTech Magic's Silverlight application. I have never written Silverlight app before ... Here is working sample:

<Grid x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <Grid x:Name="Canvas" Background="White" Height="150" Width="200">
            <Image x:Name="SampleImage" Source="Penguins.jpg" Stretch="Fill" VerticalAlignment="Center" HorizontalAlignment="Center" Height="120" Width="160" RenderTransformOrigin="0.5,0.5" >
                <Image.Projection>
                    <Matrix3DProjection x:Name="Matrix" ProjectionMatrix="1, 0, 0, 0, 
                                                                      0, 1, 0, 0, 
                                                                      0, 0, 1, 0, 
                                                                      0, 0, 0, 1"/>
                </Image.Projection>
            </Image>
        </Grid>
        <Grid HorizontalAlignment="Center" VerticalAlignment="Top">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <TextBlock Text=""X-Angle"" VerticalAlignment="Top" HorizontalAlignment="Right"/>
            <TextBox x:Name="XAngleTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=XValueSlider}" TextChanged="XAngleTextBox_TextChanged"/>
            <Slider x:Name="XValueSlider" Grid.Row="1" Grid.ColumnSpan="2" LargeChange="10" Maximum="80" SmallChange="1"/>
            <TextBlock Text="Perspective Angle" VerticalAlignment="Top" Grid.Row="2" HorizontalAlignment="Right"/>
            <TextBox x:Name="PerspectiveAngleTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=PerspectiveSlider}" TextChanged="PerspectiveAngleTextBox_TextChanged" />
            <Slider x:Name="PerspectiveSlider" Grid.Row="3" Grid.ColumnSpan="2" Maximum="120" SmallChange="1" LargeChange="10" Value="60"/>
        </Grid>
    </StackPanel>
</Grid>



public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();
    }

    private void UpdateMatrix()
    {
        double val;


        double X = double.TryParse(XAngleTextBox.Text, NumberStyles.Any, 
            CultureInfo.InvariantCulture, out val) ? val : 0.0;
        double FOV = double.TryParse(PerspectiveAngleTextBox.Text, NumberStyles.Any, 
            CultureInfo.InvariantCulture, out val) ? val : 0.0;

        ApplyProjection(FOV, X);
    }

    private double DegreeToRadian(double angle)
    {
        return Math.PI * angle / 180.0;
    }

    private void XAngleTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
    {
        UpdateMatrix();
    }

    private void PerspectiveAngleTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
    {
        UpdateMatrix();
    }

    private void ApplyProjection(double FOV, double X)
    {
        // Translate the image along the negative Z-axis such that it occupies 50% of the
        // vertical field of view.
        double fov = DegreeToRadian(FOV);
        double translationZ = -SampleImage.Width / Math.Tan(fov / 2.0); 
        double theta = DegreeToRadian(X);

        // You can create a 3D effect by creating a number of simple 
        // tranformation Matrix3D matrixes and then multiply them together.
        Matrix3D centerImageAtOrigin = TranslationTransform(
                 -SampleImage.Width / 2.0,
                 -SampleImage.Height / 2.0, 0);
        Matrix3D invertYAxis = CreateScaleTransform(1.0, -1.0, 1.0);
        Matrix3D rotateAboutY = RotateYTransform(theta);
        Matrix3D translateAwayFromCamera = TranslationTransform(0, 0, translationZ);
        Matrix3D perspective = PerspectiveTransformFovRH(fov,
                Canvas.ActualWidth / Canvas.ActualHeight,   
                1.0,                                                // near plane
                1000.0);                                            // far plane
        Matrix3D viewport = ViewportTransform(Canvas.ActualWidth, Canvas.ActualHeight);

        Matrix3D m = centerImageAtOrigin * invertYAxis;
        m = m * rotateAboutY;
        m = m * translateAwayFromCamera;
        m = m * perspective;
        m = m * viewport;

        Matrix3DProjection m3dProjection = new Matrix3DProjection();
        m3dProjection.ProjectionMatrix = m;

        SampleImage.Projection = m3dProjection;
    }

    private Matrix3D TranslationTransform(double tx, double ty, double tz)
    {
        Matrix3D m = new Matrix3D();

        m.M11 = 1.0; m.M12 = 0.0; m.M13 = 0.0; m.M14 = 0.0;
        m.M21 = 0.0; m.M22 = 1.0; m.M23 = 0.0; m.M24 = 0.0;
        m.M31 = 0.0; m.M32 = 0.0; m.M33 = 1.0; m.M34 = 0.0;
        m.OffsetX = tx; m.OffsetY = ty; m.OffsetZ = tz; m.M44 = 1.0;

        return m;
    }

    private Matrix3D CreateScaleTransform(double sx, double sy, double sz)
    {
        Matrix3D m = new Matrix3D();

        m.M11 = sx; m.M12 = 0.0; m.M13 = 0.0; m.M14 = 0.0;
        m.M21 = 0.0; m.M22 = sy; m.M23 = 0.0; m.M24 = 0.0;
        m.M31 = 0.0; m.M32 = 0.0; m.M33 = sz; m.M34 = 0.0;
        m.OffsetX = 0.0; m.OffsetY = 0.0; m.OffsetZ = 0.0; m.M44 = 1.0;

        return m;
    }

    private Matrix3D RotateYTransform(double theta)
    {
        double sin = Math.Sin(theta);
        double cos = Math.Cos(theta);

        Matrix3D m = new Matrix3D();

        m.M11 = cos; m.M12 = 0.0; m.M13 = -sin; m.M14 = 0.0;
        m.M21 = 0.0; m.M22 = 1.0; m.M23 = 0.0; m.M24 = 0.0;
        m.M31 = sin; m.M32 = 0.0; m.M33 = cos; m.M34 = 0.0;
        m.OffsetX = 0.0; m.OffsetY = 0.0; m.OffsetZ = 0.0; m.M44 = 1.0;

        return m;
    }

    private Matrix3D RotateZTransform(double theta)
    {
        double cos = Math.Cos(theta);
        double sin = Math.Sin(theta);

        Matrix3D m = new Matrix3D();
        m.M11 = cos; m.M12 = sin; m.M13 = 0.0; m.M14 = 0.0;
        m.M21 = -sin; m.M22 = cos; m.M23 = 0.0; m.M24 = 0.0;
        m.M31 = 0.0; m.M32 = 0.0; m.M33 = 1.0; m.M34 = 0.0;
        m.OffsetX = 0.0; m.OffsetY = 0.0; m.OffsetZ = 0.0; m.M44 = 1.0;
        return m;
    }

    private Matrix3D PerspectiveTransformFovRH(double fieldOfView, double aspectRatio, double zNearPlane, double zFarPlane)
    {
        double width = 1.0 / Math.Tan(fieldOfView / 2.0);
        double height = width * aspectRatio;
        double d = zNearPlane - zFarPlane;

        Matrix3D m = new Matrix3D();
        m.M11 = width; m.M12 = 0; m.M13 = 0; m.M14 = 0;
        m.M21 = 0; m.M22 = height; m.M23 = 0; m.M24 = 0;
        m.M31 = 0; m.M32 = 0; m.M33 = zFarPlane / d; m.M34 = -1;
        m.OffsetX = 0; m.OffsetY = 0; m.OffsetZ = zNearPlane * zFarPlane / d; m.M44 = 0;

        return m;
    }

    private Matrix3D ViewportTransform(double width, double height)
    {
        Matrix3D m = new Matrix3D();

        m.M11 = width / 2.0; m.M12 = 0.0; m.M13 = 0.0; m.M14 = 0.0;
        m.M21 = 0.0; m.M22 = -height / 2.0; m.M23 = 0.0; m.M24 = 0.0;
        m.M31 = 0.0; m.M32 = 0.0; m.M33 = 1.0; m.M34 = 0.0;
        m.OffsetX = width / 2.0; m.OffsetY = height / 2.0; m.OffsetZ = 0.0; m.M44 = 1.0;

        return m;
    }

    private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
    {
        UpdateMatrix();
    }

}
日裸衫吸 2024-09-17 02:30:57

似乎准确地解释了您的情况

This seems to explain exactly your scenario

魔法少女 2024-09-17 02:30:57

我的线性代数有点生疏,有信心帮助您,但是这篇文章 看起来这对您来说可能是一个很好的起点

My linear algebra is a little rusty be confident in helping, but this article looks like it could be a good starting place for you

圈圈圆圆圈圈 2024-09-17 02:30:57

好的。我已将 Ladislav Mrnka 的矩阵变换答案与我之前的示例应用程序结合起来,但他们的 3D 矩阵示例中似乎存在一些拼写错误,而且我对 3D 矩阵数学的了解不够强,无法纠正它。最终结果是图像应该是空白的显示:(

为了让这个聚会开始,我提供了一个带有 Xaml 的完整交互式测试应用程序(如下),以便 Ladislav Mrnka(或其他具有更好 3D 数学知识的人)可以纠正问题。

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <Image x:Name="SampleImage" Height="101" Source="Image1.png" Stretch="Fill" VerticalAlignment="Center" HorizontalAlignment="Center" Width="128" RenderTransformOrigin="0.5,0.5">
            <Image.Projection>
                <Matrix3DProjection x:Name="Matrix" ProjectionMatrix="1, 0, 0, 0,
                                                                      0, 1, 0, 0,
                                                                      0, 0, 1, 0,
                                                                      0, 0, 0, 1"/>
            </Image.Projection>
        </Image>
        <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <TextBlock Text=""X-Angle"" VerticalAlignment="Top" HorizontalAlignment="Right"/>
            <TextBox x:Name="XAngleTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=XValueSlider}" TextChanged="XAngleTextBox_TextChanged"/>
            <Slider x:Name="XValueSlider" Grid.Row="1" Grid.ColumnSpan="2" LargeChange="10" Maximum="80" SmallChange="1"/>
            <TextBlock Text="Perspective Angle" VerticalAlignment="Top" Grid.Row="2" HorizontalAlignment="Right"/>
            <TextBox x:Name="PerspectiveAngleTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=PerspectiveSlider}" TextChanged="PerspectiveAngleTextBox_TextChanged"/>
            <Slider x:Name="PerspectiveSlider" Grid.Row="3" Grid.ColumnSpan="2" Maximum="120" SmallChange="1" LargeChange="10"/>
            <TextBlock Text="Z-Near" VerticalAlignment="Top" Grid.Row="4" HorizontalAlignment="Right"/>
            <TextBox x:Name="ZNearTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=ZNearSlider}" TextChanged="ZNearTextBox_TextChanged"/>
            <Slider x:Name="ZNearSlider" Grid.Row="5" Grid.ColumnSpan="2" Minimum="-1000" Maximum="1000" SmallChange="1" LargeChange="10"/>
            <TextBlock Text="Z-Far" VerticalAlignment="Top" Grid.Row="6" HorizontalAlignment="Right"/>
            <TextBox x:Name="ZFarTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Row="6" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=ZFarSlider}" TextChanged="ZFarTextBox_TextChanged"/>
            <Slider x:Name="ZFarSlider" Grid.Row="7" Grid.ColumnSpan="2" Minimum="-1000" Maximum="1000" SmallChange="1" LargeChange="10"/>
        </Grid>
    </StackPanel>
</Grid>

隐藏代码:

using System;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace PerspectivePhotoTest
{
    public partial class PerspectivePhotosMatrix : UserControl
    {
        public PerspectivePhotosMatrix()
        {
            InitializeComponent();
        }

        private void UpdateMatrix()
        {
            double X = DegreeToRadian(double.Parse(XAngleTextBox.Text));
            double xScale = X;
            double FOV = DegreeToRadian(double.Parse(PerspectiveAngleTextBox.Text));
            double yScale = Math.Cos(FOV / 2) / Math.Sin(FOV / 2);
            double zn = double.Parse(ZNearTextBox.Text);
            double zf = double.Parse(ZFarTextBox.Text);
            double z = 0;

            Matrix.ProjectionMatrix = new Matrix3D(xScale, 0, 0, 0,
                0, Math.Cos(X) * yScale, -Math.Sin(X) * z * zf / (zf - zn), -Math.Sin(X),
                0, Math.Sin(X) * yScale, Math.Cos(X) * z * zf / (zf - zn), Math.Cos(X),
                0,0,(-zn*zf)/(zf-zn), 0);
        }

        private double DegreeToRadian(double angle)
        {
            return Math.PI * angle / 180.0;
        }

        private void XAngleTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            UpdateMatrix();
        }

        private void PerspectiveAngleTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            UpdateMatrix();
        }

        private void ZNearTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            UpdateMatrix();
        }

        private void ZFarTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            UpdateMatrix();
        }
    }
}

Okay. I have combined Ladislav Mrnka's Matrix transform answer with my previous sample app, but there appear to have been some typos in their 3D Matrix example and I am not strong enough with 3D Matrix math to correct it. The end result is a blank display where the image should be :(

Instead to get this party started I have provide a full interactive test app (below) with Xaml so that Ladislav Mrnka (or someone else with better 3D maths) can correct the problem.

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <Image x:Name="SampleImage" Height="101" Source="Image1.png" Stretch="Fill" VerticalAlignment="Center" HorizontalAlignment="Center" Width="128" RenderTransformOrigin="0.5,0.5">
            <Image.Projection>
                <Matrix3DProjection x:Name="Matrix" ProjectionMatrix="1, 0, 0, 0,
                                                                      0, 1, 0, 0,
                                                                      0, 0, 1, 0,
                                                                      0, 0, 0, 1"/>
            </Image.Projection>
        </Image>
        <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <TextBlock Text=""X-Angle"" VerticalAlignment="Top" HorizontalAlignment="Right"/>
            <TextBox x:Name="XAngleTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=XValueSlider}" TextChanged="XAngleTextBox_TextChanged"/>
            <Slider x:Name="XValueSlider" Grid.Row="1" Grid.ColumnSpan="2" LargeChange="10" Maximum="80" SmallChange="1"/>
            <TextBlock Text="Perspective Angle" VerticalAlignment="Top" Grid.Row="2" HorizontalAlignment="Right"/>
            <TextBox x:Name="PerspectiveAngleTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=PerspectiveSlider}" TextChanged="PerspectiveAngleTextBox_TextChanged"/>
            <Slider x:Name="PerspectiveSlider" Grid.Row="3" Grid.ColumnSpan="2" Maximum="120" SmallChange="1" LargeChange="10"/>
            <TextBlock Text="Z-Near" VerticalAlignment="Top" Grid.Row="4" HorizontalAlignment="Right"/>
            <TextBox x:Name="ZNearTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=ZNearSlider}" TextChanged="ZNearTextBox_TextChanged"/>
            <Slider x:Name="ZNearSlider" Grid.Row="5" Grid.ColumnSpan="2" Minimum="-1000" Maximum="1000" SmallChange="1" LargeChange="10"/>
            <TextBlock Text="Z-Far" VerticalAlignment="Top" Grid.Row="6" HorizontalAlignment="Right"/>
            <TextBox x:Name="ZFarTextBox" d:LayoutOverrides="Height" Grid.ColumnSpan="2" Grid.Row="6" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="40" Text="{Binding Value, ElementName=ZFarSlider}" TextChanged="ZFarTextBox_TextChanged"/>
            <Slider x:Name="ZFarSlider" Grid.Row="7" Grid.ColumnSpan="2" Minimum="-1000" Maximum="1000" SmallChange="1" LargeChange="10"/>
        </Grid>
    </StackPanel>
</Grid>

Code-behind:

using System;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace PerspectivePhotoTest
{
    public partial class PerspectivePhotosMatrix : UserControl
    {
        public PerspectivePhotosMatrix()
        {
            InitializeComponent();
        }

        private void UpdateMatrix()
        {
            double X = DegreeToRadian(double.Parse(XAngleTextBox.Text));
            double xScale = X;
            double FOV = DegreeToRadian(double.Parse(PerspectiveAngleTextBox.Text));
            double yScale = Math.Cos(FOV / 2) / Math.Sin(FOV / 2);
            double zn = double.Parse(ZNearTextBox.Text);
            double zf = double.Parse(ZFarTextBox.Text);
            double z = 0;

            Matrix.ProjectionMatrix = new Matrix3D(xScale, 0, 0, 0,
                0, Math.Cos(X) * yScale, -Math.Sin(X) * z * zf / (zf - zn), -Math.Sin(X),
                0, Math.Sin(X) * yScale, Math.Cos(X) * z * zf / (zf - zn), Math.Cos(X),
                0,0,(-zn*zf)/(zf-zn), 0);
        }

        private double DegreeToRadian(double angle)
        {
            return Math.PI * angle / 180.0;
        }

        private void XAngleTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            UpdateMatrix();
        }

        private void PerspectiveAngleTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            UpdateMatrix();
        }

        private void ZNearTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            UpdateMatrix();
        }

        private void ZFarTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            UpdateMatrix();
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文