How do I get the coordinate of a fragment in a fragment shader in Metal?

This minimal metal shader pair renders a simple interpolated gradient (if square / triangle present) to the screen based on the vertex color attributes:

#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);
}

      

... with the following vertices:

{
  // 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
}

      

So far so good. It displays the well-known gradient triangle / square. The one you find in almost every HelloWorld GPU tutorial.


However, I need to have a fragment shader that, instead of using the interpolated vertex color, calculates the color based on the position of the fragments on the screen. It takes a screen-filling square of vertices and then only uses the fragment shader to compute the actual colors.

From my understanding the vertex position is equal float4

, with the first three elements being a 3d vector and the fourth element being 1.0

.

So I thought, should it be easy to modify the above so that it just reinterprets the vertex position as a color in the fragment shader, right?

#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);
}

      

... with the following vertices:

{
  // 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,
}

      

I was quite surprised, however, to display a uniformly colored (yellow) screen, instead of a gradient going from red=0.0

to red=1.0

on the x-axis and from green=0.0

to green=1.0

on the x-axis:

( expected output output versus actual output output )

Seems to interpolated.position

give the same value for each chunk.

What am I doing wrong here?

Ps: (Although this dummy slice logic could easily have been done using vertex interpolation, my actual slice logic cannot.)

+3


source to share


1 answer


Apparently interpolation. Position gives the same value for each chunk.



No, the values ​​are very high. A variable with the qualifier [[position]] in the fragment shader is in pixel coordinates. Divide the render target sizes and you can see what you want, except that you have to invert the green value because the metal convention is to define the top-left as the source for that, not the bottom-left.

+3


source







All Articles