iOS 金属中的透视正确纹理映射
Perspective correct texture mapping in iOS Metal
我尝试了各种方法来进行插值并生成透视正确的图像,但是 none 的建议方法有效。
我当前的代码是:
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
理论上,第 4 个参数 w
应该有助于透视校正插值。据报道,这个技巧在 OpenGL ES 中有效,但就我而言,无论是否有,都没有任何变化。
我得到的是:
蓝色为透明色,黑色梯形内为贴图内容。红色梯形应位于黑色梯形的中心。如果你用Photoshop打开它并追踪黑色梯形的对角线,它会完全通过红色梯形的对角线。
这意味着渲染的两个三角形没有使用透视正确采样读取纹理。很经典的纹理贴图问题,我怎么也想不通
谁能看到错误?
透视正确的纹理映射已成为十多年来的规范,并且每个图形都默认使用 API,尤其是 Metal。您无需执行任何操作即可获得透视正确的结果,前提是您在设置中没有做完全错误的事情。
事实上,Metal 允许您直接在 MSL 中指定插值和采样。 默认,一切都使用center_perspective
限定符(像素中心,透视正确插值,明显的例外是位置center_no_perspective
。
鉴于从光栅化器进入您的片段函数的数据是 stage_in
,您只需在 MSL 中直接在成员声明之后添加所需的 sampling/interpolation 限定符,就像任何其他属性一样代码。明确详细说明的示例:
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.
};
至于你的其他选项,它基本上是 center/centroid/sample 和 perspective/no_perspective 的组合,以及 flat
(没有插值,显然是整数)。在列表中,它看起来像:
[[ flat ]]
无插值,整数成员。
[[ center_perspective ]]
居中,透视正确 (默认)
[[ center_no_perspective ]]
居中,线性 (位置默认值)
[[ centroid_perspective ]]
质心,透视正确
[[ centroid_no_perspective ]]
质心,线性
[[ sample_perspective ]]
示例,透视正确*
[[ sample_no_perspective ]]
样本,线性*
警告: sample_*
限定符将使片段函数按样本执行,而不是通常按片段执行。使用亚像素是潜在的性能危险信号,因此在必要时使用。
我尝试了各种方法来进行插值并生成透视正确的图像,但是 none 的建议方法有效。
我当前的代码是:
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
理论上,第 4 个参数 w
应该有助于透视校正插值。据报道,这个技巧在 OpenGL ES 中有效,但就我而言,无论是否有,都没有任何变化。
我得到的是:
蓝色为透明色,黑色梯形内为贴图内容。红色梯形应位于黑色梯形的中心。如果你用Photoshop打开它并追踪黑色梯形的对角线,它会完全通过红色梯形的对角线。
这意味着渲染的两个三角形没有使用透视正确采样读取纹理。很经典的纹理贴图问题,我怎么也想不通
谁能看到错误?
透视正确的纹理映射已成为十多年来的规范,并且每个图形都默认使用 API,尤其是 Metal。您无需执行任何操作即可获得透视正确的结果,前提是您在设置中没有做完全错误的事情。
事实上,Metal 允许您直接在 MSL 中指定插值和采样。 默认,一切都使用center_perspective
限定符(像素中心,透视正确插值,明显的例外是位置center_no_perspective
。
鉴于从光栅化器进入您的片段函数的数据是 stage_in
,您只需在 MSL 中直接在成员声明之后添加所需的 sampling/interpolation 限定符,就像任何其他属性一样代码。明确详细说明的示例:
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.
};
至于你的其他选项,它基本上是 center/centroid/sample 和 perspective/no_perspective 的组合,以及 flat
(没有插值,显然是整数)。在列表中,它看起来像:
[[ flat ]]
无插值,整数成员。[[ center_perspective ]]
居中,透视正确 (默认)[[ center_no_perspective ]]
居中,线性 (位置默认值)[[ centroid_perspective ]]
质心,透视正确[[ centroid_no_perspective ]]
质心,线性[[ sample_perspective ]]
示例,透视正确*[[ sample_no_perspective ]]
样本,线性*
警告: sample_*
限定符将使片段函数按样本执行,而不是通常按片段执行。使用亚像素是潜在的性能危险信号,因此在必要时使用。