Constant CANOPY_LIGHT_TAIL
Source pub const CANOPY_LIGHT_TAIL: &str = r#"fn canopy_light(world_position: vec3f) -> f32 {
let sun_input = canopy_params.sun_receiver.xyz;
let sun = select(vec3f(0.0, 1.0, 0.0), sun_input / max(length(sun_input), 1e-6), length(sun_input) > 1e-6);
if (canopy_params.mode_sharpness.x > 0.5) {
let origin = vec3f(canopy_params.center_extent.x, canopy_params.wind.z, canopy_params.center_extent.y);
let horizontal = vec3f(-sun.z, 0.0, sun.x);
let gobo_u = horizontal / max(length(horizontal), 1e-6);
let gobo_v = cross(gobo_u, sun);
let q = vec2f(dot(world_position - origin, gobo_u), dot(world_position - origin, gobo_v));
let light = procedural_canopy(q, canopy_params.wind.x);
let scale = canopy_params.mode_sharpness.z;
if (scale == 1.0) { return light; }
return 1.0 - (1.0 - light) * scale;
}
let reference = select(vec3f(0.0, 1.0, 0.0), vec3f(0.0, 0.0, 1.0), abs(sun.y) > 0.99);
let axis_u = normalize(cross(sun, reference));
let axis_v = cross(axis_u, sun);
let projected = vec2f(dot(world_position, axis_u), dot(world_position, axis_v));
let uv = (projected - canopy_params.projected_extent.xy) / canopy_params.projected_extent.zw + vec2f(0.5);
if (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) { return 1.0; }
let texel = 1.0 / vec2f(textureDimensions(canopy_cookie));
let crown = vec3f(canopy_params.center_extent.x, canopy_params.wind.w, canopy_params.center_extent.y);
let distance = dot(crown - world_position, sun);
if (distance <= 0.0) { return 1.0; }
let radius = max(canopy_params.direction.w * distance * canopy_params.mode_sharpness.y / canopy_params.projected_extent.z, texel.x);
var shade = 0.0;
for (var y = -2; y <= 2; y++) {
for (var x = -2; x <= 2; x++) {
if (x * x + y * y <= 4) {
let offset = vec2f(f32(x), f32(y)) * radius * 0.5;
shade += textureSampleLevel(canopy_cookie, canopy_sampler, uv + offset, 0.0).r;
}
}
}
return 1.0 - shade / 13.0;
}
"#;