将屏幕坐标转换为 Metal 的标准化设备坐标

Sau*_*mya 2 3d swift metal

我正在尝试使用用户触摸来渲染一个 2D 三角形。所以,我会让用户触摸屏幕上的三个点,这些点将用作三角形的顶点。

war*_*enm 7

您已经知道您需要从顶点着色器返回剪辑空间坐标(技术上不是标准化的设备坐标)。问题是从 UIKit 坐标到 Metal 的剪辑空间坐标的方式和位置。

让我们从定义这些不同的空间开始。请注意,下面,我其实使用NDC坐标简单起见,因为在这种特殊情况下,我们不通过返回顶点位置引入的角度w != 1。(这里我指w的是剪辑空间位置的坐标;在下面的讨论中,w始终指的是视图宽度)。

UIKit 到 Metal NDC 的转换

我们在任何方便的空间(这通常称为模型空间)中将顶点传递到我们的顶点着色器中。由于我们在 2D 中工作,我们不需要通常的一系列转换到世界空间,然后是眼睛空间。本质上,UIKit 视图的坐标是我们的模型空间、世界空间和眼睛空间三合一。

我们需要某种正交投影矩阵来从这个空间移动到裁剪空间。如果我们去掉与 z 轴相关的不必要部分,并假设我们的视图边界的原点是 (0, 0),我们会得到以下转换:

UIKit 到 NDC 转换为矩阵

我们可以将这个矩阵传递给我们的顶点着色器,或者我们可以在将顶点发送到 GPU 之前进行转换。考虑到所涉及的数据很少,此时这真的无关紧要。事实上,完全使用矩阵有点浪费,因为我们可以通过几个乘法和加法来转换每个坐标。下面是它在 Metal 顶点函数中的样子:

float2 inverseViewSize(1.0f / width, 1.0f / height); // passed in a buffer
float clipX = (2.0f * in.position.x * inverseViewSize.x) - 1.0f;
float clipY = (2.0f * -in.position.y * inverseViewSize.y) + 1.0f;
float4 clipPosition(clipX, clipY, 0.0f, 1.0f);
Run Code Online (Sandbox Code Playgroud)

只是为了验证我们从这个变换中得到了正确的结果,让我们插入我们视图的左上和右下点以确保它们在剪辑空间的末端结束(通过线性,如果这些点正确变换,那么将所有其他人):

一些样本点转换

这些点看起来是正确的,所以我们完成了。如果您担心此转换引入的明显失真,请注意它被光栅化之前发生的视口转换完全取消。