如何在C#的2D画布中不使用外部库绘制旋转立方体
手动实现C# WinForms 3D立方体旋转与2D投影
你当前的代码是通过固定坐标绘制类3D线条,但要实现可旋转的立方体,核心是先维护立方体的3D顶点集合,对每个顶点执行旋转变换后,再通过透视投影映射到2D画布,最后连接顶点绘制边。以下是具体实现步骤和指导:
一、定义3D顶点与立方体结构
首先用结构体存储3D顶点坐标,再定义立方体的8个顶点和连接顶点的边索引:
// 3D顶点结构体 public struct Vertex3D { public float X, Y, Z; public Vertex3D(float x, float y, float z) { X = x; Y = y; Z = z; } } // 中心在原点的立方体顶点(边长为2) private Vertex3D[] cubeVertices = new Vertex3D[] { new Vertex3D(-1, -1, -1), new Vertex3D(1, -1, -1), new Vertex3D(1, 1, -1), new Vertex3D(-1, 1, -1), new Vertex3D(-1, -1, 1), new Vertex3D(1, -1, 1), new Vertex3D(1, 1, 1), new Vertex3D(-1, 1, 1) }; // 立方体的边:每两个整数代表一条边的起始/结束顶点索引 private int[] cubeEdges = new int[] { 0,1, 1,2, 2,3, 3,0, // 前侧面 4,5, 5,6, 6,7, 7,4, // 后侧面 0,4, 1,5, 2,6, 3,7 // 连接前后的边 };
二、实现3D旋转变换
绕坐标轴的旋转基于三角函数推导,直接对每个顶点的坐标进行计算即可:
绕X轴旋转
private Vertex3D RotateX(Vertex3D v, float angleRad) { float cos = (float)Math.Cos(angleRad); float sin = (float)Math.Sin(angleRad); return new Vertex3D( v.X, v.Y * cos - v.Z * sin, v.Y * sin + v.Z * cos ); }
绕Y轴旋转
private Vertex3D RotateY(Vertex3D v, float angleRad) { float cos = (float)Math.Cos(angleRad); float sin = (float)Math.Sin(angleRad); return new Vertex3D( v.X * cos + v.Z * sin, v.Y, -v.X * sin + v.Z * cos ); }
绕Z轴旋转
private Vertex3D RotateZ(Vertex3D v, float angleRad) { float cos = (float)Math.Cos(angleRad); float sin = (float)Math.Sin(angleRad); return new Vertex3D( v.X * cos - v.Y * sin, v.X * sin + v.Y * cos, v.Z ); }
三、实现透视投影
利用相似三角形原理实现近大远小的透视效果,同时适配WinForms的坐标系统(Y轴向下):
private PointF Project(Vertex3D v, int screenWidth, int screenHeight, float fov, float zOffset) { // zOffset确保立方体在相机前方,避免Z坐标为0或负数 float z = v.Z + zOffset; if (z <= 0) z = 0.1f; // 防止除以0 // 计算缩放比例,fov控制视野范围 float scale = fov / z; // WinForms Y轴向下,所以对Y坐标取反 float screenX = v.X * scale + screenWidth / 2f; float screenY = -v.Y * scale + screenHeight / 2f; return new PointF(screenX, screenY); }
四、完整绘制与旋转动画
通过Timer更新旋转角度,在Paint事件中完成顶点变换、投影和绘制:
private float rotX = 0f, rotY = 0f, rotZ = 0f; private Timer rotationTimer; public Form1() { InitializeComponent(); // 初始化定时器,控制旋转帧率 rotationTimer = new Timer { Interval = 30 }; rotationTimer.Tick += (s, e) => { rotX += 0.02f; rotY += 0.03f; rotZ += 0.01f; Invalidate(); // 触发重绘 }; rotationTimer.Start(); } private void Form1_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; g.Clear(Color.White); int width = ClientSize.Width; int height = ClientSize.Height; float fov = 200f; // 视野系数,值越大立方体看起来越近 float zOffset = 5f; // 立方体与相机的距离 // 对所有顶点执行旋转+投影 PointF[] projectedPoints = new PointF[cubeVertices.Length]; for (int i = 0; i < cubeVertices.Length; i++) { Vertex3D v = cubeVertices[i]; v = RotateX(v, rotX); v = RotateY(v, rotY); v = RotateZ(v, rotZ); projectedPoints[i] = Project(v, width, height, fov, zOffset); } // 绘制所有边 for (int i = 0; i < cubeEdges.Length; i += 2) { int startIdx = cubeEdges[i]; int endIdx = cubeEdges[i + 1]; g.DrawLine(Pens.Black, projectedPoints[startIdx], projectedPoints[endIdx]); } }
五、关键细节与学习指导
- 坐标适配:WinForms的Y轴方向向下,所以投影时必须对顶点Y坐标取反,否则立方体显示颠倒。
- 旋转角度:所有旋转函数接收的是弧度值,
Math.Cos/Math.Sin默认使用弧度参数,直接累加浮点值即可实现平滑旋转。 - 旋转矩阵优化:可以将三个旋转矩阵合并为一个复合矩阵,减少重复计算,提升性能(适合大量顶点的场景)。
- 透视原理:透视投影的核心是相似三角形,可自行推导
scale = fov / z的公式,理解视野、焦距和投影平面的关系。 - 旋转矩阵学习:从线性代数的3D变换矩阵入手,掌握绕X/Y/Z轴旋转的矩阵推导逻辑,这是手动实现3D变换的基础。
内容的提问来源于stack exchange,提问作者Mirra Chvaliuk
相关产品推荐
相关产品推荐

