diff options
Diffstat (limited to 'shaders')
| -rw-r--r-- | shaders/model.frag.slang | 65 | ||||
| -rw-r--r-- | shaders/model.vert.slang | 25 | ||||
| -rw-r--r-- | shaders/model_wireframe.frag.slang | 2 | ||||
| -rw-r--r-- | shaders/shadow.frag.slang | 20 | ||||
| -rw-r--r-- | shaders/shadow.vert.slang | 38 |
5 files changed, 129 insertions, 21 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; diff --git a/shaders/model.vert.slang b/shaders/model.vert.slang index 380ff37..3ddb39d 100644 --- a/shaders/model.vert.slang +++ b/shaders/model.vert.slang @@ -19,8 +19,9 @@ struct model_uniforms { float4x4 mvp; float4x4 model; + float4x4 light_view_projection; float3 camera_position; - float padding; + float shadow_normal_offset; }; [[vk::binding(0, 1)]] @@ -32,18 +33,22 @@ struct VSInput { }; struct VSOutput { - float4 position : SV_Position; - float3 normal : TEXCOORD0; - float3 world_position : TEXCOORD1; - float3 camera_position : TEXCOORD2; + float4 position : SV_Position; + float3 normal : TEXCOORD0; + float3 world_position : TEXCOORD1; + float3 camera_position : TEXCOORD2; + float4 light_space_position : TEXCOORD3; }; VSOutput main(VSInput input) { VSOutput output; - float4 world_position = mul(uniforms.model, float4(input.position, 1.0)); - output.position = mul(uniforms.mvp, float4(input.position, 1.0)); - output.normal = normalize(mul((float3x3)uniforms.model, input.normal)); - output.world_position = world_position.xyz; - output.camera_position = uniforms.camera_position; + float4 world_position = mul(uniforms.model, float4(input.position, 1.0)); + float3 world_normal = normalize(mul((float3x3)uniforms.model, input.normal)); + output.position = mul(uniforms.mvp, float4(input.position, 1.0)); + output.normal = world_normal; + output.world_position = world_position.xyz; + output.camera_position = uniforms.camera_position; + float3 offset_position = world_position.xyz + world_normal * uniforms.shadow_normal_offset; + output.light_space_position = mul(uniforms.light_view_projection, float4(offset_position, 1.0)); return output; }
\ No newline at end of file diff --git a/shaders/model_wireframe.frag.slang b/shaders/model_wireframe.frag.slang index bac0966..10c2a52 100644 --- a/shaders/model_wireframe.frag.slang +++ b/shaders/model_wireframe.frag.slang @@ -20,8 +20,6 @@ struct FSOutput { float4 out_color : SV_Target0; }; -// Ignores geometry normals/lighting entirely; used with the LINE fill mode -// pipeline so only the mesh edges are drawn, in pure white. FSOutput main() { FSOutput output; output.out_color = float4(1.0, 1.0, 1.0, 1.0); diff --git a/shaders/shadow.frag.slang b/shaders/shadow.frag.slang new file mode 100644 index 0000000..4f895f1 --- /dev/null +++ b/shaders/shadow.frag.slang @@ -0,0 +1,20 @@ +/* + * Tuxánci 2 - A first person shooter + * Copyright (C) 2025-2026 Connor Thomson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +void main() { +}
\ No newline at end of file diff --git a/shaders/shadow.vert.slang b/shaders/shadow.vert.slang new file mode 100644 index 0000000..1e56f46 --- /dev/null +++ b/shaders/shadow.vert.slang @@ -0,0 +1,38 @@ +/* + * Tuxánci 2 - A first person shooter + * Copyright (C) 2025-2026 Connor Thomson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +struct shadow_uniforms { + float4x4 light_model_view_projection; +}; + +[[vk::binding(0, 1)]] +ConstantBuffer<shadow_uniforms> uniforms : register(b0, space1); + +struct VSInput { + float3 position : POSITION0; +}; + +struct VSOutput { + float4 position : SV_Position; +}; + +VSOutput main(VSInput input) { + VSOutput output; + output.position = mul(uniforms.light_model_view_projection, float4(input.position, 1.0)); + return output; +}
\ No newline at end of file |