简要概述,您可以想象我正在将 3D 原理图的点绘制到画布上,想想 CAD。
我有 3D XYZ Vector3 坐标中的点,并且我有模型、视图、投影矩阵所有设置。我已经完成了此操作并生成了 OpenGL 和 Metal (Mac/iOS) 视图,它们都工作得很好。
我现在只想在 2D 画布(SkiaSharp)上绘制点作为另一种视图表示,但我遇到了一个我无法理解的问题。
首先,我对其进行了简单的正交视图,效果很好。
我使用 Matrix4x4
CreateOrthographic
来做 Projection
矩阵(https://learn.microsoft.com/en-us/dotnet/api/system.numerics.matrix4x4.createorthographic?view=net-8.0)
我使用
CreateLookAt
作为 View
矩阵 (https://learn.microsoft.com/en-us/dotnet/api/system.numerics.matrix4x4.createlookat?view=net-8.0)
相机垂直放置在单个模型中心上方并向下看(例如,位置为
Center.X, Center.Y, 10
,目标为 Center.X, Center.Y, 0
,相机向上作为单位 Y 向量。
这一切在正交图中看起来都很好。
然后我尝试了透视投影矩阵。
我使用了
CreatePerspectiveFieldOfView
(https://learn.microsoft.com/en-us/dotnet/api/system.numerics.matrix4x4.createperspectivefieldofview?view=net-8.0) - 我将其与数据一起使用OpenGL/Metal(看起来完全符合预期),但在 Skia 画布上手动创建的内容看起来不太正确。
它看起来仍然是正交的???
这是我尝试过并发现的......
1 - 视图看起来是正交的。当我旋转模型时,其中有一个大的平面网格,并且网格的远边缘在屏幕上的大小与网格的近边缘的大小相同......看起来是正交的,但它是一个“透视 FOV 矩阵” .
2 - 当我尝试在 Z 轴上移动相机时(无论是在
CreateLookAt
位置,还是在事后进行平移)....视图根本不会改变。再说一遍,我可以用正交视图理解,但不能用透视 FOV 理解?
3 - 输入到透视图中的长宽比会垂直或水平拉伸视图。我必须输入 1(正方形)才能获得非拉伸视图(红旗!)4 - 除了缩放整个视图(如您所料,它变得更小/更大)之外,FOV 似乎没有做太多事情 - 但仍然呈现正交?
有人知道为什么/什么/如何会看到这种影响吗?我可能设置错误或解释错误导致这样的输出吗?我是否错过了 GPU 在 OpenGL/Metal 中执行的步骤,为什么这些矩阵在将顶点转换为着色器时看起来很好,但在手动处理 Vector3 并绘制到 Skia 上时却不然?
对于其他发现类似输出的人来说,
是 MVP 设置中的透视矩阵可能会出现正交的原因,问题是有一个阶段 OpenGl/Directx/metal 都自动执行,但我错过了。
你需要做一个透视划分,我忘了做。因此,采用 Vector3 世界位置,采用 w 值设置为 1 的 Vector4 - 并用您的 MVP 矩阵对其进行转换。
然后,使用生成的 Vector4,您可以创建一个新的 Vector3,其中每个分量是 Vector4 分量除以 Vector4 W 值。
那么,
X = vec.X / vec.W
Y = vec.Y / vec.W
Z = vec.Z / vec.W
对于屏幕空间,您只需要 X 和 Y。那么,如果你不这样做,为什么它会显示为正交?
W 值是近剪平面和远剪平面之间投影点的深度。因此,如果您不进行除法,就像将每个点除以 1 - 这会将所有视觉内容置于相同的深度(远平面) - 使其看起来正交!
所以大致需要完整的步骤...
Vector3 wordPos;
Matrix4x4 MVP;
Vector4 input = new Vector4(worldPos, 1f);
Vector4 trans = Vector4.Transform(input, MVP);
Vector2 screen = new Vector2(trans.X / trans.W, trans.Y / trans.W);