smehsu
January 27, 2019, 8:30pm
1
Hi,
I’m really enjoying the book! In chapter 14 (pg. 392/ 393) we create a shadow map using an ortho projection matrix. In the description, the book mentions " If instead you used a spotlight, for example, you would use the perspective projection matrix." I’m trying to accomplish this however I’m having a hard time getting the map to work with perspective projection. Do you or anyone have a working example I could reference? Thanks!
mhorga
January 27, 2019, 9:08pm
2
@smehsu we’re happy you’re enjoying the book!
unfortunately, I haven’t seen implementations using the perspective matrix… mostly because when we think of shadows they are most of the time caused by a directional light source such as the sun.
smehsu
February 3, 2019, 4:18pm
3
I managed to figure out spotlights. I’m still trying to figure out point lights.
let aspect = Float(view.bounds.width) / Float(view.bounds.height)
scene.uniforms.projectionMatrix = float4x4(projectionFov: radians(fromDegrees: 70), near: 0.01, far: 16, aspect: aspect)
let position: float3 = [-sunlight.position.x, -sunlight.position.y, -sunlight.position.z]
let center: float3 = [0, 0, 0]
let lookAt = float4x4(eye: position, center: position - sunlight.coneDirection, up: [0,1,0])
scene.uniforms.viewMatrix = float4x4(translation: [0, 0, 7]) * lookAt
scene.uniforms.shadowMatrix = scene.uniforms.projectionMatrix * scene.uniforms.viewMatrix
Shadow shader vertex
matrix_float4x4 mvp = uniforms.projectionMatrix * uniforms.viewMatrix * uniforms.modelMatrix;
float4 position = mvp * vertexIn.position;
float4 worldPosition = uniforms.modelMatrix * vertexIn.position;
float3 directionFromLightToFragment = normalize(light.position - worldPosition.xyz);
if (light.type == Spotlight) {
float3 tConeDirection = light.coneDirection;
float3 coneDirection = normalize(-tConeDirection);
float spotResult = dot(directionFromLightToFragment, coneDirection);
float coneAngle = cos(light.coneAngle);
if (spotResult < coneAngle) {
position = position.xyww;
}
}
Main Shader
float2 xy = in.shadowPosition.xy / in.shadowPosition.w;
xy = xy * 0.5 + 0.5;
xy.y = 1 - xy.y;
constexpr sampler s(coord::normalized, filter::linear, address::clamp_to_edge, compare_func:: less);
float shadow_sample = shadowTexture.sample(s, xy);
float current_sample = in.shadowPosition.z / in.shadowPosition.w;
if (current_sample > shadow_sample ) {
color *= 0.5;
}
1 Like
mhorga
February 3, 2019, 4:57pm
4
that’s great! let us know about your progress on it.
smehsu
March 16, 2019, 3:06pm
5
Pointlight / omni directional shadow maps were significantly more work. Here are the two resources I used:
https://developer.apple.com/documentation/metal/reflections_with_layer_selection
It seems the shadow projection matrix needs to have an aspect ratio of 1 instead of using the views width / height.
mhorga
March 16, 2019, 3:57pm
6
@smehsu that’s good to hear. thanks for sharing