CSS エフェクトのブログアドレス:
3D シューティング効果?CSS で簡単に実現 - ChokCoco - 博客园 (cnblogs.com)
UWP エフェクトのブログアドレス
3D シューティング効果?UWP でも実現可能 - dino.c - 博客园 (cnblogs.com)
皆さん、本当に達人ですね。
本当にたくさん学べることがあります。
私も便乗して、WPF で試してみました。効果はあまり良くありませんが、まあまあだと思います。

CSS や UWP に比べて、老舗の WPF は 3D 効果の操作が比較的煩雑です。まず 3D モデルを作成し、次に XY 軸を回転させ、Z 軸の伸縮を調整し、最後に FOV を調整します。シューティング効果はこの Z 軸の伸縮の数値で決まり、図では 10-20 です。
もちろん一番難しいのはアニメーションの最初と最後のつなぎです。
順を追ってやっていきましょう。
3D モデルの作成
この部分は 2D コントロールを使わずにそのまま 3D モデルにできるので、特に研究することはありません。画像をテクスチャとして貼り付けます。
3D モデルの作成についてまとめると、左から右へ進めることです。
- Positions
3D モデルは三角形で構成されるため、物体の形状を表すときはできるだけ三角形を描きます。各点が頂点で、同じ頂点が存在する場合は一つを選びます。
頂点を記述するときは XYZ で、物体を正面から見たとき、物体の Z 軸は自分に向かう方向が正方向です。Y 軸は上が正、X 軸は右が正です。

ここで注意すべき点は、便宜上、左、左下、右下、右上の順で点を定義することを推奨します。

このモデルの頂点定義は P1(x,y,z), P2(x,y,z), P3(x,y,z), P4(x,y,z) です。
- TriangleIndices
これは、これらの頂点がどのように三角形を構成するかを記述します。ある面を表面とみなす場合、頂点集合のインデックスを反時計回りに定義することを忘れないでください。頂点集合は Positions で、入力した座標点がその内容で、インデックスは 0 から始まります。
裏面は時計回りです。
- TextureCoordinates
これはマテリアルの位置指定順序で、これは 2D 座標系で他のものとは異なります。左上が 0,0、右下が 1,1、つまり通常の画面座標系です。
モデルを作成したら、4 つ作成する必要があります。

3D モデルの回転
これは少し面白いです。上記でモデルを 4 つ作成しましたが、4 つの面を構成するには、Y 軸回転、X 軸回転を行いますが、方向は異なります。回転には Transform を使用します。

まあまあに見えます。
4 つの画像を作成して試してみましょう。
デバッグしやすいように、プログレスバーを付けます。
リソース
<MeshGeometry3D x:Key="Rect3D_O">
<MeshGeometry3D.Positions>
-50, 50, 0, -50, -50, 0, 50, -50, 0, 50, 50, 0
</MeshGeometry3D.Positions>
<MeshGeometry3D.TriangleIndices> 0 1 2 2 3 0 </MeshGeometry3D.TriangleIndices>
<MeshGeometry3D.TextureCoordinates>
0,0 0,1 1,1 1,0
</MeshGeometry3D.TextureCoordinates>
<MeshGeometry3D.Normals> 0,0,1, 0,0,1, 0,0,1, 0,0,1 </MeshGeometry3D.Normals>
</MeshGeometry3D>
<DiffuseMaterial x:Key="Img">
<DiffuseMaterial.Brush>
<ImageBrush ImageSource="start.jpg" />
</DiffuseMaterial.Brush>
</DiffuseMaterial>
<Viewport3D x:Name="View3D_2">
<Viewport3D.Camera>
<PerspectiveCamera
FieldOfView="{Binding ElementName=FOV, Path=Value}"
Position="0,0,100"
LookDirection="0,0,-1"
/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<AmbientLight Color="White" />
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
Axis="0 1 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
Axis="1 0 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
Axis="0 1 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
Axis="1 0 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
<Slider
Grid.Row="1"
x:Name="Left_Top_Z"
Minimum="-90"
Value="12"
Maximum="90"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<Slider
Grid.Row="2"
x:Name="Right_Bottom_Z"
Minimum="-90"
Value="-12"
Maximum="0"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<Slider
Grid.Row="3"
x:Name="ScaleZ_2"
Background="Red"
Minimum="10"
Maximum="100"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
>
<Slider.Style>
<style TargetType="Slider">
<Style.Triggers>
<Trigger Property="Tag" Value="1">
<Trigger.EnterActions>
<BeginStoryboard >
<Storyboard>
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetProperty="Value" From="11" To="20" BeginTime="0:0:0" Duration="0:0:10" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</style>
</Slider.Style>
</Slider>
<Slider
Grid.Row="4"
x:Name="FOV"
Minimum="0"
Maximum="180"
Value="176"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
実行して確認します。

なんとなくそれらしくなってきました。
残りはアニメーションの調整です。
最初と最後をつなぐアニメーションを実現したい場合、実際には 2 つのアニメーションの最初と最後に違いが見えないようにする必要があります。先輩方の基本的な考え方を参考に、透明度と遅延再生を利用して、最初と最後の違いを分からなくします。

つまり、次のようにする必要があります。

ここでは、この方式を XAML だけで実装します。
まず、タイミングの問題を解決する必要があります。ページ読み込み時に、アニメーションであるオブジェクトの特定の値を変更します。そのオブジェクトにはトリガーがあり、このオブジェクトを監視してタイマーを開始し、その後アニメーションを再生します。
値の変更には、ObjectAnimationUsingKeyFrames アニメーションを使用します。
トリガーは値トリガーで、バインドできるためです。
全体のプロセスで最も重要なのはタイムラインで、2 つのアニメーショングループがいつ表示され、いつ非表示になるかです。

