use crate::Rgba;
pub fn volumetric_fog_fx(p: f32, dist: f32) -> f32 {1.0 - (-p * dist).exp()}
pub fn volumetric_fog_alpha(alpha: f32, fx: f32, dist: f32) -> f32 {
let new_alpha = alpha * fx;
let t = alpha * if dist < std::f32::EPSILON {0.0} else {1.0};
alpha * t + new_alpha * (1.0 - t)
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum FogState {
None,
Start {
color: Rgba,
depth: f32,
distance: f32
},
End {
color: Rgba,
distance: f32
},
Commit {
color: Rgba,
distance: f32
},
}
impl FogState {
pub fn update(self, d: f32, c: Rgba) -> FogState {
use FogState::*;
match self {
None | Commit {..} =>
if c[3] < 1.0 {Start {color: c, depth: d, distance: 0.0}} else {None},
End {color, distance} => {
if c == color {Start {color, depth: d, distance}}
else {Commit {color, distance}}
}
Start {color, depth, distance} => {
let distance = (d - depth).abs() + distance;
if c == color {End {color, distance}}
else {Commit {color, distance}}
}
}
}
pub fn acc_alpha_blend_srgb_over(&mut self, d: f32, c: Rgba, acc_color: &mut Rgba) {
use crate::color::rgba_alpha_blending_srgb_over;
*self = self.update(d, c);
match self {
FogState::None => {
*acc_color = rgba_alpha_blending_srgb_over(*acc_color, c);
}
FogState::Start {..} | FogState::End {..} => {}
FogState::Commit {color: fog_color, distance} => {
let fx = volumetric_fog_fx(fog_color[3], *distance);
let [r, g, b, a] = *fog_color;
let c2 = [r, g, b, volumetric_fog_alpha(a, fx, *distance)];
*acc_color = rgba_alpha_blending_srgb_over(*acc_color, c2);
if c[3] < 1.0 {
*self = FogState::Start {color: c, depth: d, distance: 0.0};
} else {
*self = FogState::None;
*acc_color = rgba_alpha_blending_srgb_over(*acc_color, c);
}
}
}
}
pub fn acc_end_alpha_blend_srgb_over(&mut self, acc_color: &mut Rgba) {
use crate::color::rgba_alpha_blending_srgb_over;
match self {
FogState::None => {}
FogState::Start {..} => {}
FogState::Commit {..} => {}
FogState::End {color: fog_color, distance} => {
let fx = volumetric_fog_fx(fog_color[3], *distance);
let [r, g, b, a] = *fog_color;
let c = [r, g, b, volumetric_fog_alpha(a, fx, *distance)];
*acc_color = rgba_alpha_blending_srgb_over(*acc_color, c);
}
}
}
pub fn acc_alpha_blend_linear_over(&mut self, d: f32, c: Rgba, acc_color: &mut Rgba) {
use crate::color::rgba_alpha_blending_linear_over;
*self = self.update(d, c);
match self {
FogState::None => {
*acc_color = rgba_alpha_blending_linear_over(*acc_color, c);
}
FogState::Start {..} |
FogState::End {..} => {}
FogState::Commit {color: fog_color, distance} => {
let fx = volumetric_fog_fx(fog_color[3], *distance);
let [r, g, b, a] = *fog_color;
let c2 = [r, g, b, volumetric_fog_alpha(a, fx, *distance)];
*acc_color = rgba_alpha_blending_linear_over(*acc_color, c2);
if c[3] < 1.0 {
*self = FogState::Start {color: c, depth: d, distance: 0.0};
} else {
*self = FogState::None;
*acc_color = rgba_alpha_blending_linear_over(*acc_color, c);
}
}
}
}
pub fn acc_end_alpha_blend_linear_over(&mut self, acc_color: &mut Rgba) {
use crate::color::rgba_alpha_blending_linear_over;
match self {
FogState::None |
FogState::Start {..} => {}
FogState::Commit {color, distance} |
FogState::End {color, distance} => {
let fx = volumetric_fog_fx(color[3], *distance);
let [r, g, b, a] = *color;
let c = [r, g, b, volumetric_fog_alpha(a, fx, *distance)];
*acc_color = rgba_alpha_blending_linear_over(*acc_color, c);
}
}
}
}