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 ! ( 2 D , 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 ! ( 2 D , 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 ! ( 2 D , 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 ! ( 2 D , type =f32 , sampled) > ,
120+ #[ spirv( descriptor_set = 0 , binding = 2 ) ] normal_sampler : & SampledImage < Image ! ( 2 D , type =f32 , sampled) > ,
121+ #[ spirv( descriptor_set = 0 , binding = 3 ) ] albedo_sampler : & SampledImage < Image ! ( 2 D , type =f32 , sampled) > ,
122+ #[ spirv( uniform, descriptor_set = 0 , binding = 4 ) ] ubo : & UBO ,
123+ #[ spirv( descriptor_set = 0 , binding = 5 ) ] shadow_map : & SampledImage < Image ! ( 2 D , 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+ }
0 commit comments