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

时间:2019-10-08 19:55:01

标签: swift 3d metal

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

3 个答案:

答案 0 :(得分:4)

您已经知道需要从顶点着色器返回剪贴空间坐标(技术上为not normalized device coordinates)。问题是从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

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

答案 1 :(得分:0)

这是一个将基于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);
}

您将希望通过顶点函数上的缓冲区参数以某种方式将viewSize(UIKit的bounds)传递给Metal。

答案 2 :(得分:0)

使用 SIMD 值将 Thompsonmachine 的代码翻译为 swift,这是我需要传递给着色器的值。

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)
}