最終コード
<Window.Resources>
<Storyboard
RepeatBehavior="Forever"
Storyboard.TargetName="ScaleZ_2"
Duration="0:0:10"
x:Key="sb"
>
<DoubleAnimation
Storyboard.TargetName="ScaleZ_2"
Storyboard.TargetProperty="Value"
From="11"
To="20"
BeginTime="0:0:0"
Duration="0:0:10"
/>
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="View3D_2"
BeginTime="0:0:0"
>
<LinearDoubleKeyFrame KeyTime="0:0:0" Value="0" />
<LinearDoubleKeyFrame KeyTime="0:0:2" Value="1" />
<LinearDoubleKeyFrame KeyTime="0:0:6" Value="1" />
<LinearDoubleKeyFrame KeyTime="0:0:10" Value="0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<MeshGeometry3D x:Key="Rect3D_O">
<MeshGeometry3D.Positions>
-50, 50, 0, -50, -50, 0, 50, -50, 0, 50, 50, 0
</MeshGeometry3D.Positions>
<MeshGeometry3D.TriangleIndices>
0 1 2 2 3 0
</MeshGeometry3D.TriangleIndices>
<MeshGeometry3D.TextureCoordinates>
0,0 0,1 1,1 1,0
</MeshGeometry3D.TextureCoordinates>
<MeshGeometry3D.Normals>
0,0,1, 0,0,1, 0,0,1, 0,0,1
</MeshGeometry3D.Normals>
</MeshGeometry3D>
<DiffuseMaterial x:Key="Img">
<DiffuseMaterial.Brush>
<ImageBrush ImageSource="start.jpg" />
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="Loaded" SourceName="ScaleZ">
<BeginStoryboard>
<Storyboard
Storyboard.TargetName="ScaleZ"
Storyboard.TargetProperty="Tag"
Duration="0:0:5.1"
>
<ObjectAnimationUsingKeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:5">
<DiscreteObjectKeyFrame.Value> 1 </DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
<BeginStoryboard Storyboard="{StaticResource sb}" />
</EventTrigger>
</Window.Triggers>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Viewport3D x:Name="View3D_1">
<Viewport3D.Style>
<style TargetType="Viewport3D">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ScaleZ,Path=Value}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard >
<Storyboard>
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever" Storyboard.TargetProperty="Opacity" BeginTime="0:0:0" Duration="0:0:10">
<LinearDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<LinearDoubleKeyFrame KeyTime="0:0:5" Value="1"/>
<LinearDoubleKeyFrame KeyTime="0:0:8" Value="1"/>
<LinearDoubleKeyFrame KeyTime="0:0:10" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</style>
</Viewport3D.Style>
<Viewport3D.Camera>
<PerspectiveCamera
FieldOfView="{Binding ElementName=FOV, Path=Value}"
Position="0,0,100"
LookDirection="0,0,-1"
/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<AmbientLight Color="White" />
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
Axis="0 1 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
Axis="1 0 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
Axis="0 1 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
Axis="1 0 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
<Viewport3D x:Name="View3D_2">
<Viewport3D.Camera>
<PerspectiveCamera
FieldOfView="{Binding ElementName=FOV, Path=Value}"
Position="0,0,100"
LookDirection="0,0,-1"
/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<AmbientLight Color="White" />
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
Axis="0 1 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
Axis="1 0 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
Axis="0 1 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
Axis="1 0 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
<Slider
Grid.Row="1"
x:Name="Left_Top_Z"
Minimum="-90"
Value="12"
Maximum="90"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<Slider
Grid.Row="2"
x:Name="Right_Bottom_Z"
Minimum="-90"
Value="-12"
Maximum="0"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<Slider
Grid.Row="3"
x:Name="ScaleZ"
Background="Red"
Minimum="10"
Maximum="100"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
>
<Slider.Style>
<style TargetType="Slider">
<Style.Triggers>
<Trigger Property="Tag" Value="1">
<Trigger.EnterActions>
<BeginStoryboard >
<Storyboard>
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetProperty="Value" From="11" To="20" BeginTime="0:0:0" Duration="0:0:10" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</style>
</Slider.Style>
</Slider>
<Slider
Grid.Row="4"
x:Name="FOV"
Minimum="0"
Maximum="180"
Value="176"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<Slider
Grid.Row="5"
x:Name="ScaleZ_2"
Background="Black"
Minimum="10"
Maximum="100"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<StackPanel Grid.Row="6" Orientation="Vertical">
<TextBlock>
<Run Text=" View3D_1 透明度" />
<Run Text="{Binding ElementName=View3D_1, Path=Opacity}" />
<Run Text=" View3D_1 value" />
<Run Text="{Binding ElementName=ScaleZ, Path=Value}" />
<Run Text=" Tag value" />
<Run Text="{Binding ElementName=ScaleZ, Path=Tag}" />
<Run Text=" Zindex value" />
<Run Text="{Binding ElementName=View3D_1, Path=(Panel.ZIndex)}" />
</TextBlock>
<TextBlock>
<Run Text=" View3D_2 透明度" />
<Run Text="{Binding ElementName=View3D_2, Path=Opacity}" />
<Run Text=" View3D_2 value" />
<Run Text="{Binding ElementName=ScaleZ_2, Path=Value}" />
<Run Text=" Zindex value" />
<Run Text="{Binding ElementName=View3D_2, Path=(Panel.ZIndex)}" />
</TextBlock>
</StackPanel>
</Grid>
こうすることで、C# コードを一切使わずにこの効果を実現できます。

ただし、開始の 0 秒の部分はあまりうまくできておらず、少しゴーストが出ています。まだまだ学ぶべきことがたくさんあります。