首页 > 解决方案 > 将屏幕坐标转换为 Metal 的标准化设备坐标

问题描述

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

标签: swift3dmetal

解决方案


您已经知道您需要从顶点着色器返回剪辑空间坐标(技术上不是标准化的设备坐标)。问题是如何以及从何处从 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);

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

一些样本点转换

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


推荐阅读