enginerenderer 0.0.1

A zero-dependency offline rendering engine in pure Rust — CPU path tracing, BVH acceleration, 16-band spectral rendering, PBR materials, animation & video export.
Documentation
use crate::core::engine::math::Mat4;
use crate::core::engine::rendering::framebuffer::buffer::FrameBuffer;
use crate::core::engine::rendering::raster::pipeline::GBuffer;
use crate::core::engine::rendering::raytracing::Vec3;
use crate::core::engine::rendering::raytracing::ddgi::DdgiVolume;

pub struct HybridGi;

impl HybridGi {
    pub fn apply(
        gbuffer: &GBuffer,
        ddgi: &DdgiVolume,
        inv_proj: &Mat4,
        inv_view: &Mat4,
        camera_pos: Vec3,
        fb: &mut FrameBuffer,
    ) {
        let w = gbuffer.width;
        let h = gbuffer.height;

        for y in 0..h {
            for x in 0..w {
                let idx = y * w + x;
                let depth = gbuffer.depth[idx];
                if depth >= 1.0 {
                    continue;
                }

                let nx = (x as f32 / w as f32) * 2.0 - 1.0;
                let ny = 1.0 - (y as f32 / h as f32) * 2.0;
                let nz = (depth as f32) * 2.0 - 1.0;

                let clip = [nx, ny, nz, 1.0_f32];
                let view_space = mat4_transform(inv_proj, clip);
                let w_div = if view_space[3].abs() > f32::EPSILON {
                    view_space[3]
                } else {
                    1.0
                };
                let view_pos = [
                    view_space[0] / w_div,
                    view_space[1] / w_div,
                    view_space[2] / w_div,
                    1.0,
                ];
                let world_space = mat4_transform(inv_view, view_pos);
                let world_pos = Vec3::new(
                    world_space[0] as f64,
                    world_space[1] as f64,
                    world_space[2] as f64,
                );

                let gn = gbuffer.normal[idx];
                let normal = gn.normalize();
                let view_dir = (camera_pos - world_pos).normalize();

                let irradiance = ddgi.sample_irradiance(world_pos, normal, view_dir, None);
                let albedo = gbuffer.albedo[idx];
                let albedo_f64 = Vec3::new(albedo[0], albedo[1], albedo[2]);

                let gi_contrib = Vec3::new(
                    irradiance.x * albedo_f64.x,
                    irradiance.y * albedo_f64.y,
                    irradiance.z * albedo_f64.z,
                );

                if idx < fb.color.len() {
                    fb.color[idx] += gi_contrib;
                }
            }
        }
    }
}

fn mat4_transform(m: &Mat4, v: [f32; 4]) -> [f32; 4] {
    [
        m.m[0][0] * v[0] + m.m[0][1] * v[1] + m.m[0][2] * v[2] + m.m[0][3] * v[3],
        m.m[1][0] * v[0] + m.m[1][1] * v[1] + m.m[1][2] * v[2] + m.m[1][3] * v[3],
        m.m[2][0] * v[0] + m.m[2][1] * v[1] + m.m[2][2] * v[2] + m.m[2][3] * v[3],
        m.m[3][0] * v[0] + m.m[3][1] * v[1] + m.m[3][2] * v[2] + m.m[3][3] * v[3],
    ]
}