我正在尝试使用用户触摸来渲染2D三角形。因此,我将让用户触摸屏幕上的三个点,这些点将用作三角形的顶点。
答案 0 :(得分:4)
您已经知道需要从顶点着色器返回剪贴空间坐标(技术上为not normalized device coordinates)。问题是从UIKit坐标到Metal的剪贴空间坐标的方式和位置。
让我们从定义这些不同的空间开始。请注意,下面,为简单起见,我实际上使用了NDC坐标,因为在这种特殊情况下,我们不会通过使用w != 1
返回顶点位置来引入透视图。 (这里我指的是剪辑空间位置的w
坐标;在下面的讨论中,w
总是指视图宽度。)
我们在方便的任何空间(通常称为模型空间)中将顶点传递到顶点着色器中。由于我们是在2D模式下工作,因此我们不需要通常的一系列到世界空间的变换,而是到眼空间的变换。本质上,UIKit视图的坐标是我们的模型空间,世界空间和眼睛空间全部合为一体。
我们需要某种正投影投影矩阵才能从该空间移动到剪辑空间。如果我们剔除与z轴相关的不必要部分,并假定视图边界的原点为(0,0),则可以进行以下转换:
我们可以将此矩阵传递到顶点着色器中,也可以在将顶点发送到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);
只是为了验证是否从此变换中获得了正确的结果,我们插入视图的左上角和右下角点,以确保它们到达剪辑空间的末端(通过线性,如果这些点变换了,正确,其他所有人也一样):
这些要点看起来是正确的,所以我们完成了。如果您担心此转换带来的明显失真,请注意,它已被光栅化之前发生的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)
}