Search code examples
javascriptshaderwebgpuwgsl

z and w components of position vector act like they are switched in WGSL


I have the following WGSL shader that creates a triangle:

struct Vertex {
    @builtin(position) position : vec4<f32>,
    @location(0) color : vec4<f32>,
}

@vertex
fn vMain(@builtin(vertex_index) vertexIndex: u32) -> Vertex {
    var positions = array<vec4<f32>, 3> (
        vec4<f32>(0, 0.5, 0, 1),
        vec4<f32>(-0.5, -0.5, 0, 1),
        vec4<f32>(0.5, -0.5, 0, 1),
    );

    var output: Vertex;
    output.position = positions[vertexIndex];
    output.color = vec4<f32>(1, 1, 1, 1);

    return output;
}

@fragment
fn fMain(@location(0) color: vec4<f32>) -> @location(0) vec4<f32> {
    return color;
}

According to everything I've found the 4 component vectors should be treated as x, y, z, and w in that order. The problem is that when I change the w component, it acts as if I am changing the z; the vertices of the triangle look as if they are getting farther or nearer. And when I change the z, the triangle never changes shape but instead will be partially clipped sometimes.

Changing z of top vertex to 2: enter image description here

Changing w of left vertex to 2:enter image description here

I do not understand why this effect is happening.


Solution

  • W has a special meaning in the normal 3d transform pipeline. It is used to apply perspective to a model. While you are returning a 4d vector from the vertex shader, that is put through what is called a perspective divide, so in stead you should think that you are returning the 3d vector [x/w, y/w, z/w]. Note that this question is not webgpu specific and is the same through all 3d libraries.

    You can read more about this in various places: