3
votes

I am trying to understand fragment shader input position from vertex shader. Does each fragment receives its position from the vertex shader?

Here is my shaders.metal

struct VertexOut{
    float4 position [[position]];
    float4 color;
};

vertex VertexOut basic_vertex(     
                           const device packed_float3* vertex_array [[ buffer(0) ]],
                           unsigned int vid [[ vertex_id ]]) {                 
    VertexOut vertexOut;
    vertexOut.position = float4(vertex_array[vid], 1.0);
    vertexOut.color = (vertexOut.position+1)/2;
    return vertexOut;      
}

color computed in vertex shader

fragment float4 basic_fragment(VertexOut vertexOut [[stage_in]]) {
    return vertexOut.color;             
}

color computed in vertex shader

color computed in fragment shader

fragment float4 basic_fragment(VertexOut vertexOut [[stage_in]]) {
    return (vertexOut.position+1)/2;       
}

color computed in fragment shader

why are they rendered differently? what is value of vertex position passed to each fragment? It seems the all the fragments of a triangle receive the centroid of the triangle. how to compute position of each fragment in the fragment buffer?

--EDIT--

Divided position with window size in fragment shader; still the triangle is yellow.

ViewController.swift

override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let window = view.window
        if(window == nil) {return}
        let scale = view.window?.screen.nativeScale
        let layerSize = view.bounds.size
        //[640, 1136]
        frameSize = [UInt(layerSize.width * scale!), UInt(layerSize.height * scale!)]
        uniformBuffer = device.makeBuffer(bytes: frameSize, length: 2*MemoryLayout<UInt>.size, options: [])

    }
func render() {
    ...
    renderEncoder.setFragmentBuffer(uniformBuffer, offset: 0, index: 0)
    ...
}

shaders.metal

struct FrameSize{
    unsigned int x;
    unsigned int y;
};
fragment float4 basic_fragment(VertexOut vertexOut [[stage_in]],
                               const device FrameSize* frameSize [[ buffer(0) ]]
                               ) { // 1
    return float4(vertexOut.position.x/frameSize->x, vertexOut.position.y/frameSize->y, vertexOut.position.z, 1.0);              // 2
}
1

1 Answers

5
votes

The vertex positions go through the perspective division and viewport transform after they leave the vertex shader. That transforms them from clip coordinates (-1..1 in x,y, 0..1 in z) to framebuffer coordinates (x=0..width-1, y=0..height-1, z=0..1). The fragment shader receives interpolated positions in the framebuffer coordinate space.

Since the fragment position coordinates are in framebuffer space, almost all of the xy values will be greater then 1.0, so your color will just be maximum red, maximum green, and then whatever z value you've got. You probably want to re-normalize them, i.e. divide the position x value by the framebuffer width and position y by framebuffer height.