如何在 Metal 的片段着色器中获取片段坐标?

How to get fragment coordinate in fragment shader in Metal?

这个最小的金属着色器对根据顶点的颜色属性在屏幕上渲染一个简单的插值渐变(当提供顶点 quad/triangle 时):

#include <metal_stdlib>

using namespace metal;

typedef struct {
    float4 position [[position]];
    float4  color;
} vertex_t;

vertex vertex_t vertex_function(const device vertex_t *vertices [[buffer(0)]], uint vid [[vertex_id]]) {
    return vertices[vid];
}

fragment half4 fragment_function(vertex_t interpolated [[stage_in]]) {
    return half4(interpolated.color);
}

...具有以下顶点:

{
  // x,    y,   z,   w,   r,   g,   b,   a    
     1.0, -1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0,
    -1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0,
    -1.0,  1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0,

     1.0,  1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0,
     1.0, -1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0,
    -1.0,  1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0
}

到目前为止一切顺利。它呈现众所周知的渐变 triangle/quad.
您几乎可以在每个 GPU HelloWorld 教程中找到它。


然而,我需要一个片段着色器,而不是采用插值顶点颜色,而是根据屏幕上的片段位置计算颜色。 它接收一个充满屏幕的四边形顶点,然后仅使用片段着色器来计算实际颜色。

根据我的理解,顶点的位置是 float4,前三个元素是 3d 向量,第四个元素设置为 1.0

所以——我想——修改上面的代码应该很容易,让它简单地将顶点的位置重新解释为片段着色器中的颜色,对吗?

#include <metal_stdlib>

using namespace metal;

typedef struct {
    float4 position [[position]];
} vertex_t;

vertex vertex_t vertex_function(const device vertex_t *vertices [[buffer(0)]], uint vid [[vertex_id]]) {
    return vertices[vid];
}

fragment half4 fragment_function(vertex_t interpolated [[stage_in]]) {
    float4 color = interpolated.position;
    color += 1.0; // move from range -1..1 to 0..2
    color *= 0.5; // scale from range 0..2 to 0..1
    return half4(color);
}

...具有以下顶点:

{
  // x,    y,   z,   w,
     1.0, -1.0, 0.0, 1.0,
    -1.0, -1.0, 0.0, 1.0,
    -1.0,  1.0, 0.0, 1.0,

     1.0,  1.0, 0.0, 1.0,
     1.0, -1.0, 0.0, 1.0,
    -1.0,  1.0, 0.0, 1.0,
}

然而,我很惊讶地发现呈现的是统一颜色(黄色)的屏幕,而不是 x 轴上从 red=0.0red=1.0green=0.0green=0.0 的渐变green=1.0 x 轴:

(expected render output vs. actual render output)

interpolated.position 似乎为每个片段生成相同的值。

我做错了什么?

Ps:(虽然这个虚拟片段逻辑可以使用顶点插值轻松完成,但我的实际片段逻辑不能。)

The interpolated.position appears to be yielding the same value for each fragment.

不是,只是数值非常大。在片段着色器中,带有 [[position]] 限定符的变量位于像素坐标中。除以渲染目标尺寸,你会看到你想要的,除了必须反转绿色值,因为 Metal 的惯例是将左上角定义为原点,而不是左下角。