#![allow(dead_code)]
use std::f32::consts::PI;
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub struct TaaConfig {
pub blend_factor: f32,
pub clamp_mode: ClampMode,
pub jitter_count: u32,
pub sharpening: f32,
}
impl Default for TaaConfig {
fn default() -> Self {
Self {
blend_factor: 0.9,
clamp_mode: ClampMode::Aabb,
jitter_count: 16,
sharpening: 0.1,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClampMode {
Aabb,
Variance,
None,
}
#[allow(dead_code)]
pub fn halton_jitter(index: u32) -> (f32, f32) {
let x = halton(index, 2) - 0.5;
let y = halton(index, 3) - 0.5;
(x, y)
}
#[allow(dead_code)]
pub fn halton(mut index: u32, base: u32) -> f32 {
let mut f = 1.0_f32;
let mut r = 0.0_f32;
let b = base as f32;
while index > 0 {
f /= b;
r += f * (index % base) as f32;
index /= base;
}
r
}
#[allow(dead_code)]
pub fn aabb_clamp(history: [f32; 3], neighbourhood: &[[f32; 3]]) -> [f32; 3] {
if neighbourhood.is_empty() {
return history;
}
let mut min_c = [f32::MAX; 3];
let mut max_c = [f32::MIN; 3];
for c in neighbourhood {
for i in 0..3 {
min_c[i] = min_c[i].min(c[i]);
max_c[i] = max_c[i].max(c[i]);
}
}
[
history[0].clamp(min_c[0], max_c[0]),
history[1].clamp(min_c[1], max_c[1]),
history[2].clamp(min_c[2], max_c[2]),
]
}
#[allow(dead_code)]
pub fn variance_clamp(history: [f32; 3], neighbourhood: &[[f32; 3]], gamma: f32) -> [f32; 3] {
if neighbourhood.is_empty() {
return history;
}
let n = neighbourhood.len() as f32;
let mut mean = [0.0_f32; 3];
let mut mean_sq = [0.0_f32; 3];
for c in neighbourhood {
for i in 0..3 {
mean[i] += c[i];
mean_sq[i] += c[i] * c[i];
}
}
let mut result = [0.0_f32; 3];
for i in 0..3 {
mean[i] /= n;
mean_sq[i] /= n;
let variance = (mean_sq[i] - mean[i] * mean[i]).max(0.0);
let std_dev = variance.sqrt();
let lo = mean[i] - gamma * std_dev;
let hi = mean[i] + gamma * std_dev;
result[i] = history[i].clamp(lo, hi);
}
result
}
#[allow(dead_code)]
pub fn blend_taa(current: [f32; 3], history: [f32; 3], factor: f32) -> [f32; 3] {
let f = factor.clamp(0.0, 1.0);
[
current[0] * (1.0 - f) + history[0] * f,
current[1] * (1.0 - f) + history[1] * f,
current[2] * (1.0 - f) + history[2] * f,
]
}
#[allow(dead_code)]
pub fn sharpen(centre: [f32; 3], neighbours_avg: [f32; 3], amount: f32) -> [f32; 3] {
[
(centre[0] + (centre[0] - neighbours_avg[0]) * amount).clamp(0.0, 1.0),
(centre[1] + (centre[1] - neighbours_avg[1]) * amount).clamp(0.0, 1.0),
(centre[2] + (centre[2] - neighbours_avg[2]) * amount).clamp(0.0, 1.0),
]
}
#[allow(dead_code)]
pub fn reproject_uv(uv: (f32, f32), motion: (f32, f32)) -> (f32, f32) {
(uv.0 - motion.0, uv.1 - motion.1)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let c = TaaConfig::default();
assert!((0.0..=1.0).contains(&c.blend_factor));
}
#[test]
fn test_halton_jitter_range() {
for i in 0..16 {
let (x, y) = halton_jitter(i);
assert!((-0.5..=0.5).contains(&x));
assert!((-0.5..=0.5).contains(&y));
}
}
#[test]
fn test_halton_zero() {
assert!(halton(0, 2).abs() < 1e-5);
}
#[test]
fn test_aabb_clamp_inside() {
let h = [0.5, 0.5, 0.5];
let n = [[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]];
let r = aabb_clamp(h, &n);
assert!((r[0] - 0.5).abs() < 1e-5);
}
#[test]
fn test_aabb_clamp_outside() {
let h = [2.0, -1.0, 0.5];
let n = [[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]];
let r = aabb_clamp(h, &n);
assert!((r[0] - 1.0).abs() < 1e-5);
assert!(r[1].abs() < 1e-5);
}
#[test]
fn test_aabb_clamp_empty() {
let r = aabb_clamp([0.5, 0.5, 0.5], &[]);
assert!((r[0] - 0.5).abs() < 1e-5);
}
#[test]
fn test_blend_taa_zero() {
let r = blend_taa([1.0, 0.0, 0.0], [0.0, 1.0, 0.0], 0.0);
assert!((r[0] - 1.0).abs() < 1e-5);
}
#[test]
fn test_blend_taa_one() {
let r = blend_taa([1.0, 0.0, 0.0], [0.0, 1.0, 0.0], 1.0);
assert!((r[1] - 1.0).abs() < 1e-5);
}
#[test]
fn test_sharpen_no_amount() {
let r = sharpen([0.5, 0.5, 0.5], [0.4, 0.4, 0.4], 0.0);
assert!((r[0] - 0.5).abs() < 1e-5);
}
#[test]
fn test_reproject_uv() {
let (u, v) = reproject_uv((0.5, 0.5), (0.1, -0.1));
assert!((u - 0.4).abs() < 1e-5);
assert!((v - 0.6).abs() < 1e-5);
}
}