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

4
我正在尝试使用用户触摸来渲染一个二维三角形。因此,我将让用户在屏幕上触摸三个点,并将这些点用作三角形的顶点。
3个回答

11
你已经知道需要从顶点着色器返回剪裁空间坐标(严格来说是非规范化设备坐标)。问题是如何将UIKit坐标转换为Metal的剪裁空间坐标,并在哪里进行转换。
让我们先定义这些不同的空间。请注意,在下面的内容中,我实际上出于简单起见使用了NDC坐标,因为在这种情况下,我们没有通过返回具有w!= 1的顶点位置引入透视。(这里我指的是剪辑空间位置的w坐标;在接下来的讨论中,w始终指视图宽度)。

UIKit to Metal NDC transform

我们将顶点传递到方便的空间中(通常称为模型空间)中的顶点着色器中。由于我们在2D中工作,因此不需要通常的从世界空间到眼睛空间的一系列变换。实际上,UIKit视图的坐标是我们的模型空间、世界空间和眼睛空间的全部。
我们需要一些正交投影矩阵来从这个空间移动到剪辑空间。如果我们剥离与z轴相关的不必要部分,并假设我们的视图边界原点为(0, 0),那么我们就可以得出以下转换:

UIKit to NDC transformation as a matrix

我们可以将这个矩阵传入顶点着色器,或者在将顶点发送到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);

为了验证我们从这个转换中获得正确的结果,让我们插入视图的左上角和右下角点,以确保它们最终位于剪辑空间的极端位置(通过线性性,如果这些点正确地变换,那么所有其他点也将如此):

Some sample points transformed

这些观点是正确的,所以我们完成了。如果你担心这种转换引入的明显扭曲,注意它正好被视口变换抵消了,在光栅化之前发生。


1
这里有一个函数,可以将UIKit视图基于的坐标转换为Metal的剪辑空间坐标(基于warrenm的答案)。它可以直接添加到着色器文件中,并从顶点着色器函数中调用。
float2 convert_to_metal_coordinates(float2 point, float2 viewSize) {
    
    float2 inverseViewSize = 1 / viewSize;
    float clipX = (2.0f * point.x * inverseViewSize.x) - 1.0f;
    float clipY = (2.0f * -point.y * inverseViewSize.y) + 1.0f;
    
    return float2(clipX, clipY);
}

您需要将UIKit的bounds(即viewSize)通过顶点函数的缓冲参数传递给Metal。请注意保留HTML标签。

0
将Thompsonmachine的代码翻译成Swift,使用SIMD值,这正是我需要传递给着色器的。
func convertToMetalCoordinates(point: CGPoint, viewSize: CGSize) -> simd_float2 {
    let inverseViewSize = CGSize(width: 1.0 / viewSize.width, height: 1.0 / viewSize.height)
    let clipX = Float((2.0 * point.x * inverseViewSize.width) - 1.0)
    let clipY = Float((2.0 * -point.y * inverseViewSize.height) + 1.0)
    return simd_float2(clipX, clipY)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接