Как установить этот вид преобразования перспективы в Matrix3D? - PullRequest
8 голосов
/ 15 июля 2010

У меня есть изображение с несколькими значениями, чтобы сделать его перспективным в Silverlight, но я не могу понять, что мне нужно сделать математически, чтобы это произошло. Самое главное, у меня есть угол, называемый «полем зрения» ( FOV ).

Это нормальная картина: normal

Например:

X =   <b>30°</b>             X =   <b>30°</b>             X   =  <b>30°</b>
FOV = <b>30°</b>             FOV = <b>60°</b>             FOV = <b>120°</b>
<img src="https://lh5.ggpht.com/_vm4C26HAULw/TD4loFCiFmI/AAAAAAAAAIw/zieJBjwmYJk/s128/x30perspective30.jpg"/>    <img src="https://lh5.ggpht.com/_vm4C26HAULw/TD4lov80VpI/AAAAAAAAAI0/Df55ts6KIGo/s128/x30perspective60.jpg"/>    <img src="https://lh4.ggpht.com/_vm4C26HAULw/TD4lo5UWiPI/AAAAAAAAAI4/mDDroMB0o60/s128/x30perspective120.jpg"/>

X =   <b>60°</b>             X =   <b>60°</b>               X =  <b>60°</b>
FOV = <b>30°</b>             FOV = <b>60°</b>             FOV = <b>120°</b>
<img src="https://lh4.ggpht.com/_vm4C26HAULw/TD4lnwn6HuI/AAAAAAAAAIs/uQjFPQnk3t0/s128/x60perspective30.jpg"/>           <img src="https://lh6.ggpht.com/_vm4C26HAULw/TD4lnGzPaDI/AAAAAAAAAIk/fEoVp55Wc0c/s128/x60perspective60.jpg"/>            <img src="https://lh4.ggpht.com/_vm4C26HAULw/TD4lniWrzfI/AAAAAAAAAIo/tJtim0gs7rs/s128/x60perspective120.jpg"/>



Буду признателен за любую помощь, чтобы провести меня по математике, чтобы воспроизвести их в Silverlight.

Ответы [ 7 ]

6 голосов
/ 24 августа 2010

Я думаю, что проблема, с которой сталкиваются все, заключается в том, что должен быть сдвиг области просмотра вместе с трансформацией перспективы.

Попробуйте:

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 .

3 голосов
/ 22 августа 2010

После долгих игр с этим я на самом деле согласен с матричным ответом «Ладислава Мрнки» как с самым простым решением и проголосовал за его ответ.

Просто оставив образец ниже, чтобы дать вам кое-чтопоиграйте с ним, но вам нужно будет обновить его с помощью Matrix3DProjection.

Похоже, вы рассматриваете исходное изображение как имеющее одно из нескольких возможных полей обзора, например, как если бы оно было снято с широкоугольным объективом для120 ° или зум-объектив для 30 °.Затем вы пытаетесь воспроизвести соотношение сторон оригинальной сцены при отображении.Это правильно?

Если это так, вы действительно хотите растянуть изображение по горизонтали, чтобы восстановить неявную ширину, прежде чем поворачивать его с перспективным преобразованием.Это означало бы, что вы на самом деле пытаетесь решить 2 отдельные (более простые) математические задачи, например:

  • Рассчитайте ширину отображения изображения на основе FOV, соотношения сторон и ширины (используя X-Scaling).
  • Рассчитать поворот, который требуется, чтобы соответствовать перспективному преобразованию заданной ширины в пределах желаемой ширины дисплея (вращение проекции вокруг оси 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="&quot;X-Angle&quot;" 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;
        }
    }
}

Это должно дать вам ручную тестовую среду для тестирования вариантов.Я пересмотрел шаги расчета, чтобы сделать его более очевидным.

2 голосов
/ 22 августа 2010

Используйте Matrix3DProjection и установите преобразование на основе матрицы, предоставленной в конце этой функции из Direct3D. Вам нужно ваше FOV в радианах, соотношение сторон для экрана и два расстояния для отсечения (вы определяете конечную усеченную дробь). Если вы ищете дальнейшее объяснение, почему это так, вы должны получить книгу о компьютерной графике. Также обычно, что матрица для проекционного преобразования устанавливает только видимость. Поворотные объекты вокруг оси X выполняются путем отдельного преобразования, но это обычная практика для компьютерной графики, и я не уверен, работает ли он так же в Silverlight.

Edit:

Если вам нужно использовать как вращение, так и проекцию в одной матрице попробуйте использовать вот этот:

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

Где X в cos (X) и sin (X) - это ваш вращение вокруг оси X в радианах

z - перевод в направлении Z потому что вам придется двигаться с Ваше изображение, чтобы увидеть его целиком.

yScale = cot (FOV / 2) FOV находится в радиан

xScale = yScale / aspectRatio Aspect соотношение определяется высотой и шириной панели, используемой для рендеринга изображений

zn = Z рядом - все до этого обрезается. ZF = Z далеко - все после этого обрезается. Знать, что г координата изображения должна быть между те два.

Давно я не делал этого в последний раз время, поэтому я надеюсь, что я рассчитал преобразование правильно. матрица умножение должно быть правильным, но есть вероятность, что я умножил это в неправильном порядке.

Edit2:

Мое предыдущее предложение не работает. Первые матрицы, используемые для вычислений, неверны, потому что Silverlight использует транспонированные версии. Второе преобразование изображения в центр и преобразование области просмотра не используется. Я объединил предложенный Элисон код (также можно найти здесь ) с модификацией, чтобы иметь приложение Silverlight для FovX и HiTech Magic. Я никогда раньше не писал приложение 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="&quot;X-Angle&quot;" 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();
    }

}
2 голосов
/ 22 августа 2010

Это , кажется, точно объясняет ваш сценарий

2 голосов
/ 15 июля 2010

см. это и это статья. Поиск "перспектива" на странице.

1 голос
/ 24 августа 2010

Хорошо.Я совместил ответ преобразования матрицы Матрицы Ладислава Мрнки с моим предыдущим примером приложения, но, похоже, в их примере с 3D-матрицей были некоторые опечатки, и я не достаточно силен в математике с 3D-матрицей, чтобы исправить это.Конечным результатом является пустой экран, где изображение должно быть :(

Вместо этого, чтобы начать эту вечеринку, я предоставил полное интерактивное тестовое приложение (ниже) с Xaml, чтобы Ладислав Мрнка (или кто-то еще с лучшим 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="&quot;X-Angle&quot;" 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();
        }
    }
}
1 голос
/ 15 июля 2010

Моя линейная алгебра немного заржавела, будьте уверены, что помогаете, но эта статья выглядит так, как будто она может быть хорошей отправной точкой для вас

...