iOS Metal中的透视正确纹理映射

3

我尝试了多种方法来实现插值并生成透视校正图像,但是没有一种建议的方法有效。

我的当前代码如下:

struct VertexStruct
{
  float4 normalizedPosition [[ position ]];
  float4 texCoord;
}

vertex VertexStruct testVertex(device float4 *vertices [[ buffer(0) ]],
                                     uint vid [[ vertex_id ]])
{
  VertexStruct outVertices;

  outVertices.normalizedPosition = ...;

  outVertices.texCoord = float4(vertices[vid].x, vertices[vid].y, 0.0, 127.0);

  return outVertices;
}

fragment half4 testFragment(VertexStruct inFrag [[ stage_in ]],
                            texture2d<half, access::sample> volume [[ texture(0) ]])
{
  float2 texCoord = float2(inFrag.texCoord.xy * (1.0 / inFrag.texCoord.w));
  constexpr sampler s(coord::normalized, filter::linear, address::clamp_to_zero);
  return volume.sample(s, texCoord);
}

纹理大小为128x128,顶点如下:
  • 0,0
  • 0,127
  • 127,0
  • 127,127
理论上,第四个参数w应该有助于透视校正插值。这个技巧在OpenGL ES中已被报道可行,但在我的情况下,无论我是否开启都没有变化。
得到的结果如下所示:

enter image description here

蓝色是清晰的颜色,黑色梯形内的任何内容都是纹理的内容。红色梯形应该居中于黑色梯形中。如果您在Photoshop中打开并跟踪黑色梯形的对角线,则会恰好通过红色梯形的对角线。

enter image description here

这意味着渲染的两个三角形没有以透视校正采样方式读取纹理。这是纹理映射的经典问题,但我无法理解它。
有人能看出错误吗?
1个回答

4
透视纠正纹理映射已成为十多年来的常规做法,并被每个图形API默认使用,特别是Metal。只要您的设置没有完全错误,就不需要做任何事情即可获得透视纠正结果。
实际上,Metal允许您直接在MSL中指定插值和采样。默认情况下,所有内容都使用center_perspective限定符(像素中心,透视校正插值),除了位置是center_no_perspective。
由于从光栅化器传入片段函数的数据是stage_in,因此您只需在声明成员后直接在MSL代码中添加所需的采样/插值限定符,就像添加任何其他属性一样。以下是明确详细说明的示例:
struct Fragment {

    float4 position             [[ center_no_perspective ]];    // Default, can be omitted.
    float4 specialTreatment     [[ centroid_perspective ]];     // Let's say you need centroid + perspective correct.
    float2 textureCoordinates   [[ center_perspective ]];       // Default, can be omitted.

};

关于其他选项,基本上包括中心/重心/采样和透视/无透视组合,以及flat(没有插值,显然是针对整数的)。列表如下:
  • [[ flat ]] 不插值,整数成员。
  • [[ center_perspective ]] 中心,透视校正(默认)
  • [[ center_no_perspective ]] 中心,线性(位置的默认值)
  • [[ centroid_perspective ]] 重心,透视校正
  • [[ centroid_no_perspective ]] 重心,线性
  • [[ sample_perspective ]] 采样,透视校正*
  • [[ sample_no_perspective ]] 采样,线性*
警告:sample_*限定符将使片段函数每个样本执行一次,而不是通常的每个片段执行一次。子像素化是潜在的性能风险,所以请必要时使用。

嘿 @elim garak,我会将你的答案标记为正确的,因为如果我们在谈论纹理映射,这确实是正确的。我发现,如果我在顶点着色器中将我的顶点投影到一个平面上,然后在片段着色器中尝试采样纹理(这被称为“透视校正插值”),你需要一个特定的单应性矩阵来正确读取纹理。如果我改为计算点的NDC坐标并让GPU完成工作,一切都进行得很顺利。 - aledalgrande
是的,在这种情况下,您需要定制工作。但是如果可能的话,尽量充分利用底层管道是最好的选择。祝您好运! - Elim Garak

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