#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub struct OitConfig {
pub weight_exponent: f32,
pub near_plane: f32,
pub far_plane: f32,
pub max_layers: u32,
}
impl Default for OitConfig {
fn default() -> Self {
Self {
weight_exponent: 3.0,
near_plane: 0.1,
far_plane: 100.0,
max_layers: 8,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, Default)]
pub struct OitAccum {
pub color_sum: [f32; 4],
pub reveal: f32,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
pub struct TransparentFragment {
pub color: [f32; 3],
pub alpha: f32,
pub depth: f32,
}
#[allow(dead_code)]
pub fn compute_weight(alpha: f32, depth: f32, config: &OitConfig) -> f32 {
let depth = depth.clamp(config.near_plane, config.far_plane);
let z = (config.far_plane - depth) / (config.far_plane - config.near_plane);
let z_clamped = z.clamp(1e-5, 1.0);
alpha * z_clamped.powf(config.weight_exponent).max(1e-4).min(3e4)
}
#[allow(dead_code)]
pub fn accumulate_fragment(
accum: &mut OitAccum,
frag: &TransparentFragment,
config: &OitConfig,
) {
let w = compute_weight(frag.alpha, frag.depth, config);
accum.color_sum[0] += frag.color[0] * frag.alpha * w;
accum.color_sum[1] += frag.color[1] * frag.alpha * w;
accum.color_sum[2] += frag.color[2] * frag.alpha * w;
accum.color_sum[3] += frag.alpha * w;
accum.reveal *= 1.0 - frag.alpha;
}
#[allow(dead_code)]
pub fn resolve_oit(accum: &OitAccum, bg_color: [f32; 3]) -> [f32; 3] {
if accum.color_sum[3].abs() < 1e-6 {
return bg_color;
}
let avg_color = [
accum.color_sum[0] / accum.color_sum[3],
accum.color_sum[1] / accum.color_sum[3],
accum.color_sum[2] / accum.color_sum[3],
];
let coverage = 1.0 - accum.reveal;
[
avg_color[0] * coverage + bg_color[0] * accum.reveal,
avg_color[1] * coverage + bg_color[1] * accum.reveal,
avg_color[2] * coverage + bg_color[2] * accum.reveal,
]
}
#[allow(dead_code)]
pub fn linearize_depth(ndc_z: f32, near: f32, far: f32) -> f32 {
if (far - near).abs() < 1e-6 {
return near;
}
(2.0 * near * far) / (far + near - ndc_z * (far - near))
}
#[allow(dead_code)]
pub fn composite_sorted(fragments: &mut [TransparentFragment], bg: [f32; 3]) -> [f32; 3] {
fragments.sort_by(|a, b| a.depth.partial_cmp(&b.depth).unwrap_or(std::cmp::Ordering::Equal));
let mut result = bg;
let mut remaining_alpha = 1.0_f32;
for frag in fragments.iter() {
if remaining_alpha < 1e-4 {
break;
}
let contrib = frag.alpha * remaining_alpha;
result[0] = result[0] * (1.0 - contrib) + frag.color[0] * contrib;
result[1] = result[1] * (1.0 - contrib) + frag.color[1] * contrib;
result[2] = result[2] * (1.0 - contrib) + frag.color[2] * contrib;
remaining_alpha *= 1.0 - frag.alpha;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let c = OitConfig::default();
assert!(c.far_plane > c.near_plane);
}
#[test]
fn test_compute_weight_positive() {
let c = OitConfig::default();
let w = compute_weight(0.5, 1.0, &c);
assert!(w > 0.0);
}
#[test]
fn test_compute_weight_closer_heavier() {
let c = OitConfig::default();
let w_close = compute_weight(0.5, 1.0, &c);
let w_far = compute_weight(0.5, 50.0, &c);
assert!(w_close > w_far);
}
#[test]
fn test_accumulate_and_resolve_single() {
let c = OitConfig::default();
let mut accum = OitAccum { color_sum: [0.0; 4], reveal: 1.0 };
let frag = TransparentFragment { color: [1.0, 0.0, 0.0], alpha: 0.5, depth: 5.0 };
accumulate_fragment(&mut accum, &frag, &c);
let result = resolve_oit(&accum, [0.0, 0.0, 0.0]);
assert!(result[0] > 0.0);
}
#[test]
fn test_resolve_empty() {
let accum = OitAccum { color_sum: [0.0; 4], reveal: 1.0 };
let bg = [0.5, 0.5, 0.5];
let result = resolve_oit(&accum, bg);
assert!((result[0] - 0.5).abs() < 1e-5);
}
#[test]
fn test_linearize_depth_near() {
let d = linearize_depth(-1.0, 0.1, 100.0);
assert!((d - 0.1).abs() < 0.01);
}
#[test]
fn test_linearize_depth_equal_planes() {
let d = linearize_depth(0.0, 1.0, 1.0);
assert!((d - 1.0).abs() < 1e-5);
}
#[test]
fn test_composite_sorted_empty() {
let result = composite_sorted(&mut [], [0.5, 0.5, 0.5]);
assert!((result[0] - 0.5).abs() < 1e-5);
}
#[test]
fn test_composite_sorted_opaque() {
let mut frags = vec![
TransparentFragment { color: [1.0, 0.0, 0.0], alpha: 1.0, depth: 1.0 },
];
let result = composite_sorted(&mut frags, [0.0, 1.0, 0.0]);
assert!(result[0] > 0.9);
}
#[test]
fn test_composite_sorted_order() {
let mut frags = vec![
TransparentFragment { color: [1.0, 0.0, 0.0], alpha: 0.5, depth: 2.0 },
TransparentFragment { color: [0.0, 0.0, 1.0], alpha: 0.5, depth: 1.0 },
];
let result = composite_sorted(&mut frags, [0.0, 0.0, 0.0]);
assert!(result[2] > result[0]);
}
}