1
+ #![ cfg_attr( target_arch = "spirv" , no_std) ]
2
+ #![ allow( clippy:: missing_safety_doc) ]
3
+
4
+ use spirv_std:: { spirv, glam:: { vec3, vec4, Mat3 , Mat4 , Vec3 , Vec4 , Vec4Swizzles } , num_traits:: Float } ;
5
+ use core:: f32:: consts:: PI ;
6
+
7
+ #[ repr( C ) ]
8
+ #[ derive( Copy , Clone ) ]
9
+ pub struct Ubo {
10
+ pub projection : Mat4 ,
11
+ pub model : Mat4 ,
12
+ pub view : Mat4 ,
13
+ pub cam_pos : Vec3 ,
14
+ }
15
+
16
+ #[ repr( C ) ]
17
+ #[ derive( Copy , Clone ) ]
18
+ pub struct UboShared {
19
+ pub lights : [ Vec4 ; 4 ] ,
20
+ }
21
+
22
+ #[ repr( C ) ]
23
+ #[ derive( Copy , Clone ) ]
24
+ pub struct VertexPushConsts {
25
+ pub obj_pos : Vec3 ,
26
+ }
27
+
28
+ #[ repr( C ) ]
29
+ #[ derive( Copy , Clone ) ]
30
+ pub struct FragmentPushConsts {
31
+ pub _padding : [ f32 ; 3 ] , // offset 0-11 to match GLSL offset 12
32
+ pub roughness : f32 , // offset 12
33
+ pub metallic : f32 , // offset 16
34
+ pub r : f32 , // offset 20
35
+ pub g : f32 , // offset 24
36
+ pub b : f32 , // offset 28
37
+ }
38
+
39
+ #[ spirv( vertex) ]
40
+ pub fn main_vs (
41
+ in_pos : Vec3 ,
42
+ in_normal : Vec3 ,
43
+ #[ spirv( uniform, descriptor_set = 0 , binding = 0 ) ] ubo : & Ubo ,
44
+ #[ spirv( push_constant) ] push_consts : & VertexPushConsts ,
45
+ #[ spirv( position) ] out_position : & mut Vec4 ,
46
+ out_world_pos : & mut Vec3 ,
47
+ out_normal : & mut Vec3 ,
48
+ ) {
49
+ let loc_pos = ( ubo. model * vec4 ( in_pos. x , in_pos. y , in_pos. z , 1.0 ) ) . xyz ( ) ;
50
+ * out_world_pos = loc_pos + push_consts. obj_pos ;
51
+ * out_normal = Mat3 :: from_mat4 ( ubo. model ) * in_normal;
52
+ * out_position = ubo. projection * ubo. view * vec4 ( out_world_pos. x , out_world_pos. y , out_world_pos. z , 1.0 ) ;
53
+ }
54
+
55
+ fn material_color ( material : & FragmentPushConsts ) -> Vec3 {
56
+ vec3 ( material. r , material. g , material. b )
57
+ }
58
+
59
+ // Normal Distribution function
60
+ fn d_ggx ( dot_nh : f32 , roughness : f32 ) -> f32 {
61
+ let alpha = roughness * roughness;
62
+ let alpha2 = alpha * alpha;
63
+ let denom = dot_nh * dot_nh * ( alpha2 - 1.0 ) + 1.0 ;
64
+ alpha2 / ( PI * denom * denom)
65
+ }
66
+
67
+ // Geometric Shadowing function
68
+ fn g_schlicksmith_ggx ( dot_nl : f32 , dot_nv : f32 , roughness : f32 ) -> f32 {
69
+ let r = roughness + 1.0 ;
70
+ let k = ( r * r) / 8.0 ;
71
+ let gl = dot_nl / ( dot_nl * ( 1.0 - k) + k) ;
72
+ let gv = dot_nv / ( dot_nv * ( 1.0 - k) + k) ;
73
+ gl * gv
74
+ }
75
+
76
+ // Fresnel function
77
+ fn f_schlick ( cos_theta : f32 , metallic : f32 , material : & FragmentPushConsts ) -> Vec3 {
78
+ let f0 = vec3 ( 0.04 , 0.04 , 0.04 ) . lerp ( material_color ( material) , metallic) ;
79
+ f0 + ( vec3 ( 1.0 , 1.0 , 1.0 ) - f0) * ( 1.0 - cos_theta) . powf ( 5.0 )
80
+ }
81
+
82
+ // Specular BRDF composition
83
+ fn brdf ( l : Vec3 , v : Vec3 , n : Vec3 , metallic : f32 , roughness : f32 , material : & FragmentPushConsts ) -> Vec3 {
84
+ // Precalculate vectors and dot products
85
+ let h = ( v + l) . normalize ( ) ;
86
+ let dot_nv = n. dot ( v) . clamp ( 0.0 , 1.0 ) ;
87
+ let dot_nl = n. dot ( l) . clamp ( 0.0 , 1.0 ) ;
88
+ let _dot_lh = l. dot ( h) . clamp ( 0.0 , 1.0 ) ;
89
+ let dot_nh = n. dot ( h) . clamp ( 0.0 , 1.0 ) ;
90
+
91
+ // Light color fixed
92
+ let light_color = vec3 ( 1.0 , 1.0 , 1.0 ) ;
93
+
94
+ let mut color = vec3 ( 0.0 , 0.0 , 0.0 ) ;
95
+
96
+ if dot_nl > 0.0 {
97
+ let rroughness = roughness. max ( 0.05 ) ;
98
+ // D = Normal distribution (Distribution of the microfacets)
99
+ let d = d_ggx ( dot_nh, roughness) ;
100
+ // G = Geometric shadowing term (Microfacets shadowing)
101
+ let g = g_schlicksmith_ggx ( dot_nl, dot_nv, rroughness) ;
102
+ // F = Fresnel factor (Reflectance depending on angle of incidence)
103
+ let f = f_schlick ( dot_nv, metallic, material) ;
104
+
105
+ let spec = d * f * g / ( 4.0 * dot_nl * dot_nv) ;
106
+
107
+ color += spec * dot_nl * light_color;
108
+ }
109
+
110
+ color
111
+ }
112
+
113
+ #[ spirv( fragment) ]
114
+ pub fn main_fs (
115
+ in_world_pos : Vec3 ,
116
+ in_normal : Vec3 ,
117
+ #[ spirv( uniform, descriptor_set = 0 , binding = 0 ) ] ubo : & Ubo ,
118
+ #[ spirv( uniform, descriptor_set = 0 , binding = 1 ) ] ubo_params : & UboShared ,
119
+ #[ spirv( push_constant) ] material : & FragmentPushConsts ,
120
+ out_color : & mut Vec4 ,
121
+ ) {
122
+ let n = in_normal. normalize ( ) ;
123
+ let v = ( ubo. cam_pos - in_world_pos) . normalize ( ) ;
124
+
125
+ let roughness = material. roughness ;
126
+
127
+ // Specular contribution
128
+ let mut lo = vec3 ( 0.0 , 0.0 , 0.0 ) ;
129
+ for i in 0 ..4 {
130
+ let l = ( ubo_params. lights [ i] . xyz ( ) - in_world_pos) . normalize ( ) ;
131
+ lo += brdf ( l, v, n, material. metallic , roughness, material) ;
132
+ }
133
+
134
+ // Combine with ambient
135
+ let mut color = material_color ( material) * 0.02 ;
136
+ color += lo;
137
+
138
+ // Gamma correct
139
+ color = color. powf ( 0.4545 ) ;
140
+
141
+ * out_color = vec4 ( color. x , color. y , color. z , 1.0 ) ;
142
+ }
0 commit comments