1use crabslab::{Id, Slab};
3use glam::{Mat4, Vec2, Vec3, Vec4, Vec4Swizzles};
4
5#[allow(unused)]
6use spirv_std::num_traits::{Float, Zero};
7
8use crate::{
9 atlas::shader::AtlasTextureDescriptor,
10 geometry::shader::GeometryDescriptor,
11 light::shader::{
12 Candela, DirectionalLightDescriptor, LightStyle, LightingDescriptor, PointLightDescriptor,
13 ShadowCalculation, SpotLightCalculation,
14 },
15 material::shader::MaterialDescriptor,
16 math::{self, IsSampler, IsVector, Sample2d, Sample2dArray, SampleCube},
17 primitive::shader::PrimitiveDescriptor,
18 println as my_println,
19};
20
21use super::{brdf, debug};
22
23pub fn normal_distribution_ggx(n: Vec3, h: Vec3, roughness: f32) -> f32 {
28 let a = roughness * roughness;
29 let a2 = a * a;
30 let ndot_h = n.dot(h).max(0.0);
31 let ndot_h2 = ndot_h * ndot_h;
32
33 let num = a2;
34 let denom = (ndot_h2 * (a2 - 1.0) + 1.0).powf(2.0) * core::f32::consts::PI;
35
36 num / denom
37}
38
39fn geometry_schlick_ggx(ndot_v: f32, roughness: f32) -> f32 {
40 let r = roughness + 1.0;
41 let k = (r * r) / 8.0;
42 let num = ndot_v;
43 let denom = ndot_v * (1.0 - k) + k;
44
45 num / denom
46}
47
48fn geometry_smith(n: Vec3, v: Vec3, l: Vec3, roughness: f32) -> f32 {
52 let ndot_v = n.dot(v).max(0.0);
53 let ndot_l = n.dot(l).max(0.0);
54 let ggx1 = geometry_schlick_ggx(ndot_v, roughness);
55 let ggx2 = geometry_schlick_ggx(ndot_l, roughness);
56
57 ggx1 * ggx2
58}
59
60fn fresnel_schlick(
69 cos_theta: f32,
71 f0: Vec3,
74) -> Vec3 {
75 f0 + (1.0 - f0) * (1.0 - cos_theta).clamp(0.0, 1.0).powf(5.0)
76}
77
78fn fresnel_schlick_roughness(cos_theta: f32, f0: Vec3, roughness: f32) -> Vec3 {
79 f0 + (Vec3::splat(1.0 - roughness).max(f0) - f0) * (1.0 - cos_theta).clamp(0.0, 1.0).powf(5.0)
80}
81
82#[allow(clippy::too_many_arguments)]
83pub fn outgoing_radiance(
84 light_color: Vec4,
85 albedo: Vec3,
86 attenuation: f32,
87 v: Vec3,
88 l: Vec3,
89 n: Vec3,
90 metalness: f32,
91 roughness: f32,
92) -> Vec3 {
93 my_println!("outgoing_radiance");
94 my_println!(" light_color: {light_color:?}");
95 my_println!(" albedo: {albedo:?}");
96 my_println!(" attenuation: {attenuation:?}");
97 my_println!(" v: {v:?}");
98 my_println!(" l: {l:?}");
99 my_println!(" n: {n:?}");
100 my_println!(" metalness: {metalness:?}");
101 my_println!(" roughness: {roughness:?}");
102
103 let f0 = Vec3::splat(0.4).lerp(albedo, metalness);
104 my_println!(" f0: {f0:?}");
105 let radiance = light_color.xyz() * attenuation;
106 my_println!(" radiance: {radiance:?}");
107 let h = (v + l).alt_norm_or_zero();
108 my_println!(" h: {h:?}");
109 let ndf: f32 = normal_distribution_ggx(n, h, roughness);
111 my_println!(" ndf: {ndf:?}");
112 let g: f32 = geometry_smith(n, v, l, roughness);
113 my_println!(" g: {g:?}");
114 let f: Vec3 = fresnel_schlick(h.dot(v).max(0.0), f0);
115 my_println!(" f: {f:?}");
116
117 let k_s = f;
118 let k_d = (Vec3::splat(1.0) - k_s) * (1.0 - metalness);
119 my_println!(" k_s: {k_s:?}");
120
121 let numerator: Vec3 = ndf * g * f;
122 my_println!(" numerator: {numerator:?}");
123 let n_dot_l = n.dot(l).max(0.0);
124 my_println!(" n_dot_l: {n_dot_l:?}");
125 let denominator: f32 = 4.0 * n.dot(v).max(0.0) * n_dot_l + 0.0001;
126 my_println!(" denominator: {denominator:?}");
127 let specular: Vec3 = numerator / denominator;
128 my_println!(" specular: {specular:?}");
129
130 (k_d * albedo / core::f32::consts::PI + specular) * radiance * n_dot_l
131}
132
133pub fn sample_irradiance<T: SampleCube<Sampler = S>, S: IsSampler>(
134 irradiance: &T,
135 irradiance_sampler: &S,
136 n: Vec3,
138) -> Vec3 {
139 irradiance.sample_by_lod(*irradiance_sampler, n, 0.0).xyz()
140}
141
142pub fn sample_specular_reflection<T: SampleCube<Sampler = S>, S: IsSampler>(
143 prefiltered: &T,
144 prefiltered_sampler: &S,
145 camera_pos: Vec3,
147 in_pos: Vec3,
149 n: Vec3,
151 roughness: f32,
152) -> Vec3 {
153 let v = (camera_pos - in_pos).alt_norm_or_zero();
154 let reflect_dir = math::reflect(-v, n);
155 prefiltered
156 .sample_by_lod(*prefiltered_sampler, reflect_dir, roughness * 4.0)
157 .xyz()
158}
159
160pub fn get_material(
162 material_id: Id<MaterialDescriptor>,
163 has_lighting: bool,
164 material_slab: &[u32],
165) -> MaterialDescriptor {
166 if material_id.is_none() {
167 MaterialDescriptor {
171 has_lighting: false,
172 ..Default::default()
173 }
174 } else {
175 let mut material = material_slab.read_unchecked(material_id);
176 material.has_lighting &= has_lighting;
177 material
178 }
179}
180
181pub fn texture_color<A: Sample2dArray<Sampler = S>, S: IsSampler>(
182 texture_id: Id<AtlasTextureDescriptor>,
183 uv: Vec2,
184 atlas: &A,
185 sampler: &S,
186 atlas_size: glam::UVec2,
187 material_slab: &[u32],
188) -> Vec4 {
189 let texture = material_slab.read(texture_id);
190 let uv = texture.uv(uv, atlas_size);
192 crate::println!("uv: {uv}");
193 let mut color: Vec4 = atlas.sample_by_lod(*sampler, uv, 0.0);
194 if texture_id.is_none() {
195 color = Vec4::splat(1.0);
196 }
197 color
198}
199
200#[allow(clippy::too_many_arguments)]
202pub fn fragment_impl<A, T, DtA, C, S>(
203 atlas: &A,
204 atlas_sampler: &S,
205 irradiance: &C,
206 irradiance_sampler: &S,
207 prefiltered: &C,
208 prefiltered_sampler: &S,
209 brdf: &T,
210 brdf_sampler: &S,
211 shadow_map: &DtA,
212 shadow_map_sampler: &S,
213
214 geometry_slab: &[u32],
215 material_slab: &[u32],
216 lighting_slab: &[u32],
217
218 renderlet_id: Id<PrimitiveDescriptor>,
219
220 frag_coord: Vec4,
221 in_color: Vec4,
222 in_uv0: Vec2,
223 in_uv1: Vec2,
224 in_norm: Vec3,
225 in_tangent: Vec3,
226 in_bitangent: Vec3,
227 in_pos: Vec3,
228
229 output: &mut Vec4,
230) where
231 A: Sample2dArray<Sampler = S>,
232 T: Sample2d<Sampler = S>,
233 DtA: Sample2dArray<Sampler = S>,
234 C: SampleCube<Sampler = S>,
235 S: IsSampler,
236{
237 let renderlet = geometry_slab.read_unchecked(renderlet_id);
238 let geom_desc = geometry_slab.read_unchecked(renderlet.geometry_descriptor_id);
239 crate::println!("pbr_desc_id: {:?}", renderlet.geometry_descriptor_id);
240 crate::println!("pbr_desc: {geom_desc:#?}");
241 let GeometryDescriptor {
242 camera_id,
243 atlas_size,
244 resolution: _,
245 debug_channel,
246 has_lighting,
247 has_skinning: _,
248 perform_frustum_culling: _,
249 perform_occlusion_culling: _,
250 } = geom_desc;
251
252 let material = get_material(renderlet.material_id, has_lighting, material_slab);
253 crate::println!("material: {:#?}", material);
254
255 let albedo_tex_uv = if material.albedo_tex_coord == 0 {
256 in_uv0
257 } else {
258 in_uv1
259 };
260 let albedo_tex_color = texture_color(
261 material.albedo_texture_id,
262 albedo_tex_uv,
263 atlas,
264 atlas_sampler,
265 atlas_size,
266 material_slab,
267 );
268 my_println!("albedo_tex_color: {:?}", albedo_tex_color);
269
270 let metallic_roughness_uv = if material.metallic_roughness_tex_coord == 0 {
271 in_uv0
272 } else {
273 in_uv1
274 };
275 let metallic_roughness_tex_color = texture_color(
276 material.metallic_roughness_texture_id,
277 metallic_roughness_uv,
278 atlas,
279 atlas_sampler,
280 atlas_size,
281 material_slab,
282 );
283 my_println!(
284 "metallic_roughness_tex_color: {:?}",
285 metallic_roughness_tex_color
286 );
287
288 let normal_tex_uv = if material.normal_tex_coord == 0 {
289 in_uv0
290 } else {
291 in_uv1
292 };
293 let normal_tex_color = texture_color(
294 material.normal_texture_id,
295 normal_tex_uv,
296 atlas,
297 atlas_sampler,
298 atlas_size,
299 material_slab,
300 );
301 my_println!("normal_tex_color: {:?}", normal_tex_color);
302
303 let ao_tex_uv = if material.ao_tex_coord == 0 {
304 in_uv0
305 } else {
306 in_uv1
307 };
308 let ao_tex_color = texture_color(
309 material.ao_texture_id,
310 ao_tex_uv,
311 atlas,
312 atlas_sampler,
313 atlas_size,
314 material_slab,
315 );
316
317 let emissive_tex_uv = if material.emissive_tex_coord == 0 {
318 in_uv0
319 } else {
320 in_uv1
321 };
322 let emissive_tex_color = texture_color(
323 material.emissive_texture_id,
324 emissive_tex_uv,
325 atlas,
326 atlas_sampler,
327 atlas_size,
328 material_slab,
329 );
330
331 let (norm, uv_norm) = if material.normal_texture_id.is_none() {
332 (in_norm, Vec3::ZERO)
334 } else {
335 let sampled_norm = (normal_tex_color.xyz() * 2.0 - Vec3::splat(1.0)).alt_norm_or_zero();
337 let tbn = glam::mat3(
338 in_tangent.alt_norm_or_zero(),
339 in_bitangent.alt_norm_or_zero(),
340 in_norm.alt_norm_or_zero(),
341 );
342 let norm = (tbn * sampled_norm).alt_norm_or_zero();
344 (norm, sampled_norm)
345 };
346
347 let n = norm;
348 let albedo = albedo_tex_color * material.albedo_factor * in_color;
349 let roughness = metallic_roughness_tex_color.y * material.roughness_factor;
350 let metallic = metallic_roughness_tex_color.z * material.metallic_factor;
351 let ao = 1.0 + material.ao_strength * (ao_tex_color.x - 1.0);
352 let emissive =
353 emissive_tex_color.xyz() * material.emissive_factor * material.emissive_strength_multiplier;
354 let irradiance = sample_irradiance(irradiance, irradiance_sampler, n);
355 let camera = geometry_slab.read(camera_id);
356 let specular = sample_specular_reflection(
357 prefiltered,
358 prefiltered_sampler,
359 camera.position(),
360 in_pos,
361 n,
362 roughness,
363 );
364 let brdf =
365 brdf::shader::sample_brdf(brdf, brdf_sampler, camera.position(), in_pos, n, roughness);
366
367 fn colorize(u: Vec3) -> Vec4 {
368 ((u.alt_norm_or_zero() + Vec3::splat(1.0)) / 2.0).extend(1.0)
369 }
370
371 crate::println!("debug_mode: {debug_channel:?}");
372 use debug::DebugChannel::*;
373 match debug_channel {
374 None => {}
375 UvCoords0 => {
376 *output = colorize(Vec3::new(in_uv0.x, in_uv0.y, 0.0));
377 return;
378 }
379 UvCoords1 => {
380 *output = colorize(Vec3::new(in_uv1.x, in_uv1.y, 0.0));
381 return;
382 }
383 Normals => {
384 *output = colorize(norm);
385 return;
386 }
387 VertexColor => {
388 *output = in_color;
389 return;
390 }
391 VertexNormals => {
392 *output = colorize(in_norm);
393 return;
394 }
395 UvNormals => {
396 *output = colorize(uv_norm);
397 return;
398 }
399 Tangents => {
400 *output = colorize(in_tangent);
401 return;
402 }
403 Bitangents => {
404 *output = colorize(in_bitangent);
405 return;
406 }
407 DiffuseIrradiance => {
408 *output = irradiance.extend(1.0);
409 return;
410 }
411 SpecularReflection => {
412 *output = specular.extend(1.0);
413 return;
414 }
415 Brdf => {
416 *output = brdf.extend(1.0).extend(1.0);
417 return;
418 }
419 Roughness => {
420 *output = Vec3::splat(roughness).extend(1.0);
421 return;
422 }
423 Metallic => {
424 *output = Vec3::splat(metallic).extend(1.0);
425 return;
426 }
427 Albedo => {
428 *output = albedo;
429 return;
430 }
431 Occlusion => {
432 *output = Vec3::splat(ao).extend(1.0);
433 return;
434 }
435 Emissive => {
436 *output = emissive.extend(1.0);
437 return;
438 }
439 UvEmissive => {
440 *output = emissive_tex_color.xyz().extend(1.0);
441 return;
442 }
443 EmissiveFactor => {
444 *output = material.emissive_factor.extend(1.0);
445 return;
446 }
447 EmissiveStrength => {
448 *output = Vec3::splat(material.emissive_strength_multiplier).extend(1.0);
449 return;
450 }
451 }
452
453 *output = if material.has_lighting {
454 shade_fragment(
455 shadow_map,
456 shadow_map_sampler,
457 camera.position(),
458 n,
459 in_pos,
460 albedo.xyz(),
461 metallic,
462 roughness,
463 ao,
464 emissive,
465 irradiance,
466 specular,
467 brdf,
468 lighting_slab,
469 frag_coord,
470 )
471 } else {
472 crate::println!("no shading!");
473 in_color * albedo_tex_color * material.albedo_factor
474 };
475}
476
477#[allow(clippy::too_many_arguments)]
478pub fn shade_fragment<S, T>(
479 shadow_map: &T,
480 shadow_map_sampler: &S,
481 camera_pos: Vec3,
483 in_norm: Vec3,
485 in_pos: Vec3,
487 albedo: Vec3,
489 metallic: f32,
490 roughness: f32,
491 ao: f32,
492 emissive: Vec3,
493 irradiance: Vec3,
494 prefiltered: Vec3,
495 brdf: Vec2,
496
497 light_slab: &[u32],
498 frag_coord: Vec4,
499) -> Vec4
500where
501 S: IsSampler,
502 T: Sample2dArray<Sampler = S>,
503{
504 let n = in_norm.alt_norm_or_zero();
505 let v = (camera_pos - in_pos).alt_norm_or_zero();
506 let lighting_desc = light_slab.read_unchecked(Id::<LightingDescriptor>::new(0));
509 let analytical_lights_array = if lighting_desc.light_tiling_descriptor_id.is_none() {
511 lighting_desc.analytical_lights_array
512 } else {
513 let tiling_descriptor = light_slab.read_unchecked(lighting_desc.light_tiling_descriptor_id);
514 let tile_index = tiling_descriptor.tile_index_for_fragment(frag_coord.xy());
515 let tile = light_slab.read_unchecked(tiling_descriptor.tiles_array.at(tile_index));
516 tile.lights_array
517 };
518 my_println!("lights: {analytical_lights_array:?}");
519 my_println!("surface normal: {n:?}");
520 my_println!("vector from surface to camera: {v:?}");
521
522 let mut lo = Vec3::ZERO;
524 for light_id_id in analytical_lights_array.iter() {
525 let light_id = light_slab.read(light_id_id);
527 if light_id.is_none() {
528 break;
529 }
530 let light = light_slab.read(light_id);
531 let transform = light_slab.read(light.transform_id);
532 crate::println!("transform: {transform:?}");
533 let transform = Mat4::from(transform);
534
535 let (radiance, shadow) = match light.light_type {
537 LightStyle::Point => {
538 let PointLightDescriptor {
539 position,
540 color,
541 intensity: Candela(intensity_candelas),
542 } = light_slab.read(light.into_point_id());
543 let intensity = intensity_candelas / 683.0;
547 let position = transform.transform_point3(position);
548 let frag_to_light = position - in_pos;
553 let distance = frag_to_light.length();
554 if distance == 0.0 {
555 crate::println!("distance between point light and surface is zero");
556 continue;
557 }
558 let l = frag_to_light.alt_norm_or_zero();
559 let attenuation = intensity / (distance * distance);
560 let radiance =
561 outgoing_radiance(color, albedo, attenuation, v, l, n, metallic, roughness);
562 let shadow = if light.shadow_map_desc_id.is_some() {
563 ShadowCalculation::new(light_slab, light, in_pos, n, l).run_point(
566 light_slab,
567 shadow_map,
568 shadow_map_sampler,
569 position,
570 )
571 } else {
572 0.0
573 };
574 (radiance, shadow)
575 }
576
577 LightStyle::Spot => {
578 let spot_light_descriptor = light_slab.read(light.into_spot_id());
579 let calculation =
580 SpotLightCalculation::new(spot_light_descriptor, transform, in_pos);
581 crate::println!("calculation: {calculation:#?}");
582 if calculation.frag_to_light_distance == 0.0 {
583 continue;
584 }
585 let intensity =
590 spot_light_descriptor.intensity.0 / (683.0 * 4.0 * core::f32::consts::PI);
592 let attenuation: f32 = intensity * calculation.contribution;
593 let radiance = outgoing_radiance(
594 spot_light_descriptor.color,
595 albedo,
596 attenuation,
597 v,
598 calculation.frag_to_light,
599 n,
600 metallic,
601 roughness,
602 );
603 let shadow = if light.shadow_map_desc_id.is_some() {
604 ShadowCalculation::new(light_slab, light, in_pos, n, calculation.frag_to_light)
607 .run_directional_or_spot(light_slab, shadow_map, shadow_map_sampler)
608 } else {
609 0.0
610 };
611 (radiance, shadow)
612 }
613
614 LightStyle::Directional => {
615 let DirectionalLightDescriptor {
616 direction,
617 color,
618 intensity: intensity_lux,
619 } = light_slab.read(light.into_directional_id());
620 let direction = transform.transform_vector3(direction);
621 let l = -direction.alt_norm_or_zero();
622 let attenuation = intensity_lux.0 / 683.0;
625 let radiance =
626 outgoing_radiance(color, albedo, attenuation, v, l, n, metallic, roughness);
627 let shadow =
628 if light.shadow_map_desc_id.is_some() {
629 ShadowCalculation::new(light_slab, light, in_pos, n, l)
633 .run_directional_or_spot(light_slab, shadow_map, shadow_map_sampler)
634 } else {
635 0.0
636 };
637 (radiance, shadow)
638 }
639 };
640 crate::println!("radiance: {radiance}");
641 crate::println!("shadow: {shadow}");
642 lo += radiance * (1.0 - shadow);
643 }
644
645 my_println!("lo: {lo:?}");
646 let f0: Vec3 = Vec3::splat(0.04).lerp(albedo, metallic);
650 let cos_theta = n.dot(v).max(0.0);
651 let fresnel = fresnel_schlick_roughness(cos_theta, f0, roughness);
652 let ks = fresnel;
653 let kd = (1.0 - ks) * (1.0 - metallic);
654 let diffuse = irradiance * albedo;
655 let specular = prefiltered * (fresnel * brdf.x + brdf.y);
656 let ambient_light = lighting_desc.ambient_color.xyz() * lighting_desc.ambient_color.w;
658 let color = (kd * diffuse + specular) * ao + lo + emissive + ambient_light * albedo * ao;
659 color.extend(1.0)
660}