Shadows: Using a shadow map

I am trying to render shadows in a Metal view. I am using two render passes. In the first pass the view is rendered into a 2d depth texture from the point of view of a point light source. This writes into the depth texture a shadow map of the minimum depths in the view . In the second pass the view is rendered to the viewport. In this second pass the vertex shader transposes the input position to the point light POV giving a shadowSpacePosition. In the fragment shader the depth of a point in the shadow map is compared to the depth of the shadowSpacePosition and the result is used to modify color of that point.

The first pass seems to be working as desired. The shadow map shown in a frame capture looks good. The problem I am having is that I don't know how the coordinates used in shadowSpacePosition relate the to coordinates in the shadow map. How do I transpose the x,y,z of the shadowPosition so that the comparison function gives the proper result?

cmp = shadowTexture.sample_compare(shadowSampler, shadowPosition.xy , shadowPosition.z);

The shadowPosition coordinates are in world space relative to the light position frustum with the origin at the light. I don't know what the above comparison function expects. Texture coordinates clamped 0 to 1?

Any insights would be appreciated. Below are the relevant shaders:

Code Block
// Vertex shader outputs and fragment shader inputs
typedef struct
{
float4 clipSpacePosition [[position]];
float4 eyeSpacePosition;
float4 shadowSpacePosition;
float2 textureCoordinate;
float4 normal;
float4 color;
} RasterizerData;
// *******************************************************
// ******** Shaders for Textured Models ************
// *******************************************************
// Vertex function
vertex RasterizerData vertexTexShader
(
uint vertexID [[vertex_id]],
constant HT_Vertex *vertices [[buffer(HT_Vertex_Index)]],
constant HT_Uniform *param [[buffer(HT_Uniform_Index)]]
)
{
RasterizerData out;
vector_float4 newPosition = float4( vertices[vertexID].position , 1 );
newPosition = param->nodeTransform[ vertices[vertexID].pickID ] * newPosition;
out.eyeSpacePosition = newPosition;
out.clipSpacePosition = param->perspectiveTransform * newPosition;
out.shadowSpacePosition = param->toLampPOV * newPosition;
out.shadowSpacePosition = param->shadowPerspective * newPosition;
// Apply rotations about the origin to the normals
vector_float4 newNormal = float4( vertices[vertexID].normal , 1);
out.normal = param->normalsTransform[vertices[vertexID].pickID] * newNormal;
out.textureCoordinate = vertices[vertexID].textCoor;
return out;
}
// Fragment function
fragment float4 fragmentTexShader
(
RasterizerData in [[stage_in]],
constant HT_Uniform *param [[buffer(HT_Uniform_Index)]],
texture2d<half> colorTexture [[ texture(HT_Texture_Index) ]],
depth2d<float , access::sample> shadowTexture [[ texture(HT_Shadow_Index)]],
sampler textureSampler [[sampler(HT_Texture_Index)]]
)
{
vector_float4 outColor;
vector_float3 lightDirection;
float lightDistance;
float attenuation;
vector_float3 halfVector;
float diffuse;
float specular;
float cmp;
float coorRatio;
vector_float3 shadowPosition;
vector_float3 scatteredLight;
vector_float3 reflectedLight;
vector_float3 rgb;
half4 colorSample;
bool isShadow;
constexpr sampler shadowSampler(coord::normalized,
filter::linear,
address::clamp_to_edge,
compare_func:: less);
shadowPosition = in.shadowSpacePosition.xyz;
cmp = shadowTexture.sample_compare(shadowSampler, shadowPosition.xy , shadowPosition.z);
if(cmp < 0.0001)
isShadow = true;
else
isShadow = false;
// Sample the texture to obtain a color
colorSample = colorTexture.sample(textureSampler, in.textureCoordinate);
// *********
// Apply Lighting Model
// *******
return outColor;
}
// *****************************************************
// ******** Shaders: Make Shadow Map ************
// *******************************************************
typedef struct
{
float4 clipSpacePosition [[position]];
} ShadowData;
// Vertex function
vertex ShadowData shadowVertexShader
(
uint vertexID [[vertex_id]],
constant HT_Vertex *vertices [[buffer(HT_Vertex_Index)]],
constant HT_Uniform *param [[buffer(HT_Uniform_Index)]]
)
{
ShadowData out;
vector_float4 newPosition = float4( vertices[vertexID].position , 1 );
newPosition = param->nodeTransform[ vertices[vertexID].pickID ] * newPosition;
newPosition = param->toLampPOV * newPosition;
newPosition = param->shadowPerspective * newPosition;
out.clipSpacePosition = newPosition;
return out;
}


Replies

After over a week of wandering around in the dark, I discovered how convert the coordinates. The input shadowSpacePosition is in Metal Normalized Coordinates: x and y -1 to 1, z 0 to 1 with the origin at the center of the drawable. When this is stored in the depth texture it is converted to texture coordinates: x,y and z 0 to 1 with the origin on the bottom-left. So to read the proper depth in the depth texture the code is:

Code Block
constexpr sampler shadowSampler(coord::normalized,
filter::linear,
address::clamp_to_zero,
compare_func:: less);
shadowPosition = in.shadowSpacePosition.xyz;
shadowPosition.y *= -1;
shadowPosition.xy += 1.0;
shadowPosition.xy /= 2;
cmp = shadowTexture.sample_compare(shadowSampler, shadowPosition.xy , shadowPosition.z);
if(cmp < 0.1)
isShadow = true;
else
isShadow = false;