// RTVolume closest-hit (runs in SHADE). Shades the procedural sphere by
// its surface normal with a fixed sun + ambient, tinted per instance.
//
// Payload declared here so the assembler sees it before wfPayload / SHADE.
struct Payload {
    color: vec3<f32>,
};

const SUN_DIR_TO_LIGHT: vec3<f32> = vec3<f32>(0.40, 0.85, 0.35);
const SUN_COLOR:        vec3<f32> = vec3<f32>(1.20, 1.10, 0.95);
const AMBIENT_COLOR:    vec3<f32> = vec3<f32>(0.16, 0.18, 0.24);

fn instanceAlbedo(i: u32) -> vec3<f32> {
    let h = i * 2654435761u;
    return vec3<f32>(
        0.35 + 0.6 * f32((h >>  0u) & 255u) / 255.0,
        0.35 + 0.6 * f32((h >>  8u) & 255u) / 255.0,
        0.35 + 0.6 * f32((h >> 16u) & 255u) / 255.0);
}

fn closesthit_main(ray: RayDesc, hit: HitInfo, payload: ptr<function, Payload>) {
    // Object-space hit point on the unit sphere is its object-space normal.
    let posObj = hit.objectRayOrigin + hit.objectRayDirection * hit.t;
    let nObj   = normalize(posObj);
    let nWorld = normalize(vec3<f32>(
        dot(hit.objectToWorldR0.xyz, nObj),
        dot(hit.objectToWorldR1.xyz, nObj),
        dot(hit.objectToWorldR2.xyz, nObj)));

    let albedo  = instanceAlbedo(hit.customIndex);
    let viewDir = -ray.direction;
    let nFacing = select(-nWorld, nWorld, dot(nWorld, viewDir) > 0.0);
    let sunDir  = normalize(SUN_DIR_TO_LIGHT);
    let nDotL   = max(0.0, dot(nFacing, sunDir));

    rtAccumulate(albedo * (AMBIENT_COLOR + SUN_COLOR * nDotL));
}
