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