You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 10:37:35