I'm having a lot of trouble with shadow mapping, and I believe I've found the problem. When passing vectors from the vertex shader to the pixel shader, does the hardware automatically change any of the values based on the semantic?
I've compiled a barebones pair of shaders which should illustrate the problem.
Vertex shader :
struct Vertex {
float3 position : POSITION;
};
struct Pixel {
float4 position : SV_Position;
float4 light_position : POSITION;
};
cbuffer Matrices {
matrix projection;
};
Pixel RenderVertexShader(Vertex input) {
Pixel output;
output.position = mul(float4(input.position, 1.0f), projection);
output.light_position = output.position;
// We simply pass the same vector in screenspace through different semantics.
return output;
}
And a simple pixel shader to go along with it:
struct Pixel {
float4 position : SV_Position;
float4 light_position : POSITION;
};
float4 RenderPixelShader(Pixel input) : SV_Target {
// At this point, (input.position.z / input.position.w) is a normal depth value.
// However, (input.light_position.z / input.light_position.w) is 0.999f or similar.
// If the primitive is touching the near plane, it very quickly goes to 0.
return (0.0f).rrrr;
}
How is it possible to make the hardware treat light_position in the same way which position is being treated between the vertex and pixel shaders?
This differrence is almost exclusive to the Z component, as the X and Y values of light_position are behaving as expected.
EDIT: Aha! (input.position.z)
without dividing by W is the same as (input.light_position.z / input.light_position.w)
. Not sure why this is.
EDIT 2: Dividing (output.light_position) by (output.light_position.w) in the vertex shader causes really odd behavior, where (input.light_position.z / input.light_position.w) will be either 0 or 1 based on the camera's position and angle.
EDIT 3: Moving the projection multiplication into the pixel shader doesn't seem to make any difference. This means that the hardware is doing something to the SV_Position semantic which is critical to make it work.
EDIT 4: (input.light_position.z / input.light_position.w) IS behaving like depth, it's just that the precision seems to have decreased unbelievably. Only does it approach 0 once it's primitive literally intersects the near plane.
xyzw
(instead ofxyz
) byw
, it will not matter if the hardware divides byw
again... becausew
will be 1.0. Not sure how relevant that is here, but that is one reason you should divide the enitre vector byw
, not just the first 3-components. That said,w
is usually assigned1/w
when the hardware does this division (clip-space -> NDC) so that you can reverse the transformation. \$\endgroup\$