解決WPF Viewport3D透視模式時窗體模糊。
最近折騰Viewport3D玩,遇到了一些詭異的問題,研究一下略有心得,特此和大家分享~
三維圖形概述:
https://msdn.microsoft.com/zh-cn/library/ms747437.aspx概要
三維坐標系
二維圖形的 WPF 坐標系將原點定位在呈現區域(通常是屏幕)的左上角。 在二維繫統中,x 軸上的正值朝右,y 軸上的正值朝下。 但是,在三維坐標系中,原點位於呈現區域的中心,x 軸上的正值朝右,但是 y 軸上的正值朝上,z 軸上的正值從原點向外朝向觀察者。
照相機
透視投影和正投影
關於TextureCoordinates(紋理坐標):
<MeshGeometry3D Positions="-250,-150,0 250,-150,0 250,150,0 -250,150,0" TextureCoordinates="0,1 1,1 1,0 0,0" TriangleIndices="0,1,2 0,2,3" />
上面這段MeshGeometry3D(用於生成3-D形狀的三角形基元)對照圖2三維坐標系會發現是個從左下角逆時針繪製的矩形。如果要將一個窗體UserControl紋理應用到Viewport2DVisual3D上,我們就需要合理的設置TextureCoordinates(紋理坐標)。對照圖1二維坐標系,紋理坐標同樣是由左下開始繪製的矩形。這樣設置,紋理就可以顯示正常了。 關於視角和距離的關係: 含義:
點 | 說明 |
A | 視點 |
線 | 說明 |
BC | 查看的目標寬度 |
bc | 展示的視窗寬度 |
AD | 視點到查看目標的距離 |
ad | 視點到視窗的距離 |
角度 | 說明 |
cAb | 視角(FieldOfView) |
<Window x:Class="App3DWindow.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:app3DWindow="clr-namespace:App3DWindow" xmlns:asyncDelegate="clr-namespace:AsyncDelegate" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Title="MainWindow" Width="500" Height="310" AllowsTransparency="True" Background="Transparent" Loaded="MainWindow_OnLoaded" WindowStyle="None" WindowStartupLocation="CenterScreen" mc:Ignorable="d"> <Window.Resources> <Storyboard x:Key="Turn2ConfigViewStoryboard"> <Rotation3DAnimationUsingKeyFrames Storyboard.TargetName="containerUIElement3D" Storyboard.TargetProperty="(Visual3D.Transform).(Transform3DGroup.Children)[2].(RotateTransform3D.Rotation)"> <EasingRotation3DKeyFrame KeyTime="0:0:2"> <EasingRotation3DKeyFrame.EasingFunction> <QuinticEase EasingMode="EaseOut" /> </EasingRotation3DKeyFrame.EasingFunction> <EasingRotation3DKeyFrame.Value> <AxisAngleRotation3D Angle="180" Axis="0,1,0" /> </EasingRotation3DKeyFrame.Value> </EasingRotation3DKeyFrame> </Rotation3DAnimationUsingKeyFrames> </Storyboard> <Storyboard x:Key="Turn2DownloadViewStoryboard"> <Rotation3DAnimationUsingKeyFrames Storyboard.TargetName="containerUIElement3D" Storyboard.TargetProperty="(Visual3D.Transform).(Transform3DGroup.Children)[2].(RotateTransform3D.Rotation)"> <EasingRotation3DKeyFrame KeyTime="0:0:2"> <EasingRotation3DKeyFrame.EasingFunction> <QuinticEase EasingMode="EaseOut" /> </EasingRotation3DKeyFrame.EasingFunction> <EasingRotation3DKeyFrame.Value> <AxisAngleRotation3D Angle="360" Axis="0,1,0" /> </EasingRotation3DKeyFrame.Value> </EasingRotation3DKeyFrame> </Rotation3DAnimationUsingKeyFrames> </Storyboard> <!-- 相機 --> <PerspectiveCamera x:Key="PerspectiveCamera" FieldOfView="90" LookDirection="0,0,-1" NearPlaneDistance="1" Position="0,0,250" UpDirection="0,1,0" /> <OrthographicCamera x:Key="OrthographicCamera" Width="500" LookDirection="0,0,-1" Position="0,0,250" UpDirection="0,1,0" /> </Window.Resources> <Grid Background="Transparent"> <Viewport3D Margin="0"> <Viewport3D.Camera> <!-- 相機 --> <PerspectiveCamera x:Name="PerspectiveCamera" FieldOfView="90" LookDirection="0,0,-1" NearPlaneDistance="1" Position="0,0,250" UpDirection="0,1,0" /> </Viewport3D.Camera> <Viewport3D.Children> <ContainerUIElement3D x:Name="containerUIElement3D"> <ContainerUIElement3D.Transform> <Transform3DGroup> <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0" /> <ScaleTransform3D ScaleX="1" ScaleY="1" ScaleZ="1" /> <RotateTransform3D d:EulerAngles="0,0,0"> <RotateTransform3D.Rotation> <AxisAngleRotation3D Angle="0" Axis="0,1,0" /> </RotateTransform3D.Rotation> </RotateTransform3D> <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0" /> <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0" /> </Transform3DGroup> </ContainerUIElement3D.Transform> <!-- 正面視角 --> <Viewport2DVisual3D x:Name="FrontViewport2DVisual3D"> <Viewport2DVisual3D.Transform> <Transform3DGroup> <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0" /> <ScaleTransform3D ScaleX="1" ScaleY="1" ScaleZ="1" /> <RotateTransform3D d:EulerAngles="0,0,0"> <RotateTransform3D.Rotation> <AxisAngleRotation3D Angle="0" Axis="0,0,0" /> </RotateTransform3D.Rotation> </RotateTransform3D> <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0" /> <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0" /> </Transform3DGroup> </Viewport2DVisual3D.Transform> <Viewport2DVisual3D.Geometry> <MeshGeometry3D Positions="-250,-150,0 250,-150,0 250,150,0 -250,150,0" TextureCoordinates="0,1 1,1 1,0 0,0" TriangleIndices="0,1,2 0,2,3" /> </Viewport2DVisual3D.Geometry> <Viewport2DVisual3D.Material> <DiffuseMaterial Viewport2DVisual3D.IsVisualHostMaterial="True" /> </Viewport2DVisual3D.Material> <Viewport2DVisual3D.Visual> <app3DWindow:DownloadWindow x:Name="DownloadWindowPart" Width="500" Height="300" Background="White" SnapsToDevicePixels="True" /> </Viewport2DVisual3D.Visual> </Viewport2DVisual3D> <!-- 背面視角 --> <Viewport2DVisual3D x:Name="BackViewport2DVisual3D"> <Viewport2DVisual3D.Transform> <Transform3DGroup> <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0" /> <ScaleTransform3D ScaleX="1" ScaleY="1" ScaleZ="1" /> <RotateTransform3D d:EulerAngles="0,0,0"> <RotateTransform3D.Rotation> <AxisAngleRotation3D Angle="0" Axis="0,0,0" /> </RotateTransform3D.Rotation> </RotateTransform3D> <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0" /> <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0" /> </Transform3DGroup> </Viewport2DVisual3D.Transform> <Viewport2DVisual3D.Geometry> <MeshGeometry3D Positions="-250,-150,0 250,-150,0 250,150,0 -250,150,0" TextureCoordinates="1,1 0,1 0,0 1,0 " TriangleIndices="0,3,2 0,2,1" /> </Viewport2DVisual3D.Geometry> <Viewport2DVisual3D.Material> <DiffuseMaterial Viewport2DVisual3D.IsVisualHostMaterial="True" /> </Viewport2DVisual3D.Material> <Viewport2DVisual3D.Visual> <app3DWindow:ProxyConfigView x:Name="ProxyConfigViewPart" Width="500" Height="300" Background="White" SnapsToDevicePixels="True" /> </Viewport2DVisual3D.Visual> </Viewport2DVisual3D> </ContainerUIElement3D> <ModelVisual3D> <ModelVisual3D.Content> <!-- 光源 --> <AmbientLight x:Name="ViewLight" /> </ModelVisual3D.Content> </ModelVisual3D> </Viewport3D.Children> </Viewport3D> <Grid x:Name="DisplayGrid"> <Grid.RowDefinitions> <RowDefinition Height="40" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <UserControl x:Name="DisPlayControl" Grid.Row="0" Grid.RowSpan="2" /> <!--<Border x:Name="Part_Drag" Grid.Row="0" Background="AliceBlue" PreviewMouseLeftButtonDown="DisPlayControl_OnPreviewMouseLeftButtonDown" />--> </Grid> </Grid> </Window>
using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media.Animation; namespace App3DWindow { /// <summary> /// MainWindow.xaml 的交互邏輯 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } /// <summary> /// 載入時進行模糊情況特殊處理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) { //return; SetDownloadWindowDisplayMode(); var turn2ConfigViewStoryboard = this.TryFindResource("Turn2ConfigViewStoryboard") as Storyboard; if (turn2ConfigViewStoryboard != null) { turn2ConfigViewStoryboard.Completed += (s, e1) => {//動畫執行完成切換展示模式 SetConfigViewDisplayMode(); SetDownloadWindowTurningMode(); }; turn2ConfigViewStoryboard.CurrentTimeInvalidated += (s, e1) => {//動畫執行時切換成翻轉模式 SetDownloadWindowTurningMode(); }; }; var turn2DownloadViewStoryboard = this.TryFindResource("Turn2DownloadViewStoryboard") as Storyboard; if (turn2DownloadViewStoryboard != null) { turn2DownloadViewStoryboard.Completed += (s, e1) => {//動畫執行完成切換展示模式 SetDownloadWindowDisplayMode(); SetConfigViewTurningMode(); }; turn2DownloadViewStoryboard.CurrentTimeInvalidated += (s, e1) => {//動畫執行時切換成翻轉模式 SetConfigViewTurningMode(); }; }; } /// <summary> /// 設置下載頁面進入展示模式 /// </summary> private void SetDownloadWindowDisplayMode() { //var container = DownloadWindowPart.Parent as Viewport2DVisual3D; //if (container == null) return; //container.Visual = null; if (FrontViewport2DVisual3D.Visual==(DownloadWindowPart)) { FrontViewport2DVisual3D.Visual = null; } DisPlayControl.Content = DownloadWindowPart; } /// <summary> /// 設置配置頁面進入展示模式 /// </summary> private void SetConfigViewDisplayMode() { //var container = ProxyConfigViewPart.Parent as Viewport2DVisual3D; //if (container == null) return; //container.Visual = null; if (BackViewport2DVisual3D.Visual==(ProxyConfigViewPart)) { BackViewport2DVisual3D.Visual = null; } DisPlayControl.Content = ProxyConfigViewPart; } /// <summary> /// 設置下載頁面進入翻轉模式 /// </summary> private void SetDownloadWindowTurningMode() { var container = DownloadWindowPart.Parent as UserControl; if (container == null) return; container.Content = null; FrontViewport2DVisual3D.Visual = DownloadWindowPart; } /// <summary> /// 設置配置頁面進入翻轉模式 /// </summary> private void SetConfigViewTurningMode() { var container = ProxyConfigViewPart.Parent as UserControl; if (container == null) return; container.Content = null; BackViewport2DVisual3D.Visual = ProxyConfigViewPart; } /// <summary> /// 支持拖拽 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void DisPlayControl_OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { this.DragMove(); } } }
呈現效果: 效果見下圖,翻轉動畫執行完畢後不再模糊。