我需要在 C# 中的 2D 画布上绘制一个类似 3D 的立方体(使用 Windows 窗体中的图形),并使其绕其轴旋转。但是,我不允许使用任何外部库(例如 OpenGL 或 DirectX)来为我处理此问题。如何手动实现 3D 旋转并投影到 2D 画布上?任何有关在 C# 中使用旋转矩阵和透视投影的资源或指南将不胜感激!
在这个阶段,我只能使用基元绘制一个立方体。
graphics.DrawLine(Pens.Black, width / 2 - x, height / 2 - x, width / 2 + x, height / 2 - x);
graphics.DrawLine(Pens.Black, width / 2 - x, height / 2 - x, width / 2 - x, height / 2 + x);
graphics.DrawLine(Pens.Red, width / 2 + x, height / 2 - x, width / 2 + x, height / 2 + x);
graphics.DrawLine(Pens.Black, width / 2 - x, height / 2 + x, width / 2 + x, height / 2 + x);
graphics.DrawLine(Pens.Black, width / 2 + x, height / 2 - x, width / 2 + x + 3 / 2 * x, height / 2 - x - 3 / 2 * x);
graphics.DrawLine(Pens.Black, width / 2 - x, height / 2 - x, width / 2 - x + 3 / 2 * x, height / 2 - x - 3 / 2 * x);
graphics.DrawLine(Pens.Black, width / 2 - x + 3 / 2 * x, height / 2 - x - 3 / 2 * x, width / 2 + 2 * x, height / 2 - x - 3 / 2 * x);
graphics.DrawLine(Pens.Blue, width / 2 + x, height / 2 + x, width / 2 + 2 * x, height / 2 + x - 3 / 2 * x);
graphics.DrawLine(Pens.Brown, width / 2 + 2 * x, height / 2 + x - 3 / 2 * x, width / 2 + 2 * x, height / 2 + x - 3 / 2 * x - 2 * x);
首先,我们需要获得立方体的 8 个点并能够旋转它们。四元数数学确实很复杂,但是一旦掌握了它的窍门,它们的 API 就非常简单了。
这是我如何做到这一点的基本要点(可能有更好的方法,我对图形不太精通)。不管怎样,你在 3D 空间中得到了立方体的 4 个非相对的角。在 3D 空间中旋转它们,然后将它们转换为 2D 表示。找出 2D 空间中哪个角最接近中心并将其删除。复制剩余点的负值,最终得到 6 个点,您可以将它们画成立方体。
这可能会破坏立方体面向前方的边缘情况,在这种情况下,您只需要 4 个点来绘制立方体。但这留给读者作为练习。
public class RotatingCube
{
public Vector3[] points = new Vector3[4]
{
// This combination of points is important here!
new( 1, 1, 1),
new( 1, -1, 1),
new(-1, -1, 1),
new(-1, 1, 1),
// new(-1, -1, -1),
// new(-1, 1, -1),
// new( 1, 1, -1),
// new( 1, -1, -1),
};
public Vector2[] GetPoints2D(Quaternion rotation)
{
Vector2 points2D = new Vector2[6];
// Rotate the 3D point and make it 2D.
// Whilst we are doing that, figure out which point is closest to the center.
double lowestLength = double.MaxValue;
int lowestIndex;
for (int i = 0; i < 4; i++)
{
var point = Vector3.Transform(point[i], rotation);
points2D[i] = new Vector2(point.X, point.Y);
var length = points2D[i].LengthSquared();
if (length < lowestLength)
{
lowestLength = length;
lowestIndex = i;
}
}
// Remove the point closest to the center and copy the reverse of the other points.
points2D[lowestIndex] = points2D[3];
points2D[3] = -points2D[0];
points2D[4] = -points2D[1];
points2D[5] = -points2D[2];
}
public void Draw(Quaternion rotation)
{
// This won't work because the graphics API expects an array of Point[]
// but you get the idea, just times it by 100 or sumethin'
// Also you're lucky I was bored and have worked with 2D representation
// of 3D objects before :)
var points2D = GetPoints2D(rotation);
graphics.FillPolygon(myBrush, points2D);
}
}