Skip to content

Commit 49fe96e

Browse files
committed
Add deferredshadows rust shaders
1 parent e22e3d8 commit 49fe96e

File tree

13 files changed

+383
-2
lines changed

13 files changed

+383
-2
lines changed

shaders/rust/Cargo.lock

Lines changed: 21 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

shaders/rust/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ members = [
2929
"deferred/mrt",
3030
"deferredmultisampling/deferred",
3131
"deferredmultisampling/mrt",
32+
"deferredshadows/deferred",
33+
"deferredshadows/mrt",
34+
"deferredshadows/shadow",
3235
"descriptorbuffer/cube",
3336
"descriptorindexing",
3437
"descriptorsets/cube",
18.4 KB
Binary file not shown.
696 Bytes
Binary file not shown.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "deferredshadows-deferred"
3+
version = "0.1.0"
4+
edition.workspace = true
5+
6+
[dependencies]
7+
spirv-std = { workspace = true }
8+
9+
[lib]
10+
crate-type = ["dylib"]
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
#![cfg_attr(target_arch = "spirv", no_std)]
2+
3+
use spirv_std::{
4+
glam::{Mat4, Vec2, Vec3, Vec4, Vec4Swizzles},
5+
spirv, Image,
6+
image::SampledImage,
7+
num_traits::Float,
8+
};
9+
10+
const LIGHT_COUNT: usize = 3;
11+
const SHADOW_FACTOR: f32 = 0.25;
12+
const AMBIENT_LIGHT: f32 = 0.1;
13+
const USE_PCF: bool = true;
14+
const SHADOW_MAP_SIZE: f32 = 2048.0;
15+
16+
#[repr(C)]
17+
#[derive(Copy, Clone)]
18+
pub struct Light {
19+
pub position: Vec4,
20+
pub target: Vec4,
21+
pub color: Vec4,
22+
pub view_matrix: Mat4,
23+
}
24+
25+
#[repr(C)]
26+
#[derive(Copy, Clone)]
27+
pub struct UBO {
28+
pub view_pos: Vec4,
29+
pub lights: [Light; LIGHT_COUNT],
30+
pub use_shadows: i32,
31+
pub display_debug_target: i32,
32+
}
33+
34+
fn texture_proj(
35+
shadow_map: &SampledImage<Image!(2D, type=f32, sampled, arrayed)>,
36+
p: Vec4,
37+
layer: f32,
38+
offset: Vec2,
39+
) -> f32 {
40+
let mut shadow = 1.0;
41+
let shadow_coord = p / p.w;
42+
let shadow_coord_xy = shadow_coord.xy() * 0.5 + 0.5;
43+
44+
if shadow_coord.z > -1.0 && shadow_coord.z < 1.0 {
45+
let sample_coord = Vec3::new(shadow_coord_xy.x + offset.x, shadow_coord_xy.y + offset.y, layer);
46+
let dist = shadow_map.sample(sample_coord).x;
47+
if shadow_coord.w > 0.0 && dist < shadow_coord.z {
48+
shadow = SHADOW_FACTOR;
49+
}
50+
}
51+
shadow
52+
}
53+
54+
fn filter_pcf(
55+
shadow_map: &SampledImage<Image!(2D, type=f32, sampled, arrayed)>,
56+
sc: Vec4,
57+
layer: f32,
58+
) -> f32 {
59+
let scale = 1.5;
60+
let dx = scale * 1.0 / SHADOW_MAP_SIZE;
61+
let dy = scale * 1.0 / SHADOW_MAP_SIZE;
62+
63+
let mut shadow_factor = 0.0;
64+
let mut count = 0;
65+
let range = 1;
66+
67+
for x in -range..=range {
68+
for y in -range..=range {
69+
shadow_factor += texture_proj(
70+
shadow_map,
71+
sc,
72+
layer,
73+
Vec2::new(dx * x as f32, dy * y as f32),
74+
);
75+
count += 1;
76+
}
77+
}
78+
shadow_factor / count as f32
79+
}
80+
81+
fn shadow(
82+
frag_color: Vec3,
83+
frag_pos: Vec3,
84+
ubo: &UBO,
85+
shadow_map: &SampledImage<Image!(2D, type=f32, sampled, arrayed)>,
86+
) -> Vec3 {
87+
let mut result = frag_color;
88+
for i in 0..LIGHT_COUNT {
89+
let shadow_clip = ubo.lights[i].view_matrix * Vec4::new(frag_pos.x, frag_pos.y, frag_pos.z, 1.0);
90+
91+
let shadow_factor = if USE_PCF {
92+
filter_pcf(shadow_map, shadow_clip, i as f32)
93+
} else {
94+
texture_proj(shadow_map, shadow_clip, i as f32, Vec2::ZERO)
95+
};
96+
97+
result *= shadow_factor;
98+
}
99+
result
100+
}
101+
102+
#[spirv(vertex)]
103+
pub fn main_vs(
104+
#[spirv(vertex_index)] vertex_index: u32,
105+
#[spirv(position)] out_position: &mut Vec4,
106+
out_uv: &mut Vec2,
107+
) {
108+
let uv = Vec2::new(
109+
((vertex_index << 1) & 2) as f32,
110+
(vertex_index & 2) as f32,
111+
);
112+
*out_uv = uv;
113+
*out_position = Vec4::new(uv.x * 2.0 - 1.0, uv.y * 2.0 - 1.0, 0.0, 1.0);
114+
}
115+
116+
#[spirv(fragment)]
117+
pub fn main_fs(
118+
in_uv: Vec2,
119+
#[spirv(descriptor_set = 0, binding = 1)] position_sampler: &SampledImage<Image!(2D, type=f32, sampled)>,
120+
#[spirv(descriptor_set = 0, binding = 2)] normal_sampler: &SampledImage<Image!(2D, type=f32, sampled)>,
121+
#[spirv(descriptor_set = 0, binding = 3)] albedo_sampler: &SampledImage<Image!(2D, type=f32, sampled)>,
122+
#[spirv(uniform, descriptor_set = 0, binding = 4)] ubo: &UBO,
123+
#[spirv(descriptor_set = 0, binding = 5)] shadow_map: &SampledImage<Image!(2D, type=f32, sampled, arrayed)>,
124+
out_frag_color: &mut Vec4,
125+
) {
126+
// Get G-Buffer values
127+
let frag_pos = position_sampler.sample(in_uv).xyz();
128+
let normal = normal_sampler.sample(in_uv).xyz();
129+
let albedo = albedo_sampler.sample(in_uv);
130+
131+
let mut frag_color;
132+
133+
// Debug display
134+
if ubo.display_debug_target > 0 {
135+
frag_color = match ubo.display_debug_target {
136+
1 => shadow(Vec3::ONE, frag_pos, ubo, shadow_map),
137+
2 => frag_pos,
138+
3 => normal,
139+
4 => albedo.xyz(),
140+
5 => Vec3::splat(albedo.w),
141+
_ => Vec3::ZERO,
142+
};
143+
*out_frag_color = Vec4::new(frag_color.x, frag_color.y, frag_color.z, 1.0);
144+
return;
145+
}
146+
147+
// Ambient part
148+
frag_color = albedo.xyz() * AMBIENT_LIGHT;
149+
150+
let n = normal.normalize();
151+
152+
for i in 0..LIGHT_COUNT {
153+
// Vector to light
154+
let mut l = ubo.lights[i].position.xyz() - frag_pos;
155+
let dist = l.length();
156+
l = l.normalize();
157+
158+
// Viewer to fragment
159+
let v = (ubo.view_pos.xyz() - frag_pos).normalize();
160+
161+
let light_cos_inner_angle = 15.0f32.to_radians().cos();
162+
let light_cos_outer_angle = 25.0f32.to_radians().cos();
163+
let light_range = 100.0;
164+
165+
// Direction vector from source to target
166+
let dir = (ubo.lights[i].position.xyz() - ubo.lights[i].target.xyz()).normalize();
167+
168+
// Dual cone spot light with smooth transition between inner and outer angle
169+
let cos_dir = l.dot(dir);
170+
let spot_effect = smoothstep(light_cos_outer_angle, light_cos_inner_angle, cos_dir);
171+
let height_attenuation = smoothstep(light_range, 0.0, dist);
172+
173+
// Diffuse lighting
174+
let ndot_l = n.dot(l).max(0.0);
175+
let diff = Vec3::splat(ndot_l);
176+
177+
// Specular lighting
178+
let r = reflect(-l, n);
179+
let ndot_r = r.dot(v).max(0.0);
180+
let spec = Vec3::splat(ndot_r.powf(16.0) * albedo.w * 2.5);
181+
182+
frag_color += (diff + spec) * spot_effect * height_attenuation * ubo.lights[i].color.xyz() * albedo.xyz();
183+
}
184+
185+
// Shadow calculations in a separate pass
186+
if ubo.use_shadows > 0 {
187+
frag_color = shadow(frag_color, frag_pos, ubo, shadow_map);
188+
}
189+
190+
*out_frag_color = Vec4::new(frag_color.x, frag_color.y, frag_color.z, 1.0);
191+
}
192+
193+
fn smoothstep(edge0: f32, edge1: f32, x: f32) -> f32 {
194+
let t = ((x - edge0) / (edge1 - edge0)).clamp(0.0, 1.0);
195+
t * t * (3.0 - 2.0 * t)
196+
}
197+
198+
fn reflect(i: Vec3, n: Vec3) -> Vec3 {
199+
i - 2.0 * n.dot(i) * n
200+
}
2.34 KB
Binary file not shown.
8.88 KB
Binary file not shown.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "deferredshadows-mrt"
3+
version = "0.1.0"
4+
edition.workspace = true
5+
6+
[dependencies]
7+
spirv-std = { workspace = true }
8+
9+
[lib]
10+
crate-type = ["dylib"]
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#![cfg_attr(target_arch = "spirv", no_std)]
2+
3+
use spirv_std::{
4+
glam::{Mat4, Vec2, Vec3, Vec4, Vec4Swizzles},
5+
spirv, Image,
6+
image::SampledImage,
7+
};
8+
9+
#[repr(C)]
10+
#[derive(Copy, Clone)]
11+
pub struct UBO {
12+
pub projection: Mat4,
13+
pub model: Mat4,
14+
pub view: Mat4,
15+
pub instance_pos: [Vec4; 3],
16+
}
17+
18+
#[spirv(vertex)]
19+
pub fn main_vs(
20+
in_pos: Vec4,
21+
in_uv: Vec2,
22+
in_color: Vec3,
23+
in_normal: Vec3,
24+
in_tangent: Vec3,
25+
#[spirv(uniform, descriptor_set = 0, binding = 0)] ubo: &UBO,
26+
#[spirv(instance_index)] instance_index: u32,
27+
#[spirv(position)] out_position: &mut Vec4,
28+
out_normal: &mut Vec3,
29+
out_uv: &mut Vec2,
30+
out_color: &mut Vec3,
31+
out_world_pos: &mut Vec3,
32+
out_tangent: &mut Vec3,
33+
) {
34+
let tmp_pos = in_pos + ubo.instance_pos[instance_index as usize];
35+
36+
*out_position = ubo.projection * ubo.view * ubo.model * tmp_pos;
37+
38+
*out_uv = in_uv;
39+
40+
// Vertex position in world space
41+
*out_world_pos = (ubo.model * tmp_pos).xyz();
42+
43+
// Normal in world space
44+
*out_normal = in_normal.normalize();
45+
*out_tangent = in_tangent.normalize();
46+
47+
// Currently just vertex color
48+
*out_color = in_color;
49+
}
50+
51+
#[spirv(fragment)]
52+
pub fn main_fs(
53+
in_normal: Vec3,
54+
in_uv: Vec2,
55+
_in_color: Vec3,
56+
in_world_pos: Vec3,
57+
in_tangent: Vec3,
58+
#[spirv(descriptor_set = 0, binding = 1)] sampler_color: &SampledImage<Image!(2D, type=f32, sampled)>,
59+
#[spirv(descriptor_set = 0, binding = 2)] sampler_normal_map: &SampledImage<Image!(2D, type=f32, sampled)>,
60+
out_position: &mut Vec4,
61+
out_normal: &mut Vec4,
62+
out_albedo: &mut Vec4,
63+
) {
64+
*out_position = Vec4::new(in_world_pos.x, in_world_pos.y, in_world_pos.z, 1.0);
65+
66+
// Calculate normal in tangent space
67+
let n = in_normal.normalize();
68+
let t = in_tangent.normalize();
69+
let b = n.cross(t);
70+
let tnorm = (sampler_normal_map.sample(in_uv).xyz() * 2.0 - Vec3::ONE).normalize();
71+
// TBN matrix multiplication - transforms from tangent space to world space
72+
let tnorm = t * tnorm.x + b * tnorm.y + n * tnorm.z;
73+
*out_normal = Vec4::new(tnorm.x, tnorm.y, tnorm.z, 1.0);
74+
75+
*out_albedo = sampler_color.sample(in_uv);
76+
}

0 commit comments

Comments
 (0)