Я имею изображение с и имею несколько значений для создания его перспективой в Silverlight, но не могу вполне выяснить то, что я должен сделать mathmatically, чтобы заставить его произойти. Самая важная вещь, у меня есть угол, названный "Полем зрения" (FOV).
Это - нормальное изображение:
Например:
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.
Я думаю, что проблема, с которой сталкиваются все, заключается в том, что вместе с преобразованием перспективы необходимо смещение окна просмотра.
Попробуйте это:
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.
Моя линейная алгебра немного заржавела, будьте уверены в помощи, но эта статья выглядит так, как будто она может стать для вас хорошей отправной точкой
Похоже, вы обрабатываете исходное изображение как имеющее одно из нескольких возможных полей зрения, например как будто снято с широкоугольным объективом на 120 ° или зум-объективом на 30 °.Затем вы пытаетесь воспроизвести соотношение сторон исходной сцены при отображении. Это правильно?
Если это так, то в процедуре вы действительно хотите растянуть изображение по горизонтали, чтобы восстановить неявную ширину, прежде чем повернуть его с помощью перспективного преобразования. Это означало бы, что вы на самом деле пытаетесь решить здесь две отдельные (более простые) математические задачи, например: [
Моя трудность заключается в том, что в примерах фотографий не указаны какие-либо конкретные правила в отношении дисплея. Ширина дисплея различается, поэтому я не могу понять, каким должен быть ваш конечный результат. Если вы можете предоставить дополнительную информацию, я смогу предоставить конкретные расчеты.
Хорошо, в зависимости от того, как вы используете настройки перспективы в PowerPoint, 2 необходимых шага действительно таковы:
Первый расчет очень прост. Вам нужно установить масштаб на косинус (угол X). Второе - это оценка, поскольку угол перспективы Powerpoint, похоже, не связан с поворотом.
Я предоставил полный образец XAML и кода программной части ниже для создания показанного приложения *.
*** Примечание: существует серьезный недостаток, заключающийся в том, что функция «Преобразование проекции» не может искажать изображение до требуемой степени.Вместо этого я пробую 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;
}
}
}
Это должно дать вам основу для тестирования дескрипторов, чтобы опробовать варианты. Я переработал этапы расчета, чтобы сделать его более очевидным.
Используйте 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=""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();
}
}
Хорошо. Я объединил ответ Ладислава Мрнки на преобразование матрицы с моим предыдущим примером приложения, но, похоже, в их примере с 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=""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();
}
}
}