macro_rules! impl_light_body {
($inner:ident) => {
fn shader_source(&self, i: u32) -> String {
self.$inner().shader_source(i)
}
fn use_uniforms(&self, program: &Program, i: u32) {
self.$inner().use_uniforms(program, i)
}
fn id(&self) -> u8 {
self.$inner().id()
}
};
}
mod directional_light;
use std::ops::Deref;
#[doc(inline)]
pub use directional_light::*;
mod spot_light;
#[doc(inline)]
pub use spot_light::*;
mod point_light;
#[doc(inline)]
pub use point_light::*;
mod ambient_light;
#[doc(inline)]
pub use ambient_light::*;
mod environment;
#[doc(inline)]
pub use environment::*;
use crate::core::*;
use crate::renderer::camera::*;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Attenuation {
pub constant: f32,
pub linear: f32,
pub quadratic: f32,
}
impl Default for Attenuation {
fn default() -> Self {
Self {
constant: 1.0,
linear: 0.0,
quadratic: 0.0,
}
}
}
pub trait Light {
fn shader_source(&self, i: u32) -> String;
fn use_uniforms(&self, program: &Program, i: u32);
fn id(&self) -> u8;
}
impl<T: Light + ?Sized> Light for &T {
impl_light_body!(deref);
}
impl<T: Light + ?Sized> Light for &mut T {
impl_light_body!(deref);
}
impl<T: Light> Light for Box<T> {
impl_light_body!(as_ref);
}
impl<T: Light> Light for std::rc::Rc<T> {
impl_light_body!(as_ref);
}
impl<T: Light> Light for std::sync::Arc<T> {
impl_light_body!(as_ref);
}
impl<T: Light> Light for std::cell::RefCell<T> {
impl_light_body!(borrow);
}
impl<T: Light> Light for std::sync::Arc<std::sync::RwLock<T>> {
fn shader_source(&self, i: u32) -> String {
self.read().unwrap().shader_source(i)
}
fn use_uniforms(&self, program: &Program, i: u32) {
self.read().unwrap().use_uniforms(program, i)
}
fn id(&self) -> u8 {
self.read().unwrap().id()
}
}
pub fn lights_shader_source(lights: &[&dyn Light], lighting_model: LightingModel) -> String {
let mut shader_source = lighting_model_shader(lighting_model).to_string();
shader_source.push_str(include_str!("../core/shared.frag"));
shader_source.push_str(include_str!("light/shaders/light_shared.frag"));
let mut dir_fun = String::new();
for (i, light) in lights.iter().enumerate() {
shader_source.push_str(&light.shader_source(i as u32));
dir_fun.push_str(&format!("color += calculate_lighting{}(surface_color, position, normal, view_direction, metallic, roughness, occlusion);\n", i))
}
shader_source.push_str(&format!(
"
vec3 calculate_lighting(vec3 camera_position, vec3 surface_color, vec3 position, vec3 normal, float metallic, float roughness, float occlusion)
{{
vec3 color = vec3(0.0, 0.0, 0.0);
vec3 view_direction = normalize(camera_position - position);
{}
return color;
}}
",
&dir_fun
));
shader_source
}
fn shadow_matrix(camera: &Camera) -> Mat4 {
let bias_matrix = crate::Mat4::new(
0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0,
);
bias_matrix * camera.projection() * camera.view()
}
fn compute_up_direction(direction: Vec3) -> Vec3 {
if vec3(1.0, 0.0, 0.0).dot(direction).abs() > 0.9 {
(vec3(0.0, 1.0, 0.0).cross(direction)).normalize()
} else {
(vec3(1.0, 0.0, 0.0).cross(direction)).normalize()
}
}
use crate::renderer::{LightingModel, NormalDistributionFunction};
pub(crate) fn lighting_model_shader(lighting_model: LightingModel) -> &'static str {
match lighting_model {
LightingModel::Phong => "#define PHONG",
LightingModel::Blinn => "#define BLINN",
LightingModel::Cook(normal, _) => match normal {
NormalDistributionFunction::Blinn => "#define COOK\n#define COOK_BLINN\n",
NormalDistributionFunction::Beckmann => "#define COOK\n#define COOK_BECKMANN\n",
NormalDistributionFunction::TrowbridgeReitzGGX => "#define COOK\n#define COOK_GGX\n",
},
}
}