Как Установить этот Вид Перспективы, Преобразовывают в Matrix3D?

Я имею изображение с и имею несколько значений для создания его перспективой в Silverlight, но не могу вполне выяснить то, что я должен сделать mathmatically, чтобы заставить его произойти. Самая важная вещь, у меня есть угол, названный "Полем зрения" (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.

8
задан Community 8 February 2017 в 14:28
поделиться

7 ответов

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

Попробуйте это:

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.

6
ответ дан 5 December 2019 в 09:23
поделиться

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

2
ответ дан 5 December 2019 в 09:23
поделиться

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

1
ответ дан 5 December 2019 в 09:23
поделиться

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

Просто оставьте пример ниже, чтобы вы могли поиграть, но вам нужно будет обновить его с помощью 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="&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;
        }
    }
}

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

3
ответ дан 5 December 2019 в 09:23
поделиться

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

Редактировать:

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

 xScale 0 0 0
0 cos (X) * yScale -sin (X) * z * zf / (zf-zn) -sin (X)
0 sin (X) * y Масштабирование cox (X) * z * zf / (zf-zn) cos (X)
0 0 (-zn * zf) / (zf-zn) 0

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

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

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

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

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

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

Edit2:

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

}
3
ответ дан 5 December 2019 в 09:23
поделиться

Хорошо. Я объединил ответ Ладислава Мрнки на преобразование матрицы с моим предыдущим примером приложения, но, похоже, в их примере с 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
ответ дан 5 December 2019 в 09:23
поделиться

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

.
2
ответ дан 5 December 2019 в 09:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: