ambient_renderer 0.2.1

Ambient renderer. Host-only.
Documentation

struct Plane {
    normal: vec3<f32>,
    distance: f32,
};

fn plane_distance(plane: Plane, position: vec3<f32>) -> f32 {
    return dot(plane.normal, position) + plane.distance;
}

struct Camera {
    view: mat4x4<f32>,
    position: vec4<f32>,
    frustum_right: Plane,
    frustum_top: Plane,
    orthographic_size: vec2<f32>,
    frustum_near: f32,
    frustum_far: f32,
    cot_fov_2: f32,
};

struct Params {
    main_camera: Camera,
    shadow_cameras: array<Camera, MAX_SHADOW_CASCADES>,
    lod_cutoff_scaling: f32,
};

@group(LODDING_BIND_GROUP)
@binding(0)
var<uniform> params: Params;

struct CameraCullResult {
    fully_contained: bool,
    inside: bool,
};

fn cull_camera(camera: Camera, bounding_sphere: vec4<f32>) -> CameraCullResult {
    var res: CameraCullResult;

    let center = (camera.view * vec4<f32>(bounding_sphere.xyz, 1.)).xyz;
    let radius = bounding_sphere.w;

    let sphere_mirrored = vec3<f32>(abs(center.xy), center.z);

    let top_dist = plane_distance(camera.frustum_top, sphere_mirrored);
    let right_dist = plane_distance(camera.frustum_right, sphere_mirrored);

    res.inside = !(top_dist > radius) && !(right_dist > radius) && center.z + radius > camera.frustum_near && center.z - radius < camera.frustum_far;

    res.fully_contained = !(top_dist > -radius) && !(right_dist > -radius) && center.z - radius > camera.frustum_near && center.z + radius < camera.frustum_far;

    return res;
}

fn get_lod(entity_loc: vec2<u32>) -> u32 {

    let bounding_sphere = get_entity_world_bounding_sphere(entity_loc);
    let radius = bounding_sphere.w;

    var lod_cutoffs = get_entity_lod_cutoffs(entity_loc) ;

    let dist = length(params.main_camera.position.xyz - bounding_sphere.xyz);
    let clip_space_radius = radius * params.main_camera.cot_fov_2 / dist;
    for (var i = 0u; i < 4u; i = i + 1u) {
        for (var j = 0u; j < 4u; j = j + 1u) {
            if clip_space_radius >= lod_cutoffs[i][j] * params.lod_cutoff_scaling {
                return i * 4u + j;
            }
        }
    }

    return 0u;
}

fn update(entity_loc: vec2<u32>) {
    if has_entity_gpu_lod(entity_loc) {
        set_entity_gpu_lod(entity_loc, vec4<f32>(f32(get_lod(entity_loc)), 0.0, 0.0, 0.0));
    }
    var cameras: mat4x4<f32>;
    let bounding_sphere = get_entity_world_bounding_sphere(entity_loc);
    cameras[0][0] = f32(cull_camera(params.main_camera, bounding_sphere).inside);

    for (var i = 1u; i <= SHADOW_CASCADESu; i = i + 1u) {
        let a = i >> 2u;
        let b = i & 3u;

        cameras[a][b] = 0.0;
    }
    for (var i = 0u; i < SHADOW_CASCADESu; i = i + 1u) {
        let radius = bounding_sphere.w;
        let pixel_size = vec2<f32>(radius) * 2. / params.shadow_cameras[i].orthographic_size;
        let min_pixel_size = min(pixel_size.x, pixel_size.y);

        if min_pixel_size < 0.01 {
            break;
        }

        let res = cull_camera(params.shadow_cameras[i], bounding_sphere);
        if res.inside {
            let a = (i + 1u) >> 2u;
            let b = (i + 1u) & 3u;
            cameras[a][b] = 1.0;
        }
        if res.fully_contained {
            break;
        }
    }
    set_entity_renderer_cameras_visible(entity_loc, cameras);
}