diff --git a/intern/cycles/kernel/light/spot.h b/intern/cycles/kernel/light/spot.h index 8dc65168358..c5090573d4d 100644 --- a/intern/cycles/kernel/light/spot.h +++ b/intern/cycles/kernel/light/spot.h @@ -60,7 +60,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight, ls->t = FLT_MAX; if (d_sq > r_sq) { /* Outside sphere. */ - const float one_minus_cos_half_spot_spread = 1.0f - klight->spot.cos_half_spot_angle; + const float one_minus_cos_half_spot_spread = 1.0f - klight->spot.cos_half_larger_spread; const float one_minus_cos_half_angle = sin_sqr_to_one_minus_cos(r_sq / d_sq); if (in_volume_segment || one_minus_cos_half_angle < one_minus_cos_half_spot_spread) { @@ -147,7 +147,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight, return true; } -ccl_device_forceinline float spot_light_pdf(const float cos_half_spread, +ccl_device_forceinline float spot_light_pdf(const ccl_global KernelSpotLight *spot, const float d_sq, const float r_sq, const float3 N, @@ -155,7 +155,8 @@ ccl_device_forceinline float spot_light_pdf(const float cos_half_spread, const uint32_t path_flag) { if (d_sq > r_sq) { - return M_1_2PI_F / min(sin_sqr_to_one_minus_cos(r_sq / d_sq), 1.0f - cos_half_spread); + return M_1_2PI_F / + min(sin_sqr_to_one_minus_cos(r_sq / d_sq), 1.0f - spot->cos_half_larger_spread); } const bool has_transmission = (path_flag & PATH_RAY_MIS_HAD_TRANSMISSION); @@ -183,7 +184,7 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne /* NOTE : preserve pdf in area measure. */ const float jacobian_solid_angle_to_area = 0.5f * fabsf(d_sq - r_sq - t_sq) / (radius * ls->t * t_sq); - ls->pdf = spot_light_pdf(klight->spot.cos_half_spot_angle, d_sq, r_sq, N, ls->D, path_flag) * + ls->pdf = spot_light_pdf(&klight->spot, d_sq, r_sq, N, ls->D, path_flag) * jacobian_solid_angle_to_area; ls->Ng = normalize(ls->P - klight->co); @@ -234,7 +235,7 @@ ccl_device_inline bool spot_light_sample_from_intersection( ls->eval_fac = klight->spot.eval_fac; if (klight->spot.is_sphere) { - ls->pdf = spot_light_pdf(klight->spot.cos_half_spot_angle, d_sq, r_sq, N, ray_D, path_flag); + ls->pdf = spot_light_pdf(&klight->spot, d_sq, r_sq, N, ray_D, path_flag); ls->Ng = normalize(ls->P - klight->co); } else { diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index c44c571426e..2ed5a790199 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -1374,6 +1374,8 @@ typedef struct KernelSpotLight { float half_cot_half_spot_angle; float spot_smooth; int is_sphere; + /* For non-uniform object scaling, the actual spread might be different. */ + float cos_half_larger_spread; } KernelSpotLight; /* PointLight is SpotLight with only radius and invarea being used. */ diff --git a/intern/cycles/scene/light.cpp b/intern/cycles/scene/light.cpp index 0dc5e231162..78d237bcd8c 100644 --- a/intern/cycles/scene/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -1346,13 +1346,22 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce klights[light_index].area.normalize_spread = normalize_spread; } if (light->light_type == LIGHT_SPOT) { - float cos_half_spot_angle = cosf(light->spot_angle * 0.5f); - float spot_smooth = 1.0f / ((1.0f - cos_half_spot_angle) * light->spot_smooth); + const float cos_half_spot_angle = cosf(light->spot_angle * 0.5f); + const float spot_smooth = 1.0f / ((1.0f - cos_half_spot_angle) * light->spot_smooth); + const float tan_half_spot_angle = tanf(light->spot_angle * 0.5f); + + const float len_w_sq = len_squared(light->get_dir()); + const float len_u_sq = len_squared(light->get_axisu()); + const float len_v_sq = len_squared(light->get_axisv()); + const float tan_sq = sqr(tan_half_spot_angle); klights[light_index].spot.dir = safe_normalize(light->get_dir()); klights[light_index].spot.cos_half_spot_angle = cos_half_spot_angle; - klights[light_index].spot.half_cot_half_spot_angle = 0.5f / tanf(light->spot_angle * 0.5f); + klights[light_index].spot.half_cot_half_spot_angle = 0.5f / tan_half_spot_angle; klights[light_index].spot.spot_smooth = spot_smooth; + /* Choose the angle which spans a larger cone. */ + klights[light_index].spot.cos_half_larger_spread = inversesqrtf( + 1.0f + tan_sq * fmaxf(len_u_sq, len_v_sq) / len_w_sq); } klights[light_index].shader_id = shader_id;