这个最小的 Metal 着色器对根据顶点的颜色属性在屏幕上渲染一个简单的插值渐变(当提供顶点四边形/三角形时):
#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
}
目前为止,一切都好。它呈现众所周知的梯度三角形/四边形。
您几乎在每个GPU HelloWorld教程中找到的那个。
但是,我需要有一个片段着色器,而不是采用插值顶点颜色,而是根据屏幕上的片段位置计算颜色。它接收一个屏幕填充顶点的四边形,然后仅使用片段着色器来计算实际颜色。
据我了解,顶点的位置是一个float4
,前三个元素是 3d 向量,第 4 个元素设置为 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.0
到 red=1.0
的渐变,在 x 轴上从green=0.0
到green=1.0
:
(预期渲染输出与实际渲染输出)
interpolated.position
似乎为每个片段产生相同的值。
我在这里做错了什么?
Ps:(虽然这个虚拟片段逻辑可以使用顶点插值轻松完成,但我的实际片段逻辑却不能。
插值位置似乎产生了相同的值 每个片段。
不,值非常大。片段着色器中带有 [[position]] 限定符的变量采用像素坐标。除以渲染目标尺寸,您将看到您想要的内容,除了必须反转绿色值,因为 Metal 的惯例是将左上角定义为原点,而不是左下角。