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 {
170 has_lighting: false,
171 ..Default::default()
172 }
173 } else {
174 let mut material = material_slab.read_unchecked(material_id);
175 material.has_lighting &= has_lighting;
176 material
177 }
178}
179
180pub fn texture_color<A: Sample2dArray<Sampler = S>, S: IsSampler>(
181 texture_id: Id<AtlasTextureDescriptor>,
182 uv: Vec2,
183 atlas: &A,
184 sampler: &S,
185 atlas_size: glam::UVec2,
186 material_slab: &[u32],
187) -> Vec4 {
188 let texture = material_slab.read(texture_id);
189 let uv = texture.uv(uv, atlas_size);
191 crate::println!("uv: {uv}");
192 let mut color: Vec4 = atlas.sample_by_lod(*sampler, uv, 0.0);
193 if texture_id.is_none() {
194 color = Vec4::splat(1.0);
195 }
196 color
197}
198
199#[allow(clippy::too_many_arguments)]
201pub fn fragment_impl<A, T, DtA, C, S>(
202 atlas: &A,
203 atlas_sampler: &S,
204 irradiance: &C,
205 irradiance_sampler: &S,
206 prefiltered: &C,
207 prefiltered_sampler: &S,
208 brdf: &T,
209 brdf_sampler: &S,
210 shadow_map: &DtA,
211 shadow_map_sampler: &S,
212
213 geometry_slab: &[u32],
214 material_slab: &[u32],
215 lighting_slab: &[u32],
216
217 renderlet_id: Id<PrimitiveDescriptor>,
218
219 frag_coord: Vec4,
220 in_color: Vec4,
221 in_uv0: Vec2,
222 in_uv1: Vec2,
223 in_norm: Vec3,
224 in_tangent: Vec3,
225 in_bitangent: Vec3,
226 in_pos: Vec3,
227
228 output: &mut Vec4,
229) where
230 A: Sample2dArray<Sampler = S>,
231 T: Sample2d<Sampler = S>,
232 DtA: Sample2dArray<Sampler = S>,
233 C: SampleCube<Sampler = S>,
234 S: IsSampler,
235{
236 let renderlet = geometry_slab.read_unchecked(renderlet_id);
237 let geom_desc = geometry_slab.read_unchecked(renderlet.geometry_descriptor_id);
238 crate::println!("pbr_desc_id: {:?}", renderlet.geometry_descriptor_id);
239 crate::println!("pbr_desc: {geom_desc:#?}");
240 let GeometryDescriptor {
241 camera_id,
242 atlas_size,
243 resolution: _,
244 debug_channel,
245 has_lighting,
246 has_skinning: _,
247 perform_frustum_culling: _,
248 perform_occlusion_culling: _,
249 } = geom_desc;
250
251 let material = get_material(renderlet.material_id, has_lighting, material_slab);
252 crate::println!("material: {:#?}", material);
253
254 let albedo_tex_uv = if material.albedo_tex_coord == 0 {
255 in_uv0
256 } else {
257 in_uv1
258 };
259 let albedo_tex_color = texture_color(
260 material.albedo_texture_id,
261 albedo_tex_uv,
262 atlas,
263 atlas_sampler,
264 atlas_size,
265 material_slab,
266 );
267 my_println!("albedo_tex_color: {:?}", albedo_tex_color);
268
269 let metallic_roughness_uv = if material.metallic_roughness_tex_coord == 0 {
270 in_uv0
271 } else {
272 in_uv1
273 };
274 let metallic_roughness_tex_color = texture_color(
275 material.metallic_roughness_texture_id,
276 metallic_roughness_uv,
277 atlas,
278 atlas_sampler,
279 atlas_size,
280 material_slab,
281 );
282 my_println!(
283 "metallic_roughness_tex_color: {:?}",
284 metallic_roughness_tex_color
285 );
286
287 let normal_tex_uv = if material.normal_tex_coord == 0 {
288 in_uv0
289 } else {
290 in_uv1
291 };
292 let normal_tex_color = texture_color(
293 material.normal_texture_id,
294 normal_tex_uv,
295 atlas,
296 atlas_sampler,
297 atlas_size,
298 material_slab,
299 );
300 my_println!("normal_tex_color: {:?}", normal_tex_color);
301
302 let ao_tex_uv = if material.ao_tex_coord == 0 {
303 in_uv0
304 } else {
305 in_uv1
306 };
307 let ao_tex_color = texture_color(
308 material.ao_texture_id,
309 ao_tex_uv,
310 atlas,
311 atlas_sampler,
312 atlas_size,
313 material_slab,
314 );
315
316 let emissive_tex_uv = if material.emissive_tex_coord == 0 {
317 in_uv0
318 } else {
319 in_uv1
320 };
321 let emissive_tex_color = texture_color(
322 material.emissive_texture_id,
323 emissive_tex_uv,
324 atlas,
325 atlas_sampler,
326 atlas_size,
327 material_slab,
328 );
329
330 let (norm, uv_norm) = if material.normal_texture_id.is_none() {
331 (in_norm, Vec3::ZERO)
333 } else {
334 let sampled_norm = (normal_tex_color.xyz() * 2.0 - Vec3::splat(1.0)).alt_norm_or_zero();
336 let tbn = glam::mat3(
337 in_tangent.alt_norm_or_zero(),
338 in_bitangent.alt_norm_or_zero(),
339 in_norm.alt_norm_or_zero(),
340 );
341 let norm = (tbn * sampled_norm).alt_norm_or_zero();
343 (norm, sampled_norm)
344 };
345
346 let n = norm;
347 let albedo = albedo_tex_color * material.albedo_factor * in_color;
348 let roughness = metallic_roughness_tex_color.y * material.roughness_factor;
349 let metallic = metallic_roughness_tex_color.z * material.metallic_factor;
350 let ao = 1.0 + material.ao_strength * (ao_tex_color.x - 1.0);
351 let emissive =
352 emissive_tex_color.xyz() * material.emissive_factor * material.emissive_strength_multiplier;
353 let irradiance = sample_irradiance(irradiance, irradiance_sampler, n);
354 let camera = geometry_slab.read(camera_id);
355 let specular = sample_specular_reflection(
356 prefiltered,
357 prefiltered_sampler,
358 camera.position(),
359 in_pos,
360 n,
361 roughness,
362 );
363 let brdf =
364 brdf::shader::sample_brdf(brdf, brdf_sampler, camera.position(), in_pos, n, roughness);
365
366 fn colorize(u: Vec3) -> Vec4 {
367 ((u.alt_norm_or_zero() + Vec3::splat(1.0)) / 2.0).extend(1.0)
368 }
369
370 crate::println!("debug_mode: {debug_channel:?}");
371 use debug::DebugChannel::*;
372 match debug_channel {
373 None => {}
374 UvCoords0 => {
375 *output = colorize(Vec3::new(in_uv0.x, in_uv0.y, 0.0));
376 return;
377 }
378 UvCoords1 => {
379 *output = colorize(Vec3::new(in_uv1.x, in_uv1.y, 0.0));
380 return;
381 }
382 Normals => {
383 *output = colorize(norm);
384 return;
385 }
386 VertexColor => {
387 *output = in_color;
388 return;
389 }
390 VertexNormals => {
391 *output = colorize(in_norm);
392 return;
393 }
394 UvNormals => {
395 *output = colorize(uv_norm);
396 return;
397 }
398 Tangents => {
399 *output = colorize(in_tangent);
400 return;
401 }
402 Bitangents => {
403 *output = colorize(in_bitangent);
404 return;
405 }
406 DiffuseIrradiance => {
407 *output = irradiance.extend(1.0);
408 return;
409 }
410 SpecularReflection => {
411 *output = specular.extend(1.0);
412 return;
413 }
414 Brdf => {
415 *output = brdf.extend(1.0).extend(1.0);
416 return;
417 }
418 Roughness => {
419 *output = Vec3::splat(roughness).extend(1.0);
420 return;
421 }
422 Metallic => {
423 *output = Vec3::splat(metallic).extend(1.0);
424 return;
425 }
426 Albedo => {
427 *output = albedo;
428 return;
429 }
430 Occlusion => {
431 *output = Vec3::splat(ao).extend(1.0);
432 return;
433 }
434 Emissive => {
435 *output = emissive.extend(1.0);
436 return;
437 }
438 UvEmissive => {
439 *output = emissive_tex_color.xyz().extend(1.0);
440 return;
441 }
442 EmissiveFactor => {
443 *output = material.emissive_factor.extend(1.0);
444 return;
445 }
446 EmissiveStrength => {
447 *output = Vec3::splat(material.emissive_strength_multiplier).extend(1.0);
448 return;
449 }
450 }
451
452 *output = if material.has_lighting {
453 shade_fragment(
454 shadow_map,
455 shadow_map_sampler,
456 camera.position(),
457 n,
458 in_pos,
459 albedo.xyz(),
460 metallic,
461 roughness,
462 ao,
463 emissive,
464 irradiance,
465 specular,
466 brdf,
467 lighting_slab,
468 frag_coord,
469 )
470 } else {
471 crate::println!("no shading!");
472 in_color * albedo_tex_color * material.albedo_factor
473 };
474}
475
476#[allow(clippy::too_many_arguments)]
477pub fn shade_fragment<S, T>(
478 shadow_map: &T,
479 shadow_map_sampler: &S,
480 camera_pos: Vec3,
482 in_norm: Vec3,
484 in_pos: Vec3,
486 albedo: Vec3,
488 metallic: f32,
489 roughness: f32,
490 ao: f32,
491 emissive: Vec3,
492 irradiance: Vec3,
493 prefiltered: Vec3,
494 brdf: Vec2,
495
496 light_slab: &[u32],
497 frag_coord: Vec4,
498) -> Vec4
499where
500 S: IsSampler,
501 T: Sample2dArray<Sampler = S>,
502{
503 let n = in_norm.alt_norm_or_zero();
504 let v = (camera_pos - in_pos).alt_norm_or_zero();
505 let lighting_desc = light_slab.read_unchecked(Id::<LightingDescriptor>::new(0));
508 let analytical_lights_array = if lighting_desc.light_tiling_descriptor_id.is_none() {
510 lighting_desc.analytical_lights_array
511 } else {
512 let tiling_descriptor = light_slab.read_unchecked(lighting_desc.light_tiling_descriptor_id);
513 let tile_index = tiling_descriptor.tile_index_for_fragment(frag_coord.xy());
514 let tile = light_slab.read_unchecked(tiling_descriptor.tiles_array.at(tile_index));
515 tile.lights_array
516 };
517 my_println!("lights: {analytical_lights_array:?}");
518 my_println!("surface normal: {n:?}");
519 my_println!("vector from surface to camera: {v:?}");
520
521 let mut lo = Vec3::ZERO;
523 for light_id_id in analytical_lights_array.iter() {
524 let light_id = light_slab.read(light_id_id);
526 if light_id.is_none() {
527 break;
528 }
529 let light = light_slab.read(light_id);
530 let transform = light_slab.read(light.transform_id);
531 crate::println!("transform: {transform:?}");
532 let transform = Mat4::from(transform);
533
534 let (radiance, shadow) = match light.light_type {
536 LightStyle::Point => {
537 let PointLightDescriptor {
538 position,
539 color,
540 intensity: Candela(intensity_candelas),
541 } = light_slab.read(light.into_point_id());
542 let intensity = intensity_candelas / 683.0;
545 let position = transform.transform_point3(position);
546 let frag_to_light = position - in_pos;
551 let distance = frag_to_light.length();
552 if distance == 0.0 {
553 crate::println!("distance between point light and surface is zero");
554 continue;
555 }
556 let l = frag_to_light.alt_norm_or_zero();
557 let attenuation = intensity / (distance * distance);
558 let radiance =
559 outgoing_radiance(color, albedo, attenuation, v, l, n, metallic, roughness);
560 let shadow = if light.shadow_map_desc_id.is_some() {
561 ShadowCalculation::new(light_slab, light, in_pos, n, l).run_point(
564 light_slab,
565 shadow_map,
566 shadow_map_sampler,
567 position,
568 )
569 } else {
570 0.0
571 };
572 (radiance, shadow)
573 }
574
575 LightStyle::Spot => {
576 let spot_light_descriptor = light_slab.read(light.into_spot_id());
577 let calculation =
578 SpotLightCalculation::new(spot_light_descriptor, transform, in_pos);
579 crate::println!("calculation: {calculation:#?}");
580 if calculation.frag_to_light_distance == 0.0 {
581 continue;
582 }
583 let intensity =
587 spot_light_descriptor.intensity.0 / (683.0 * 4.0 * core::f32::consts::PI);
589 let attenuation: f32 = intensity * calculation.contribution;
590 let radiance = outgoing_radiance(
591 spot_light_descriptor.color,
592 albedo,
593 attenuation,
594 v,
595 calculation.frag_to_light,
596 n,
597 metallic,
598 roughness,
599 );
600 let shadow = if light.shadow_map_desc_id.is_some() {
601 ShadowCalculation::new(light_slab, light, in_pos, n, calculation.frag_to_light)
604 .run_directional_or_spot(light_slab, shadow_map, shadow_map_sampler)
605 } else {
606 0.0
607 };
608 (radiance, shadow)
609 }
610
611 LightStyle::Directional => {
612 let DirectionalLightDescriptor {
613 direction,
614 color,
615 intensity: intensity_lux,
616 } = light_slab.read(light.into_directional_id());
617 let direction = transform.transform_vector3(direction);
618 let l = -direction.alt_norm_or_zero();
619 let attenuation = intensity_lux.0 / 683.0;
621 let radiance =
622 outgoing_radiance(color, albedo, attenuation, v, l, n, metallic, roughness);
623 let shadow =
624 if light.shadow_map_desc_id.is_some() {
625 ShadowCalculation::new(light_slab, light, in_pos, n, l)
628 .run_directional_or_spot(light_slab, shadow_map, shadow_map_sampler)
629 } else {
630 0.0
631 };
632 (radiance, shadow)
633 }
634 };
635 crate::println!("radiance: {radiance}");
636 crate::println!("shadow: {shadow}");
637 lo += radiance * (1.0 - shadow);
638 }
639
640 my_println!("lo: {lo:?}");
641 let f0: Vec3 = Vec3::splat(0.04).lerp(albedo, metallic);
645 let cos_theta = n.dot(v).max(0.0);
646 let fresnel = fresnel_schlick_roughness(cos_theta, f0, roughness);
647 let ks = fresnel;
648 let kd = (1.0 - ks) * (1.0 - metallic);
649 let diffuse = irradiance * albedo;
650 let specular = prefiltered * (fresnel * brdf.x + brdf.y);
651 let color = (kd * diffuse + specular) * ao + lo + emissive;
652 color.extend(1.0)
653}