diff options
Diffstat (limited to 'shaders/model.frag.slang')
| -rw-r--r-- | shaders/model.frag.slang | 65 |
1 files changed, 56 insertions, 9 deletions
diff --git a/shaders/model.frag.slang b/shaders/model.frag.slang index 4684db0..ac12d0e 100644 --- a/shaders/model.frag.slang +++ b/shaders/model.frag.slang @@ -16,10 +16,29 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +struct light_uniforms { + float3 direction; + float shadow_texel_size; + float shadow_minimum_bias; + float shadow_maximum_bias; + float shadow_strength; + float padding; +}; + +[[vk::binding(0, 3)]] +ConstantBuffer<light_uniforms> light : register(b0, space3); + +[[vk::binding(0, 2)]] +Texture2D<float> shadow_map : register(t0, space2); + +[[vk::binding(1, 2)]] +SamplerComparisonState shadow_sampler : register(s0, space2); + struct FSInput { - float3 normal : TEXCOORD0; - float3 world_position : TEXCOORD1; - float3 camera_position : TEXCOORD2; + float3 normal : TEXCOORD0; + float3 world_position : TEXCOORD1; + float3 camera_position : TEXCOORD2; + float4 light_space_position : TEXCOORD3; }; struct FSOutput { @@ -30,17 +49,45 @@ static const float ambient_strength = 0.22; static const float diffuse_strength = 0.78; static const float specular_strength = 0.6; static const float shininess = 32.0; +static const int shadow_pcf_radius = 1; + +float shadow_visibility(float4 light_space_position, float normal_dot_light) { + float3 projected = light_space_position.xyz / light_space_position.w; + if (projected.z < 0.0 || projected.z > 1.0) { + return 1.0; + } + + float2 uv = float2(0.5 + 0.5 * projected.x, 0.5 - 0.5 * projected.y); + if (any(uv < 0.0) || any(uv > 1.0)) { + return 1.0; + } + + float bias = max(light.shadow_maximum_bias * (1.0 - normal_dot_light), light.shadow_minimum_bias); + float reference = projected.z - bias; + float visibility = 0.0; + float taps = 0.0; + for (int y = -shadow_pcf_radius; y <= shadow_pcf_radius; y++) { + for (int x = -shadow_pcf_radius; x <= shadow_pcf_radius; x++) { + float2 offset = float2(x, y) * light.shadow_texel_size; + visibility += shadow_map.SampleCmpLevelZero(shadow_sampler, uv + offset, reference); + taps += 1.0; + } + } + + return visibility / taps; +} FSOutput main(FSInput input) { - float3 light_direction = normalize(float3(-0.45, 0.75, 0.55)); + float3 light_direction = normalize(light.direction); float3 normal = normalize(input.normal); float3 view_direction = normalize(input.camera_position - input.world_position); float3 halfway_direction = normalize(light_direction + view_direction); - - float diffuse = max(dot(normal, light_direction), 0.0); - float specular = diffuse > 0.0 ? pow(max(dot(normal, halfway_direction), 0.0), shininess) : 0.0; - - float shade = saturate(ambient_strength + diffuse * diffuse_strength + specular * specular_strength); + float normal_dot_light = dot(normal, light_direction); + float diffuse = max(normal_dot_light, 0.0); + float specular = diffuse > 0.0 ? pow(max(dot(normal, halfway_direction), 0.0), shininess) : 0.0; + float visibility = shadow_visibility(input.light_space_position, normal_dot_light); + visibility = lerp(1.0, visibility, light.shadow_strength); + float shade = saturate(ambient_strength + visibility * (diffuse * diffuse_strength + specular * specular_strength)); FSOutput output; output.out_color = float4(shade, shade, shade, 1.0); return output; |