一、坐标系
- WPF 的 3D 坐标系原点通常位于对象中心,X 轴向右,Y 轴向上,Z 轴朝向观察者(右手定则)。
正四面体旋转以原点为中心,绕Z 轴自动旋转。
二、正八面体的坐标
- 正八面体有6个顶点,12条边,8个面。
取六个顶点为:(0,0,0),(-1,0,0),(0,1,0),(0,-1,0),(0,0,1),(0,0,-1) ,可以验证所有六条棱长均为 2\sqrt{2}2 ,所以这是一个棱长为 2\sqrt{2}2 的正四面体,且四个顶点全是整数。
这些点的坐标全是整数。它们构成一个正八面体:六个顶点到原点距离都等于 1,任意两个相邻顶点之间的距离都是 2\sqrt{2}2 ,相对顶点之间距离为2。这个多面体有 8 个全等的等边三角形面,因此是正八面体。
三、设计
-
正八面体有 6 个顶点、8 个等边三角形面。
利用之前得到的整数顶点坐标,我们可以把它们放大到合适的尺寸( 5, 0, 0), (-5, 0, 0),
( 0, 5, 0), ( 0,-5, 0),
( 0, 0, 5), ( 0, 0,-5) -
8 个面分别由这些顶点中的三个组成(每个面是连接原点相邻轴端点形成的三角形)。
具体面的顶点索引(从 0 到 5)如下:面1: 0,2,4 ( 5,0,0), (0,5,0), (0,0,5) 三角形索引:0,1,2 法线朝外
面2: 0,4,3 ( 5,0,0), (0,0,5), (0,-5,0) 三角形索引:0,1,2 法线朝外
面3: 0,3,5 ( 5,0,0), (0,-5,0), (0,0,-5) 三角形索引:0,1,2 法线朝外
面4: 0,5,2 ( 5,0,0), (0,0,-5), (0,5,0) 三角形索引:0,1,2 法线朝外
面5: 1,2,4 (-5,0,0), (0,5,0), (0,0,5) 三角形索引:0,2,1 法线朝外
面6: 1,4,3 (-5,0,0), (0,0,5), (0,-5,0) 三角形索引:0,2,1 法线朝外
面7: 1,3,5 (-5,0,0), (0,-5,0), (0,0,-5) 三角形索引:0,2,1 法线朝外
面8: 1,5,2 (-5,0,0), (0,0,-5), (0,5,0) 三角形索引:0,2,1 法线朝外
(索引顺序按逆时针方向,保证法线朝外。也可以调整顺序或启用双面渲染。)
也可以将这些面放在一个 MeshGeometry3D 中,并用一个颜色(或每个面不同颜色)。
为了保持原有的多色风格,继续使用 8 个独立的 GeometryModel3D,每个赋予不同的颜色(如红、橙、黄、绿、青、蓝、紫、粉等)。

