Cycles: improve light tree with large spot blend

In the original paper, the falloff inside `bcone.theta_e` is assumed to
be `pi/2`, which is too large for spot light and resulted in an
overestimation near the cone boundary.
To address this issue, attenuate the energy of a spot light using the
minimal possible angle formed by the light axis and the shading point
when traversing the light tree.

Ref: #122362

Pull Request: https://projects.blender.org/blender/blender/pulls/122667
This commit is contained in:
Weizhen Huang
2024-06-03 23:33:29 +02:00
committed by Weizhen Huang
parent f98b01e492
commit 296ac0e9ef
2 changed files with 18 additions and 3 deletions

View File

@@ -286,9 +286,11 @@ template<bool in_volume_segment>
ccl_device_forceinline bool spot_light_tree_parameters(const ccl_global KernelLight *klight,
const float3 centroid,
const float3 P,
const ccl_private BoundingCone &bcone,
ccl_private float &cos_theta_u,
ccl_private float2 &distance,
ccl_private float3 &point_to_centroid)
ccl_private float3 &point_to_centroid,
ccl_private float &energy)
{
float min_distance;
point_to_centroid = safe_normalize_len(centroid - P, &min_distance);
@@ -317,6 +319,18 @@ ccl_device_forceinline bool spot_light_tree_parameters(const ccl_global KernelLi
distance.x = hypotenus;
}
/* Apply a similar scaling as in `spot_light_attenuation()` to account for spot blend. */
{
/* Minimum angle formed by the emitter axis and the direction to the shading point,
* cos(theta') in the paper. */
const float cos_min_outgoing_angle = cosf(
fmaxf(0.0f, fast_acosf(dot(bcone.axis, -point_to_centroid)) - fast_acosf(cos_theta_u)));
/* Use `cos(bcone.theta_e)` instead of `klight->spot.cos_half_spot_angle` to account for
* non-uniform scaling. */
energy *= smoothstepf((cos_min_outgoing_angle - cosf(bcone.theta_e)) *
klight->spot.spot_smooth);
}
return true;
}

View File

@@ -420,6 +420,7 @@ ccl_device void light_tree_emitter_importance(KernelGlobals kg,
/* Early out if the emitter is guaranteed to be invisible. */
bool is_visible;
float energy = kemitter->energy;
if (is_triangle(kemitter)) {
is_visible = triangle_light_tree_parameters<in_volume_segment>(
kg, kemitter, centroid, P_c, N_or_D, bcone, cos_theta_u, distance, point_to_centroid);
@@ -431,7 +432,7 @@ ccl_device void light_tree_emitter_importance(KernelGlobals kg,
/* Function templates only modifies cos_theta_u when in_volume_segment = true. */
case LIGHT_SPOT:
is_visible = spot_light_tree_parameters<in_volume_segment>(
klight, centroid, P_c, cos_theta_u, distance, point_to_centroid);
klight, centroid, P_c, bcone, cos_theta_u, distance, point_to_centroid, energy);
break;
case LIGHT_POINT:
is_visible = point_light_tree_parameters<in_volume_segment>(
@@ -475,7 +476,7 @@ ccl_device void light_tree_emitter_importance(KernelGlobals kg,
bcone,
distance.x,
distance.y,
kemitter->energy,
energy,
max_importance,
min_importance);
}