四、MainWindow.xaml
<Window x:Class="WpfA_Octahedron.MainWindow"
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"
xmlns:local="clr-namespace:WpfA_Octahedron"
mc:Ignorable="d"
Title="正八面体" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="250"/>
</Grid.ColumnDefinitions>
<!-- 右侧控制面板 -->
<StackPanel Grid.Column="1" Margin="10">
<!-- 焦距控制 -->
<TextBlock Text="调节相机焦距" Margin="0,0,0,5"/>
<Slider x:Name="viewAngle" Width="200" Value="60" Minimum="10" Maximum="150" />
<TextBox Text="{Binding ElementName=viewAngle, Path=Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Width="80" Margin="0,5,0,10" HorizontalAlignment="Left"/>
<!-- X轴旋转 -->
<TextBlock Text="X轴旋转" Margin="0,0,0,5"/>
<Slider x:Name="rotateX" Width="200" Value="220" Minimum="0" Maximum="360" />
<TextBox Text="{Binding ElementName=rotateX, Path=Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Width="80" Margin="0,5,0,10" HorizontalAlignment="Left"/>
<!-- Y轴旋转 -->
<TextBlock Text="Y轴旋转" Margin="0,0,0,5"/>
<Slider x:Name="rotateY" Width="200" Value="40" Minimum="0" Maximum="360" />
<TextBox Text="{Binding ElementName=rotateY, Path=Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Width="80" Margin="0,5,0,10" HorizontalAlignment="Left"/>
<!-- Z轴旋转 -->
<TextBlock Text="Z轴旋转" Margin="0,0,0,5"/>
<Slider x:Name="rotateZ" Width="200" Value="0" Minimum="0" Maximum="360" />
<TextBox Text="{Binding ElementName=rotateZ, Path=Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Width="80" Margin="0,5,0,10" HorizontalAlignment="Left"/>
<!-- 按钮 -->
<Button x:Name="btnAutoRotate" Content="启动自动旋转" Width="120" Margin="0,20,0,5" Click="BtnAutoRotate_Click"/>
<Button x:Name="btnExit" Content="退出程序" Width="120" Click="BtnExit_Click"/>
</StackPanel>
<!-- 3D 视口(保持不变) -->
<Viewport3D Grid.Column="0">
<Viewport3D.Camera>
<PerspectiveCamera Position="0,0,50" LookDirection="0,0,-1"
FieldOfView="{Binding Value, ElementName=viewAngle}"
UpDirection="0,1,0"/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<AmbientLight Color="Gray"/>
<DirectionalLight Color="Gray" Direction="-1,-1,-1"/>
<!-- 八个三角面,使用正八面体的顶点 -->
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="5,0,0 0,5,0 0,0,5"
TriangleIndices="0,1,2"/>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush="Red"/>
</GeometryModel3D.Material>
</GeometryModel3D>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="5,0,0 0,0,5 0,-5,0"
TriangleIndices="0,1,2"/>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush="Orange"/>
</GeometryModel3D.Material>
</GeometryModel3D>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="5,0,0 0,-5,0 0,0,-5"
TriangleIndices="0,1,2"/>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush="Yellow"/>
</GeometryModel3D.Material>
</GeometryModel3D>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="5,0,0 0,0,-5 0,5,0"
TriangleIndices="0,1,2"/>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush="Green"/>
</GeometryModel3D.Material>
</GeometryModel3D>
<!-- 第5个面(原 Cyan):(-5,0,0) (0,5,0) (0,0,5) -->
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="-5,0,0 0,5,0 0,0,5"
TriangleIndices="0,2,1"/>
<!-- 改为 0,2,1 -->
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush="Cyan"/>
</GeometryModel3D.Material>
</GeometryModel3D>
<!-- 第6个面(原 Blue):(-5,0,0) (0,0,5) (0,-5,0) -->
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="-5,0,0 0,0,5 0,-5,0"
TriangleIndices="0,2,1"/>
<!-- 改为 0,2,1 -->
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush="Blue"/>
</GeometryModel3D.Material>
</GeometryModel3D>
<!-- 第7个面(原 Purple):(-5,0,0) (0,-5,0) (0,0,-5) -->
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="-5,0,0 0,-5,0 0,0,-5"
TriangleIndices="0,2,1"/>
<!-- 改为 0,2,1 -->
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush="Purple"/>
</GeometryModel3D.Material>
</GeometryModel3D>
<!-- 第8个面(原 Pink):(-5,0,0) (0,0,-5) (0,5,0) -->
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="-5,0,0 0,0,-5 0,5,0"
TriangleIndices="0,2,1"/>
<!-- 改为 0,2,1 -->
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush="Pink"/>
</GeometryModel3D.Material>
</GeometryModel3D>
</Model3DGroup>
</ModelVisual3D.Content>
<!-- 旋转变换保持不变,仍作用于整个模型 -->
<ModelVisual3D.Transform>
<Transform3DGroup>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Axis="1,0,0" Angle="{Binding Value, ElementName=rotateX}"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Axis="0,1,0" Angle="{Binding Value, ElementName=rotateY}"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Axis="0,0,1" Angle="{Binding Value, ElementName=rotateZ}"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
</Transform3DGroup>
</ModelVisual3D.Transform>
</ModelVisual3D>
</Viewport3D>
</Grid>
</Window>
五、MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Threading;
namespace WpfA_Octahedron
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private DispatcherTimer? _rotationTimer; // 可为 null
private const double RotationStep = 0.5; // 每帧旋转角度增量
public MainWindow()
{
InitializeComponent();
// 初始化定时器
_rotationTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(30) // 约 33 帧/秒
};
_rotationTimer.Tick += RotationTimer_Tick;
}
// 自动旋转按钮点击事件
private void BtnAutoRotate_Click(object sender, RoutedEventArgs e)
{
if (_rotationTimer == null)
{
// 创建定时器,每 50ms 触发一次(约 20fps)
_rotationTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(50)
};
_rotationTimer.Tick += RotationTimer_Tick;
_rotationTimer.Start();
btnAutoRotate.Content = "停止自动旋转";
}
else
{
_rotationTimer.Stop();
_rotationTimer.Tick -= RotationTimer_Tick;
_rotationTimer = null;
btnAutoRotate.Content = "启动自动旋转";
}
}
// 定时器 Tick:累加 Z 轴角度
// 注意这里的 sender 参数类型为 object?,与 EventHandler 委托匹配
private void RotationTimer_Tick(object sender, EventArgs e)
{
// 三个角度每次增加 1 度(可调整速度)
rotateX.Value = (rotateX.Value + 1) % 360;
rotateY.Value = (rotateY.Value + 1) % 360;
rotateZ.Value = (rotateZ.Value + 1) % 360;
}
// 退出按钮点击事件
private void BtnExit_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}
}
}