From ae217754deab3d16fd27c61560138e3185d280d7 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 4 Feb 2024 16:54:40 +0100 Subject: [PATCH 01/13] Refactor: Changes to point and sphere light Rename some functions, add comment, reshuffle code in preparation of adding an option for old point light behavior. --- intern/cycles/kernel/light/area.h | 4 +- intern/cycles/kernel/light/background.h | 4 +- intern/cycles/kernel/light/common.h | 2 +- intern/cycles/kernel/light/point.h | 41 ++++++----- intern/cycles/kernel/light/spot.h | 92 +++++++++++++------------ 5 files changed, 76 insertions(+), 67 deletions(-) diff --git a/intern/cycles/kernel/light/area.h b/intern/cycles/kernel/light/area.h index 197aefb4216..807ada0de03 100644 --- a/intern/cycles/kernel/light/area.h +++ b/intern/cycles/kernel/light/area.h @@ -308,7 +308,7 @@ ccl_device_inline bool area_light_eval(const ccl_global KernelLight *klight, } if (in_volume_segment || (!sample_rectangle && klight->area.tan_half_spread > 0)) { - ls->pdf *= lamp_light_pdf(Ng, -ls->D, ls->t); + ls->pdf *= light_pdf_area_to_solid_angle(Ng, -ls->D, ls->t); } return ls->eval_fac > 0; @@ -368,7 +368,7 @@ ccl_device_forceinline void area_light_mnee_sample_update(const ccl_global Kerne ls->D = normalize_len(ls->P - P, &ls->t); area_light_eval(klight, P, &ls->P, ls, zero_float2(), false); /* Convert pdf to be in area measure. */ - ls->pdf /= lamp_light_pdf(ls->Ng, -ls->D, ls->t); + ls->pdf /= light_pdf_area_to_solid_angle(ls->Ng, -ls->D, ls->t); } } diff --git a/intern/cycles/kernel/light/background.h b/intern/cycles/kernel/light/background.h index 002fa6e4b74..50eb60b83e1 100644 --- a/intern/cycles/kernel/light/background.h +++ b/intern/cycles/kernel/light/background.h @@ -193,7 +193,7 @@ ccl_device_inline float background_portal_pdf( if (is_round) { float t; float3 D = normalize_len(lightpos - P, &t); - portal_pdf += fabsf(klight->area.invarea) * lamp_light_pdf(dir, -D, t); + portal_pdf += fabsf(klight->area.invarea) * light_pdf_area_to_solid_angle(dir, -D, t); } else { portal_pdf += area_light_rect_sample( @@ -256,7 +256,7 @@ ccl_device float3 background_portal_sample(KernelGlobals kg, lightpos += ellipse_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, rand); float t; D = normalize_len(lightpos - P, &t); - *pdf = fabsf(klight->area.invarea) * lamp_light_pdf(dir, -D, t); + *pdf = fabsf(klight->area.invarea) * light_pdf_area_to_solid_angle(dir, -D, t); } else { *pdf = area_light_rect_sample(P, &lightpos, axis_u, len_u, axis_v, len_v, rand, true); diff --git a/intern/cycles/kernel/light/common.h b/intern/cycles/kernel/light/common.h index df903e1f601..ea724991817 100644 --- a/intern/cycles/kernel/light/common.h +++ b/intern/cycles/kernel/light/common.h @@ -49,7 +49,7 @@ ccl_device float3 disk_light_sample(float3 n, float2 rand) return ellipse_sample(ru, rv, rand); } -ccl_device float lamp_light_pdf(const float3 Ng, const float3 I, float t) +ccl_device float light_pdf_area_to_solid_angle(const float3 Ng, const float3 I, float t) { float cos_pi = dot(Ng, I); diff --git a/intern/cycles/kernel/light/point.h b/intern/cycles/kernel/light/point.h index 92dd02c96f4..dc63a5da793 100644 --- a/intern/cycles/kernel/light/point.h +++ b/intern/cycles/kernel/light/point.h @@ -15,19 +15,24 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, const int shader_flags, ccl_private LightSample *ls) { + const float r_sq = sqr(klight->spot.radius); + float3 lightN = P - klight->co; const float d_sq = len_squared(lightN); const float d = sqrtf(d_sq); lightN /= d; - const float r_sq = sqr(klight->spot.radius); + ls->eval_fac = klight->spot.eval_fac; + /* Spherical light geometry. */ float cos_theta; if (d_sq > r_sq) { + /* Outside sphere. */ const float one_minus_cos = sin_sqr_to_one_minus_cos(r_sq / d_sq); ls->D = sample_uniform_cone(-lightN, one_minus_cos, rand, &cos_theta, &ls->pdf); } else { + /* Inside sphere. */ const bool has_transmission = (shader_flags & SD_BSDF_HAS_TRANSMISSION); if (has_transmission) { ls->D = sample_uniform_sphere(rand); @@ -44,7 +49,6 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, ls->P = P + ls->D * ls->t; - ls->eval_fac = klight->spot.eval_fac; if (r_sq == 0) { /* Use intensity instead of radiance for point light. */ ls->eval_fac /= sqr(ls->t); @@ -57,6 +61,7 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, ls->P = ls->Ng * klight->spot.radius + klight->co; } + /* Texture coordinates. */ const Transform itfm = klight->itfm; const float2 uv = map_to_sphere(transform_direction(&itfm, ls->Ng)); /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */ @@ -92,21 +97,19 @@ ccl_device_forceinline void point_light_mnee_sample_update(const ccl_global Kern const float r_sq = sqr(radius); const float t_sq = sqr(ls->t); - ls->pdf = point_light_pdf(d_sq, r_sq, N, ls->D, path_flag); - /* NOTE : preserve pdf in area measure. */ - ls->pdf *= 0.5f * fabsf(d_sq - r_sq - t_sq) / (radius * ls->t * t_sq); + const float pdf_solid_angle_to_area = 0.5f * fabsf(d_sq - r_sq - t_sq) / + (radius * ls->t * t_sq); + ls->pdf = point_light_pdf(d_sq, r_sq, N, ls->D, path_flag) * pdf_solid_angle_to_area; ls->Ng = normalize(ls->P - klight->co); } else { - ls->eval_fac = klight->spot.eval_fac; - - ls->Ng = -ls->D; - /* PDF does not change. */ + ls->Ng = -ls->D; } + /* Texture coordinates. */ const Transform itfm = klight->itfm; const float2 uv = map_to_sphere(transform_direction(&itfm, ls->Ng)); /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */ @@ -136,25 +139,24 @@ ccl_device_inline bool point_light_sample_from_intersection( const uint32_t path_flag, ccl_private LightSample *ccl_restrict ls) { - ls->eval_fac = klight->spot.eval_fac; - const float radius = klight->spot.radius; - ls->Ng = radius > 0 ? normalize(ls->P - klight->co) : -ray_D; + ls->eval_fac = klight->spot.eval_fac; + if (radius > 0) { + ls->Ng = normalize(ls->P - klight->co); + } + else { + ls->Ng = -ray_D; + } + + /* Texture coordinates. */ const Transform itfm = klight->itfm; const float2 uv = map_to_sphere(transform_direction(&itfm, ls->Ng)); /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */ ls->u = uv.y; ls->v = 1.0f - uv.x - uv.y; - if (ls->t == FLT_MAX) { - ls->pdf = 0.0f; - } - else { - ls->pdf = point_light_pdf(len_squared(ray_P - klight->co), sqr(radius), N, ray_D, path_flag); - } - return true; } @@ -175,6 +177,7 @@ ccl_device_forceinline bool point_light_tree_parameters(const ccl_global KernelL point_to_centroid = safe_normalize_len(centroid - P, &dist_point_to_centroid); const float radius = klight->spot.radius; + if (dist_point_to_centroid > radius) { /* Equivalent to a disk light with the same angular span. */ cos_theta_u = cos_from_sin(radius / dist_point_to_centroid); diff --git a/intern/cycles/kernel/light/spot.h b/intern/cycles/kernel/light/spot.h index 10491b97541..a0912f95e18 100644 --- a/intern/cycles/kernel/light/spot.h +++ b/intern/cycles/kernel/light/spot.h @@ -43,19 +43,20 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight, const int shader_flags, ccl_private LightSample *ls) { - const float radius = klight->spot.radius; const float r_sq = sqr(klight->spot.radius); - const float3 center = klight->co; - - float3 lightN = P - center; + float3 lightN = P - klight->co; const float d_sq = len_squared(lightN); const float d = sqrtf(d_sq); lightN /= d; + ls->eval_fac = klight->spot.eval_fac; + + /* Spherical light geometry. */ float cos_theta; 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_angle = sin_sqr_to_one_minus_cos(r_sq / d_sq); @@ -68,13 +69,16 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight, ls->D = sample_uniform_cone( -klight->spot.dir, one_minus_cos_half_spot_spread, rand, &cos_theta, &ls->pdf); - if (!ray_sphere_intersect(P, ls->D, 0.0f, FLT_MAX, center, radius, &ls->P, &ls->t)) { + if (!ray_sphere_intersect( + P, ls->D, 0.0f, FLT_MAX, klight->co, klight->spot.radius, &ls->P, &ls->t)) + { /* Sampled direction does not intersect with the light. */ return false; } } } else { + /* Inside sphere. */ const bool has_transmission = (shader_flags & SD_BSDF_HAS_TRANSMISSION); if (has_transmission) { ls->D = sample_uniform_sphere(rand); @@ -86,6 +90,15 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight, cos_theta = -dot(ls->D, lightN); } + /* Attenuation. */ + const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D); + if (d_sq > r_sq) { + ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); + } + if (!in_volume_segment && ls->eval_fac == 0.0f) { + return false; + } + if (ls->t == FLT_MAX) { /* Law of cosines. */ ls->t = d * cos_theta - @@ -96,28 +109,11 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight, /* Already computed when sampling the spread cone. */ } - const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D); - ls->eval_fac = klight->spot.eval_fac; - if (d_sq > r_sq) { - ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); - } - - if (!in_volume_segment && ls->eval_fac == 0.0f) { - return false; - } - - if (r_sq == 0) { - /* Use intensity instead of radiance when the radius is zero. */ - ls->eval_fac /= sqr(ls->t); - /* `ls->Ng` is not well-defined when the radius is zero, use the incoming direction instead. */ - ls->Ng = -ls->D; - } - else { - ls->Ng = normalize(ls->P - center); - /* Remap sampled point onto the sphere to prevent precision issues with small radius. */ - ls->P = ls->Ng * radius + center; - } + /* Remap sampled point onto the sphere to prevent precision issues with small radius. */ + ls->Ng = normalize(ls->P - klight->co); + ls->P = ls->Ng * klight->spot.radius + klight->co; + /* Texture coordinates. */ spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v); return true; @@ -146,35 +142,38 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne { ls->D = normalize_len(ls->P - P, &ls->t); - const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D); ls->eval_fac = klight->spot.eval_fac; const float radius = klight->spot.radius; + bool use_attenuation = true; if (radius > 0) { const float d_sq = len_squared(P - klight->co); const float r_sq = sqr(radius); const float t_sq = sqr(ls->t); - ls->pdf = spot_light_pdf(klight->spot.cos_half_spot_angle, d_sq, r_sq, N, ls->D, path_flag); - /* NOTE : preserve pdf in area measure. */ - ls->pdf *= 0.5f * fabsf(d_sq - r_sq - t_sq) / (radius * ls->t * t_sq); + const float pdf_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) * + pdf_solid_angle_to_area; ls->Ng = normalize(ls->P - klight->co); - if (d_sq > r_sq) { - ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); - } + use_attenuation = (d_sq > r_sq); } else { - ls->Ng = -ls->D; - - ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); - /* PDF does not change. */ + ls->Ng = -ls->D; } + /* Attenuation. */ + const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D); + if (use_attenuation) { + ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); + } + + /* Texture coordinates. */ spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v); } @@ -199,13 +198,20 @@ ccl_device_inline bool spot_light_sample_from_intersection( const uint32_t path_flag, ccl_private LightSample *ccl_restrict ls) { - const float d_sq = len_squared(ray_P - klight->co); const float r_sq = sqr(klight->spot.radius); + const float d_sq = len_squared(ray_P - klight->co); - ls->pdf = spot_light_pdf(klight->spot.cos_half_spot_angle, d_sq, r_sq, N, ray_D, path_flag); - - const float3 local_ray = spot_light_to_local(&klight->spot, -ray_D); ls->eval_fac = klight->spot.eval_fac; + + if (r_sq > 0) { + ls->Ng = normalize(ls->P - klight->co); + } + else { + ls->Ng = -ray_D; + } + + /* Attenuation. */ + const float3 local_ray = spot_light_to_local(&klight->spot, -ray_D); if (d_sq > r_sq) { ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); } @@ -213,8 +219,7 @@ ccl_device_inline bool spot_light_sample_from_intersection( return false; } - ls->Ng = r_sq > 0 ? normalize(ls->P - klight->co) : -ray_D; - + /* Texture coordinates. */ spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v); return true; @@ -232,6 +237,7 @@ ccl_device_forceinline bool spot_light_tree_parameters(const ccl_global KernelLi const float3 point_to_centroid_ = safe_normalize_len(centroid - P, &dist_point_to_centroid); const float radius = klight->spot.radius; + cos_theta_u = (dist_point_to_centroid > radius) ? cos_from_sin(radius / dist_point_to_centroid) : -1.0f; -- 2.30.2 From e3c8c3163f67a1de250d3f2ed3b5eeb4e677d547 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 21 Nov 2023 15:47:39 +0100 Subject: [PATCH 02/13] Cycles: Internal changes for option for old point light --- intern/cycles/kernel/light/point.h | 116 +++++++++++++------- intern/cycles/kernel/light/spot.h | 170 ++++++++++++++++++----------- intern/cycles/kernel/types.h | 2 +- intern/cycles/scene/light.cpp | 3 + intern/cycles/scene/light.h | 2 + 5 files changed, 190 insertions(+), 103 deletions(-) diff --git a/intern/cycles/kernel/light/point.h b/intern/cycles/kernel/light/point.h index dc63a5da793..fd9e3e45a30 100644 --- a/intern/cycles/kernel/light/point.h +++ b/intern/cycles/kernel/light/point.h @@ -24,41 +24,49 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, ls->eval_fac = klight->spot.eval_fac; - /* Spherical light geometry. */ - float cos_theta; - if (d_sq > r_sq) { - /* Outside sphere. */ - const float one_minus_cos = sin_sqr_to_one_minus_cos(r_sq / d_sq); - ls->D = sample_uniform_cone(-lightN, one_minus_cos, rand, &cos_theta, &ls->pdf); - } - else { - /* Inside sphere. */ - const bool has_transmission = (shader_flags & SD_BSDF_HAS_TRANSMISSION); - if (has_transmission) { - ls->D = sample_uniform_sphere(rand); - ls->pdf = M_1_2PI_F * 0.5f; + if (klight->spot.is_sphere) { + /* Spherical light geometry. */ + float cos_theta; + if (d_sq > r_sq) { + /* Outside sphere. */ + const float one_minus_cos = sin_sqr_to_one_minus_cos(r_sq / d_sq); + ls->D = sample_uniform_cone(-lightN, one_minus_cos, rand, &cos_theta, &ls->pdf); } else { - sample_cos_hemisphere(N, rand, &ls->D, &ls->pdf); + /* Inside sphere. */ + const bool has_transmission = (shader_flags & SD_BSDF_HAS_TRANSMISSION); + if (has_transmission) { + ls->D = sample_uniform_sphere(rand); + ls->pdf = M_1_2PI_F * 0.5f; + } + else { + sample_cos_hemisphere(N, rand, &ls->D, &ls->pdf); + } + cos_theta = -dot(ls->D, lightN); } - cos_theta = -dot(ls->D, lightN); - } - /* Law of cosines. */ - ls->t = d * cos_theta - copysignf(safe_sqrtf(r_sq - d_sq + d_sq * sqr(cos_theta)), d_sq - r_sq); + /* Law of cosines. */ + ls->t = d * cos_theta - + copysignf(safe_sqrtf(r_sq - d_sq + d_sq * sqr(cos_theta)), d_sq - r_sq); - ls->P = P + ls->D * ls->t; - - if (r_sq == 0) { - /* Use intensity instead of radiance for point light. */ - ls->eval_fac /= sqr(ls->t); - /* `ls->Ng` is not well-defined for point light, so use the incoming direction instead. */ - ls->Ng = -ls->D; + /* Remap sampled point onto the sphere to prevent precision issues with small radius. */ + ls->P = P + ls->D * ls->t; + ls->Ng = normalize(ls->P - klight->co); + ls->P = ls->Ng * klight->spot.radius + klight->co; } else { - ls->Ng = normalize(ls->P - klight->co); - /* Remap sampled point onto the sphere to prevent precision issues with small radius. */ - ls->P = ls->Ng * klight->spot.radius + klight->co; + /* Point light with ad-hoc radius based on orienteded disk. */ + ls->P = klight->co; + if (r_sq > 0.0f) { + ls->P += disk_light_sample(lightN, rand) * klight->spot.radius; + } + + ls->D = normalize_len(ls->P - P, &ls->t); + ls->Ng = -ls->D; + + /* PDF. */ + const float invarea = (r_sq > 0.0f) ? 1.0f / (r_sq * M_PI_F) : 1.0f; + ls->pdf = invarea * light_pdf_area_to_solid_angle(lightN, -ls->D, ls->t); } /* Texture coordinates. */ @@ -92,7 +100,7 @@ ccl_device_forceinline void point_light_mnee_sample_update(const ccl_global Kern const float radius = klight->spot.radius; - if (radius > 0) { + if (klight->spot.is_sphere) { const float d_sq = len_squared(P - klight->co); const float r_sq = sqr(radius); const float t_sq = sqr(ls->t); @@ -126,8 +134,16 @@ ccl_device_inline bool point_light_intersect(const ccl_global KernelLight *kligh return false; } - float3 P; - return ray_sphere_intersect(ray->P, ray->D, ray->tmin, ray->tmax, klight->co, radius, &P, t); + if (klight->spot.is_sphere) { + float3 P; + return ray_sphere_intersect(ray->P, ray->D, ray->tmin, ray->tmax, klight->co, radius, &P, t); + } + else { + float3 P; + const float3 diskN = normalize(ray->P - klight->co); + return ray_disk_intersect( + ray->P, ray->D, ray->tmin, ray->tmax, klight->co, diskN, radius, &P, t); + } } ccl_device_inline bool point_light_sample_from_intersection( @@ -139,14 +155,24 @@ ccl_device_inline bool point_light_sample_from_intersection( const uint32_t path_flag, ccl_private LightSample *ccl_restrict ls) { - const float radius = klight->spot.radius; + const float r_sq = sqr(klight->spot.radius); ls->eval_fac = klight->spot.eval_fac; - if (radius > 0) { + if (klight->spot.is_sphere) { + const float d_sq = len_squared(ray_P - klight->co); + ls->pdf = point_light_pdf(d_sq, r_sq, N, ray_D, path_flag); ls->Ng = normalize(ls->P - klight->co); } else { + if (ls->t != FLT_MAX) { + const float3 lightN = normalize(ray_P - klight->co); + const float invarea = (r_sq > 0.0f) ? 1.0f / (r_sq * M_PI_F) : 1.0f; + ls->pdf = invarea * light_pdf_area_to_solid_angle(lightN, -ray_D, ls->t); + } + else { + ls->pdf = 0.0f; + } ls->Ng = -ray_D; } @@ -178,16 +204,24 @@ ccl_device_forceinline bool point_light_tree_parameters(const ccl_global KernelL const float radius = klight->spot.radius; - if (dist_point_to_centroid > radius) { - /* Equivalent to a disk light with the same angular span. */ - cos_theta_u = cos_from_sin(radius / dist_point_to_centroid); - distance = dist_point_to_centroid * make_float2(1.0f / cos_theta_u, 1.0f); + if (klight->spot.is_sphere) { + if (dist_point_to_centroid > radius) { + /* Equivalent to a disk light with the same angular span. */ + cos_theta_u = cos_from_sin(radius / dist_point_to_centroid); + distance = dist_point_to_centroid * make_float2(1.0f / cos_theta_u, 1.0f); + } + else { + /* Similar to background light. */ + cos_theta_u = -1.0f; + /* HACK: pack radiance scaling in the distance. */ + distance = one_float2() * radius / M_SQRT2_F; + } } else { - /* Similar to background light. */ - cos_theta_u = -1.0f; - /* HACK: pack radiance scaling in the distance. */ - distance = one_float2() * radius / M_SQRT2_F; + const float hypotenus = sqrtf(sqr(radius) + sqr(dist_point_to_centroid)); + cos_theta_u = dist_point_to_centroid / hypotenus; + + distance = make_float2(hypotenus, dist_point_to_centroid); } return true; diff --git a/intern/cycles/kernel/light/spot.h b/intern/cycles/kernel/light/spot.h index a0912f95e18..d22a8b24595 100644 --- a/intern/cycles/kernel/light/spot.h +++ b/intern/cycles/kernel/light/spot.h @@ -52,70 +52,96 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight, ls->eval_fac = klight->spot.eval_fac; - /* Spherical light geometry. */ - float cos_theta; - 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_angle = sin_sqr_to_one_minus_cos(r_sq / d_sq); + if (klight->spot.is_sphere) { + /* Spherical light geometry. */ + float cos_theta; + 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_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) { - /* Sample visible part of the sphere. */ - ls->D = sample_uniform_cone(-lightN, one_minus_cos_half_angle, rand, &cos_theta, &ls->pdf); - } - else { - /* Sample spread cone. */ - ls->D = sample_uniform_cone( - -klight->spot.dir, one_minus_cos_half_spot_spread, rand, &cos_theta, &ls->pdf); + if (in_volume_segment || one_minus_cos_half_angle < one_minus_cos_half_spot_spread) { + /* Sample visible part of the sphere. */ + ls->D = sample_uniform_cone(-lightN, one_minus_cos_half_angle, rand, &cos_theta, &ls->pdf); + } + else { + /* Sample spread cone. */ + ls->D = sample_uniform_cone( + -klight->spot.dir, one_minus_cos_half_spot_spread, rand, &cos_theta, &ls->pdf); - if (!ray_sphere_intersect( - P, ls->D, 0.0f, FLT_MAX, klight->co, klight->spot.radius, &ls->P, &ls->t)) - { - /* Sampled direction does not intersect with the light. */ - return false; + if (!ray_sphere_intersect( + P, ls->D, 0.0f, FLT_MAX, klight->co, klight->spot.radius, &ls->P, &ls->t)) + { + /* Sampled direction does not intersect with the light. */ + return false; + } } } - } - else { - /* Inside sphere. */ - const bool has_transmission = (shader_flags & SD_BSDF_HAS_TRANSMISSION); - if (has_transmission) { - ls->D = sample_uniform_sphere(rand); - ls->pdf = M_1_2PI_F * 0.5f; + else { + /* Inside sphere. */ + const bool has_transmission = (shader_flags & SD_BSDF_HAS_TRANSMISSION); + if (has_transmission) { + ls->D = sample_uniform_sphere(rand); + ls->pdf = M_1_2PI_F * 0.5f; + } + else { + sample_cos_hemisphere(N, rand, &ls->D, &ls->pdf); + } + cos_theta = -dot(ls->D, lightN); + } + + /* Attenuation. */ + const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D); + if (d_sq > r_sq) { + ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); + } + if (!in_volume_segment && ls->eval_fac == 0.0f) { + return false; + } + + if (ls->t == FLT_MAX) { + /* Law of cosines. */ + ls->t = d * cos_theta - + copysignf(safe_sqrtf(r_sq - d_sq + d_sq * sqr(cos_theta)), d_sq - r_sq); + ls->P = P + ls->D * ls->t; } else { - sample_cos_hemisphere(N, rand, &ls->D, &ls->pdf); + /* Already computed when sampling the spread cone. */ } - cos_theta = -dot(ls->D, lightN); - } - /* Attenuation. */ - const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D); - if (d_sq > r_sq) { - ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); - } - if (!in_volume_segment && ls->eval_fac == 0.0f) { - return false; - } + /* Remap sampled point onto the sphere to prevent precision issues with small radius. */ + ls->Ng = normalize(ls->P - klight->co); + ls->P = ls->Ng * klight->spot.radius + klight->co; - if (ls->t == FLT_MAX) { - /* Law of cosines. */ - ls->t = d * cos_theta - - copysignf(safe_sqrtf(r_sq - d_sq + d_sq * sqr(cos_theta)), d_sq - r_sq); - ls->P = P + ls->D * ls->t; + /* Texture coordinates. */ + spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v); } else { - /* Already computed when sampling the spread cone. */ + /* Point light with ad-hoc radius based on orienteded disk. */ + ls->P = klight->co; + if (r_sq > 0.0f) { + ls->P += disk_light_sample(lightN, rand) * klight->spot.radius; + } + + ls->D = normalize_len(ls->P - P, &ls->t); + ls->Ng = -ls->D; + + /* Attenuation. */ + const float3 local_ray = spot_light_to_local(&klight->spot, -ls->D); + ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); + if (!in_volume_segment && ls->eval_fac == 0.0f) { + return false; + } + + /* PDF. */ + const float invarea = (r_sq > 0.0f) ? 1.0f / (r_sq * M_PI_F) : 1.0f; + ls->pdf = invarea * light_pdf_area_to_solid_angle(lightN, -ls->D, ls->t); + + /* Texture coordinates. */ + spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v); } - /* Remap sampled point onto the sphere to prevent precision issues with small radius. */ - ls->Ng = normalize(ls->P - klight->co); - ls->P = ls->Ng * klight->spot.radius + klight->co; - - /* Texture coordinates. */ - spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v); - return true; } @@ -147,7 +173,7 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne const float radius = klight->spot.radius; bool use_attenuation = true; - if (radius > 0) { + if (klight->spot.is_sphere) { const float d_sq = len_squared(P - klight->co); const float r_sq = sqr(radius); const float t_sq = sqr(ls->t); @@ -203,16 +229,25 @@ ccl_device_inline bool spot_light_sample_from_intersection( ls->eval_fac = klight->spot.eval_fac; - if (r_sq > 0) { + 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->Ng = normalize(ls->P - klight->co); } else { + if (ls->t != FLT_MAX) { + const float3 lightN = normalize(ray_P - klight->co); + const float invarea = (r_sq > 0.0f) ? 1.0f / (r_sq * M_PI_F) : 1.0f; + ls->pdf = invarea * light_pdf_area_to_solid_angle(lightN, -ray_D, ls->t); + } + else { + ls->pdf = 0.0f; + } ls->Ng = -ray_D; } /* Attenuation. */ const float3 local_ray = spot_light_to_local(&klight->spot, -ray_D); - if (d_sq > r_sq) { + if (!klight->spot.is_sphere || d_sq > r_sq) { ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); } if (ls->eval_fac == 0) { @@ -238,16 +273,29 @@ ccl_device_forceinline bool spot_light_tree_parameters(const ccl_global KernelLi const float radius = klight->spot.radius; - cos_theta_u = (dist_point_to_centroid > radius) ? cos_from_sin(radius / dist_point_to_centroid) : - -1.0f; + if (klight->spot.is_sphere) { + cos_theta_u = (dist_point_to_centroid > radius) ? + cos_from_sin(radius / dist_point_to_centroid) : + -1.0f; - if (in_volume_segment) { - return true; + if (in_volume_segment) { + return true; + } + + distance = (dist_point_to_centroid > radius) ? + dist_point_to_centroid * make_float2(1.0f / cos_theta_u, 1.0f) : + one_float2() * radius / M_SQRT2_F; } + else { + const float hypotenus = sqrtf(sqr(radius) + sqr(dist_point_to_centroid)); + cos_theta_u = dist_point_to_centroid / hypotenus; - distance = (dist_point_to_centroid > radius) ? - dist_point_to_centroid * make_float2(1.0f / cos_theta_u, 1.0f) : - one_float2() * radius / M_SQRT2_F; + if (in_volume_segment) { + return true; + } + + distance = make_float2(hypotenus, dist_point_to_centroid); + } point_to_centroid = point_to_centroid_; return true; diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index eea1df19392..b50a0c192a1 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -1369,7 +1369,7 @@ typedef struct KernelSpotLight { float half_cot_half_spot_angle; float inv_len_z; float spot_smooth; - float pad; + int is_sphere; } 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 a0e5e83dd09..16483f0b798 100644 --- a/intern/cycles/scene/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -110,6 +110,8 @@ NODE_DEFINE(Light) SOCKET_INT(map_resolution, "Map Resolution", 0); SOCKET_FLOAT(average_radiance, "Average Radiance", 0.0f); + SOCKET_BOOLEAN(is_sphere, "Is Sphere", true); + SOCKET_FLOAT(spot_angle, "Spot Angle", M_PI_4_F); SOCKET_FLOAT(spot_smooth, "Spot Smooth", 0.0f); @@ -1253,6 +1255,7 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce klights[light_index].co = light->get_co(); klights[light_index].spot.radius = radius; klights[light_index].spot.eval_fac = eval_fac; + klights[light_index].spot.is_sphere = light->get_is_sphere() && radius != 0.0f; } else if (light->light_type == LIGHT_DISTANT) { shader_id &= ~SHADER_AREA_LIGHT; diff --git a/intern/cycles/scene/light.h b/intern/cycles/scene/light.h index 5b89160b371..8d6c1eac2db 100644 --- a/intern/cycles/scene/light.h +++ b/intern/cycles/scene/light.h @@ -48,6 +48,8 @@ class Light : public Node { NODE_SOCKET_API(int, map_resolution) NODE_SOCKET_API(float, average_radiance) + NODE_SOCKET_API(bool, is_sphere) + NODE_SOCKET_API(float, spot_angle) NODE_SOCKET_API(float, spot_smooth) -- 2.30.2 From 068a67a2d4ae701790a8a1d86db49d0c800f8410 Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Tue, 6 Feb 2024 16:07:58 +0100 Subject: [PATCH 03/13] UI: add Shape option for point and spot light to use sphere/aligned disk. Also pass the option to Cycles. --- intern/cycles/blender/addon/ui.py | 2 ++ intern/cycles/blender/light.cpp | 2 ++ .../startup/bl_ui/properties_data_light.py | 1 + source/blender/makesdna/DNA_light_types.h | 9 ++++++- source/blender/makesrna/intern/rna_light.cc | 24 +++++++++++++++++++ 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 05a0f6c03c7..f1522a5a50b 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1573,6 +1573,7 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel): col.separator() if light.type in {'POINT', 'SPOT'}: + col.prop(light, "shape", text="Shape") col.prop(light, "shadow_soft_size", text="Radius") elif light.type == 'SUN': col.prop(light, "angle") @@ -1587,6 +1588,7 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel): sub.prop(light, "size_y", text="Y") if not (light.type == 'AREA' and clamp.is_portal): + col.separator() sub = col.column() sub.prop(clamp, "max_bounces") diff --git a/intern/cycles/blender/light.cpp b/intern/cycles/blender/light.cpp index 23c780c7831..fe621c85e12 100644 --- a/intern/cycles/blender/light.cpp +++ b/intern/cycles/blender/light.cpp @@ -48,6 +48,7 @@ void BlenderSync::sync_light(BL::Object &b_parent, BL::PointLight b_point_light(b_light); light->set_size(b_point_light.shadow_soft_size()); light->set_light_type(LIGHT_POINT); + light->set_is_sphere(b_point_light.shape() == BL::PointLight::shape_SPHERE); break; } case BL::Light::type_SPOT: { @@ -56,6 +57,7 @@ void BlenderSync::sync_light(BL::Object &b_parent, light->set_light_type(LIGHT_SPOT); light->set_spot_angle(b_spot_light.spot_size()); light->set_spot_smooth(b_spot_light.spot_blend()); + light->set_is_sphere(b_spot_light.shape() == BL::SpotLight::shape_SPHERE); break; } /* Hemi were removed from 2.8 */ diff --git a/scripts/startup/bl_ui/properties_data_light.py b/scripts/startup/bl_ui/properties_data_light.py index 87e1902af18..66ddfc2f82f 100644 --- a/scripts/startup/bl_ui/properties_data_light.py +++ b/scripts/startup/bl_ui/properties_data_light.py @@ -98,6 +98,7 @@ class DATA_PT_EEVEE_light(DataButtonsPanel, Panel): col.separator() if light.type in {'POINT', 'SPOT'}: + col.prop(light, "shape", text="Shape") col.prop(light, "shadow_soft_size", text="Radius") elif light.type == 'SUN': col.prop(light, "angle") diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h index b9863f30791..2a31015e80f 100644 --- a/source/blender/makesdna/DNA_light_types.h +++ b/source/blender/makesdna/DNA_light_types.h @@ -39,6 +39,8 @@ typedef struct Light { float radius; /* Spot Light. */ + short spot_shape; + short _pad3; float spotsize; float spotblend; @@ -77,7 +79,6 @@ typedef struct Light { float spec_fac, att_dist; float shadow_softness_factor; float shadow_trace_distance; - float _pad3; /* Preview */ struct PreviewImage *preview; @@ -160,3 +161,9 @@ enum { LA_AREA_DISK = 4, LA_AREA_ELLIPSE = 5, }; + +/** #Light::spot_shape */ +enum { + LA_SPOT_SPHERE = 0, + LA_SPOT_DISK = 1, +}; diff --git a/source/blender/makesrna/intern/rna_light.cc b/source/blender/makesrna/intern/rna_light.cc index b3791cbfb1f..a4cb9291b62 100644 --- a/source/blender/makesrna/intern/rna_light.cc +++ b/source/blender/makesrna/intern/rna_light.cc @@ -337,6 +337,17 @@ static void rna_def_light_shadow(StructRNA *srna, bool sun) } } +const EnumPropertyItem prop_spotshape_items[] = { + {LA_SPOT_SPHERE, "SPHERE", 0, "Sphere", "Emits light from a double-sided sphere geometry"}, + {LA_SPOT_DISK, + "DISK", + 0, + "Aligned Disk", + "The light appears to be a disk pointing towards the shading point. This shape is not " + "physically feasible, but gives softer shadows compared to sphere light"}, + {0, nullptr, 0, nullptr, nullptr}, +}; + static void rna_def_point_light(BlenderRNA *brna) { StructRNA *srna; @@ -346,6 +357,13 @@ static void rna_def_point_light(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Point Light", "Omnidirectional point Light"); RNA_def_struct_ui_icon(srna, ICON_LIGHT_POINT); + PropertyRNA *prop; + prop = RNA_def_property(srna, "shape", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "spot_shape"); + RNA_def_property_enum_items(prop, prop_spotshape_items); + RNA_def_property_ui_text(prop, "Shape", "Sphere or aligned disk"); + RNA_def_property_update(prop, 0, "rna_Light_draw_update"); + rna_def_light_energy(srna, LA_LOCAL); rna_def_light_shadow(srna, false); } @@ -442,6 +460,12 @@ static void rna_def_spot_light(BlenderRNA *brna) "Show Cone", "Display transparent cone in 3D view to visualize which objects are contained in it"); RNA_def_property_update(prop, 0, "rna_Light_draw_update"); + + prop = RNA_def_property(srna, "shape", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "spot_shape"); + RNA_def_property_enum_items(prop, prop_spotshape_items); + RNA_def_property_ui_text(prop, "Shape", "Sphere or aligned disk"); + RNA_def_property_update(prop, 0, "rna_Light_draw_update"); } static void rna_def_sun_light(BlenderRNA *brna) -- 2.30.2 From 389a641dda2d9b33319b4c537feb185a025e96f2 Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Tue, 6 Feb 2024 16:16:46 +0100 Subject: [PATCH 04/13] EEVEE: add sphere/disk shape option for point and spot light --- .../draw/engines/eevee/eevee_lights.cc | 8 +++++ .../draw/engines/eevee/eevee_private.h | 4 ++- .../engines/eevee/shaders/lights_lib.glsl | 33 ++++++++++++------- .../draw/engines/eevee_next/eevee_light.cc | 10 +++--- .../engines/eevee_next/eevee_shader_shared.hh | 16 +++++++-- .../draw/engines/eevee_next/eevee_shadow.cc | 2 +- .../eevee_light_culling_select_comp.glsl | 6 ++-- .../eevee_light_culling_tile_comp.glsl | 3 +- .../eevee_next/shaders/eevee_light_lib.glsl | 20 +++++++---- .../shaders/eevee_shadow_tag_usage_lib.glsl | 2 +- .../shaders/eevee_shadow_tracing_lib.glsl | 8 ++++- 11 files changed, 81 insertions(+), 31 deletions(-) diff --git a/source/blender/draw/engines/eevee/eevee_lights.cc b/source/blender/draw/engines/eevee/eevee_lights.cc index 6c4c5fce831..e9cf17c02c3 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.cc +++ b/source/blender/draw/engines/eevee/eevee_lights.cc @@ -191,6 +191,14 @@ static void eevee_light_setup(Object *ob, EEVEE_Light *evli) if ((la->type == LA_AREA) && ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) { evli->light_type = LAMPTYPE_AREA_ELLIPSE; } + else if (la->spot_shape == LA_SPOT_DISK) { + if (la->type == LA_LOCAL) { + evli->light_type = LAMPTYPE_OMNI_DISK; + } + else if (la->type == LA_SPOT) { + evli->light_type = LAMPTYPE_SPOT_DISK; + } + } float shape_power = light_shape_radiance_get(la, evli); mul_v3_fl(evli->color, shape_power * la->energy); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 41b45cce082..b9a5cd1500a 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -447,7 +447,9 @@ typedef struct EEVEE_Light { float diff, spec, volume, volume_radius; } EEVEE_Light; -/* Special type for elliptic area lights, matches lamps_lib.glsl */ +/* Special type for elliptic area lights and point/spot disk lights, matches lights_lib.glsl */ +#define LAMPTYPE_OMNI_DISK 0.5f +#define LAMPTYPE_SPOT_DISK 2.5f #define LAMPTYPE_AREA_ELLIPSE 100.0f typedef struct EEVEE_Shadow { diff --git a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl index 876124ff932..2a5bbb72394 100644 --- a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl @@ -86,10 +86,16 @@ uniform depth2DArrayShadow shadowCascadeTexture; /** \name Shadow Functions * \{ */ -/* type */ -#define POINT 0.0 +/* Type. Consistent with DNA `Light::type`, except for `OMNI_DISK`/`SPOT_DISK` and `AREA_ELLIPSE`, + * which are reinterpreted from `light.spot_type` and `light.area_type`. */ #define SUN 1.0 -#define SPOT 2.0 + +#define OMNI_SPHERE 0.0 +#define OMNI_DISK 0.5 + +#define SPOT_SPHERE 2.0 +#define SPOT_DISK 2.5 + #define AREA_RECT 4.0 /* Used to define the area light shape, doesn't directly correspond to a Blender light type. */ #define AREA_ELLIPSE 100.0 @@ -233,10 +239,10 @@ float spot_attenuation(LightData ld, vec3 l_vector) float light_attenuation(LightData ld, vec4 l_vector) { float vis = 1.0; - if (ld.l_type == SPOT) { + if (ld.l_type == SPOT_SPHERE || ld.l_type == SPOT_DISK) { vis *= spot_attenuation(ld, l_vector.xyz); } - if (ld.l_type >= SPOT) { + if (ld.l_type >= SPOT_SPHERE) { vis *= step(0.0, -dot(l_vector.xyz, ld.l_forward)); } if (ld.l_type != SUN) { @@ -328,7 +334,7 @@ float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector) return ltc_evaluate_disk(N, V, mat3(1.0), points); } - else if (ld.l_type == POINT) { + else if (ld.l_type == OMNI_SPHERE) { if (l_vector.w < ld.l_radius) { /* Inside, treat as hemispherical light. */ return 1.0; @@ -343,6 +349,7 @@ float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector) } } else { + /* Sun light or omni disk light. */ float radius = ld.l_radius; radius /= (ld.l_type == SUN) ? 1.0 : l_vector.w; vec3 L = (ld.l_type == SUN) ? -ld.l_forward : (l_vector.xyz / l_vector.w); @@ -364,7 +371,7 @@ float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector) return ltc_evaluate_quad(corners, vec3(0.0, 0.0, 1.0)); } - else if (ld.l_type == POINT && l_vector.w < ld.l_radius) { + else if ((ld.l_type == OMNI_SPHERE || ld.l_type == SPOT_SPHERE) && l_vector.w < ld.l_radius) { /* Inside the sphere light, integrate over the hemisphere. */ return 1.0; } @@ -373,7 +380,7 @@ float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector) float radius_x = is_ellipse ? ld.l_sizex : ld.l_radius; float radius_y = is_ellipse ? ld.l_sizey : ld.l_radius; - if (ld.l_type == POINT) { + if (ld.l_type == OMNI_SPHERE || ld.l_type == SPOT_SPHERE) { /* The sine of the half-angle spanned by a sphere light is equal to the tangent of the * half-angle spanned by a disk light with the same radius. */ radius_x *= inversesqrt(1.0 - sqr(radius_x / l_vector.w)); @@ -381,10 +388,14 @@ float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector) } vec3 L = (ld.l_type == SUN) ? -ld.l_forward : l_vector.xyz; - vec3 Px = ld.l_right; - vec3 Py = ld.l_up; + vec3 Px, Py; - if (ld.l_type == SPOT || ld.l_type == POINT) { + if (ld.l_type == SUN || ld.l_type == AREA_ELLIPSE) { + Px = ld.l_right; + Py = ld.l_up; + } + else { + /* Omni and spot lights. */ make_orthonormal_basis(l_vector.xyz / l_vector.w, Px, Py); } diff --git a/source/blender/draw/engines/eevee_next/eevee_light.cc b/source/blender/draw/engines/eevee_next/eevee_light.cc index ed22ab6deab..1dc01542168 100644 --- a/source/blender/draw/engines/eevee_next/eevee_light.cc +++ b/source/blender/draw/engines/eevee_next/eevee_light.cc @@ -22,16 +22,18 @@ namespace blender::eevee { /** \name LightData * \{ */ -static eLightType to_light_type(short blender_light_type, short blender_area_type) +static eLightType to_light_type(short blender_light_type, + short blender_area_type, + short blender_spot_type) { switch (blender_light_type) { default: case LA_LOCAL: - return LIGHT_POINT; + return (blender_spot_type == LA_SPOT_SPHERE) ? LIGHT_OMNI_SPHERE : LIGHT_OMNI_DISK; case LA_SUN: return LIGHT_SUN; case LA_SPOT: - return LIGHT_SPOT; + return (blender_spot_type == LA_SPOT_SPHERE) ? LIGHT_SPOT_SPHERE : LIGHT_SPOT_DISK; case LA_AREA: return ELEM(blender_area_type, LA_AREA_DISK, LA_AREA_ELLIPSE) ? LIGHT_ELLIPSE : LIGHT_RECT; } @@ -76,7 +78,7 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold) this->power[LIGHT_SPECULAR] = la->spec_fac * shape_power; this->power[LIGHT_VOLUME] = la->volume_fac * point_power; - eLightType new_type = to_light_type(la->type, la->area_shape); + eLightType new_type = to_light_type(la->type, la->area_shape, la->spot_shape); if (assign_if_different(this->type, new_type)) { shadow_discard_safe(shadows); } diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 018cadea2a2..cf71706768d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -712,8 +712,13 @@ BLI_STATIC_ASSERT_ALIGN(LightCullingData, 16) enum eLightType : uint32_t { LIGHT_SUN = 0u, LIGHT_SUN_ORTHO = 1u, - LIGHT_POINT = 10u, - LIGHT_SPOT = 11u, + /* Point light. */ + LIGHT_OMNI_SPHERE = 10u, + LIGHT_OMNI_DISK = 11u, + /* Spot light. */ + LIGHT_SPOT_SPHERE = 12u, + LIGHT_SPOT_DISK = 13u, + /* Area light. */ LIGHT_RECT = 20u, LIGHT_ELLIPSE = 21u }; @@ -730,9 +735,14 @@ static inline bool is_area_light(eLightType type) return type >= LIGHT_RECT; } +static inline bool is_spot_light(eLightType type) +{ + return type == LIGHT_SPOT_SPHERE || type == LIGHT_SPOT_DISK; +} + static inline bool is_sun_light(eLightType type) { - return type < LIGHT_POINT; + return type < LIGHT_OMNI_SPHERE; } struct LightData { diff --git a/source/blender/draw/engines/eevee_next/eevee_shadow.cc b/source/blender/draw/engines/eevee_next/eevee_shadow.cc index 444bb95c51d..a33a5e4e822 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shadow.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shadow.cc @@ -226,7 +226,7 @@ void ShadowPunctual::sync(eLightType light_type, float max_distance, float softness_factor) { - if (light_type == LIGHT_SPOT) { + if (is_spot_light(light_type)) { tilemaps_needed_ = (cone_aperture > DEG2RADF(90.0f)) ? 5 : 1; } else if (is_area_light(light_type)) { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl index 15e8f576098..b5d12c88fc9 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl @@ -33,7 +33,8 @@ void main() Sphere sphere; switch (light.type) { - case LIGHT_SPOT: + case LIGHT_SPOT_SPHERE: + case LIGHT_SPOT_DISK: /* Only for < ~170 degree Cone due to plane extraction precision. */ if (light.spot_tan < 10.0) { Pyramid pyramid = shape_pyramid_non_oblique( @@ -47,7 +48,8 @@ void main() } case LIGHT_RECT: case LIGHT_ELLIPSE: - case LIGHT_POINT: + case LIGHT_OMNI_SPHERE: + case LIGHT_OMNI_DISK: sphere.center = light._position; sphere.radius = light.influence_radius_max; break; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl index 4e20787d651..b6f7b8e32fe 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl @@ -158,7 +158,8 @@ void main() bool intersect_tile = intersect(tile, sphere); switch (light.type) { - case LIGHT_SPOT: + case LIGHT_SPOT_SPHERE: + case LIGHT_SPOT_DISK: /* Only for < ~170 degree Cone due to plane extraction precision. */ if (light.spot_tan < 10.0) { Pyramid pyramid = shape_pyramid_non_oblique( diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl index 2339deae622..9dc502a3ca7 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl @@ -107,7 +107,7 @@ float light_attenuation_common(LightData light, const bool is_directional, vec3 if (is_directional) { return 1.0; } - if (light.type == LIGHT_SPOT) { + if (is_spot_light(light.type)) { return light_spot_attenuation(light, L); } if (is_area_light(light.type)) { @@ -183,7 +183,7 @@ float light_sphere_disk_radius(float sphere_radius, float distance_to_sphere) float light_ltc( sampler2DArray utility_tx, LightData light, vec3 N, vec3 V, LightVector lv, vec4 ltc_mat) { - if (light.type == LIGHT_POINT && lv.dist < light._radius) { + if (light.type == LIGHT_OMNI_SPHERE && lv.dist < light._radius) { /* Inside the sphere light, integrate over the hemisphere. */ return 1.0; } @@ -213,12 +213,20 @@ float light_ltc( make_orthonormal_basis(lv.L, Px, Py); } - vec2 size = vec2(light._area_size_x, light._area_size_y); - if (light.type == LIGHT_POINT) { - /* The sine of the half-angle spanned by a sphere light is equal to the tangent of the - * half-angle spanned by a disk light with the same radius. */ + vec2 size; + if (light.type == LIGHT_OMNI_SPHERE) { + /* Sphere. */ size = vec2(light_sphere_disk_radius(light._radius, lv.dist)); } + else if (light.type == LIGHT_OMNI_DISK) { + /* View direction-aligned disk. */ + size = vec2(light._radius); + } + else { + /* Elliptical area light. */ + size = vec2(light._area_size_x, light._area_size_y); + } + vec3 points[3]; points[0] = Px * -size.x + Py * -size.y; points[1] = Px * size.x + Py * -size.y; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl index 7be13a72295..8e4116877f9 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tag_usage_lib.glsl @@ -93,7 +93,7 @@ void shadow_tag_usage_tilemap_punctual( if (dist_to_light > light.influence_radius_max) { return; } - if (light.type == LIGHT_SPOT) { + if (is_spot_light(light.type)) { /* Early out if out of cone. */ float angle_tan = length(lP.xy / dist_to_light); if (angle_tan > light.spot_tan) { diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl index 99cb7f19c03..2619876305c 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl @@ -331,7 +331,13 @@ ShadowRayPunctual shadow_ray_generate_punctual(LightData light, /* Disk rotated towards light vector. */ vec3 right, up; make_orthonormal_basis(L, right, up); - random_2d *= light_sphere_disk_radius(light._radius, dist); + if (light.type == LIGHT_OMNI_SPHERE || light.type == LIGHT_SPOT_SPHERE) { + /* FIXME(weizhen): this is not well-defined when `dist < light._radius`. */ + random_2d *= light_sphere_disk_radius(light._radius, dist); + } + else { + random_2d *= light._radius; + } random_2d *= light.shadow_shape_scale_or_angle; vec3 point_on_light_shape = right * random_2d.x + up * random_2d.y; -- 2.30.2 From 9a3d905227e6395f2247f885550260f5bf6154a0 Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Tue, 6 Feb 2024 18:49:50 +0100 Subject: [PATCH 05/13] UI: Change Shape enum to "Use Soft Falloff" boolean --- intern/cycles/blender/addon/ui.py | 2 +- intern/cycles/blender/light.cpp | 4 +-- .../startup/bl_ui/properties_data_light.py | 2 +- .../draw/engines/eevee/eevee_lights.cc | 2 +- .../draw/engines/eevee_next/eevee_light.cc | 8 ++--- source/blender/makesdna/DNA_light_types.h | 10 ++---- source/blender/makesrna/intern/rna_light.cc | 33 ++++++++----------- 7 files changed, 25 insertions(+), 36 deletions(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index f1522a5a50b..c4649a94205 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1573,7 +1573,7 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel): col.separator() if light.type in {'POINT', 'SPOT'}: - col.prop(light, "shape", text="Shape") + col.prop(light, "use_falloff", text="Use Soft Falloff") col.prop(light, "shadow_soft_size", text="Radius") elif light.type == 'SUN': col.prop(light, "angle") diff --git a/intern/cycles/blender/light.cpp b/intern/cycles/blender/light.cpp index fe621c85e12..fcc55228a72 100644 --- a/intern/cycles/blender/light.cpp +++ b/intern/cycles/blender/light.cpp @@ -48,7 +48,7 @@ void BlenderSync::sync_light(BL::Object &b_parent, BL::PointLight b_point_light(b_light); light->set_size(b_point_light.shadow_soft_size()); light->set_light_type(LIGHT_POINT); - light->set_is_sphere(b_point_light.shape() == BL::PointLight::shape_SPHERE); + light->set_is_sphere(!b_point_light.use_falloff()); break; } case BL::Light::type_SPOT: { @@ -57,7 +57,7 @@ void BlenderSync::sync_light(BL::Object &b_parent, light->set_light_type(LIGHT_SPOT); light->set_spot_angle(b_spot_light.spot_size()); light->set_spot_smooth(b_spot_light.spot_blend()); - light->set_is_sphere(b_spot_light.shape() == BL::SpotLight::shape_SPHERE); + light->set_is_sphere(!b_spot_light.use_falloff()); break; } /* Hemi were removed from 2.8 */ diff --git a/scripts/startup/bl_ui/properties_data_light.py b/scripts/startup/bl_ui/properties_data_light.py index 66ddfc2f82f..c2aa7f2a42f 100644 --- a/scripts/startup/bl_ui/properties_data_light.py +++ b/scripts/startup/bl_ui/properties_data_light.py @@ -98,7 +98,7 @@ class DATA_PT_EEVEE_light(DataButtonsPanel, Panel): col.separator() if light.type in {'POINT', 'SPOT'}: - col.prop(light, "shape", text="Shape") + col.prop(light, "use_falloff", text="Use Soft Falloff") col.prop(light, "shadow_soft_size", text="Radius") elif light.type == 'SUN': col.prop(light, "angle") diff --git a/source/blender/draw/engines/eevee/eevee_lights.cc b/source/blender/draw/engines/eevee/eevee_lights.cc index e9cf17c02c3..ac4f7889634 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.cc +++ b/source/blender/draw/engines/eevee/eevee_lights.cc @@ -191,7 +191,7 @@ static void eevee_light_setup(Object *ob, EEVEE_Light *evli) if ((la->type == LA_AREA) && ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) { evli->light_type = LAMPTYPE_AREA_ELLIPSE; } - else if (la->spot_shape == LA_SPOT_DISK) { + else if (la->mode & LA_USE_FALLOFF) { if (la->type == LA_LOCAL) { evli->light_type = LAMPTYPE_OMNI_DISK; } diff --git a/source/blender/draw/engines/eevee_next/eevee_light.cc b/source/blender/draw/engines/eevee_next/eevee_light.cc index 1dc01542168..9cec9b610e7 100644 --- a/source/blender/draw/engines/eevee_next/eevee_light.cc +++ b/source/blender/draw/engines/eevee_next/eevee_light.cc @@ -24,16 +24,16 @@ namespace blender::eevee { static eLightType to_light_type(short blender_light_type, short blender_area_type, - short blender_spot_type) + bool use_falloff) { switch (blender_light_type) { default: case LA_LOCAL: - return (blender_spot_type == LA_SPOT_SPHERE) ? LIGHT_OMNI_SPHERE : LIGHT_OMNI_DISK; + return use_falloff ? LIGHT_OMNI_DISK : LIGHT_OMNI_SPHERE; case LA_SUN: return LIGHT_SUN; case LA_SPOT: - return (blender_spot_type == LA_SPOT_SPHERE) ? LIGHT_SPOT_SPHERE : LIGHT_SPOT_DISK; + return use_falloff ? LIGHT_SPOT_DISK : LIGHT_SPOT_SPHERE; case LA_AREA: return ELEM(blender_area_type, LA_AREA_DISK, LA_AREA_ELLIPSE) ? LIGHT_ELLIPSE : LIGHT_RECT; } @@ -78,7 +78,7 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold) this->power[LIGHT_SPECULAR] = la->spec_fac * shape_power; this->power[LIGHT_VOLUME] = la->volume_fac * point_power; - eLightType new_type = to_light_type(la->type, la->area_shape, la->spot_shape); + eLightType new_type = to_light_type(la->type, la->area_shape, la->mode & LA_USE_FALLOFF); if (assign_if_different(this->type, new_type)) { shadow_discard_safe(shadows); } diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h index 2a31015e80f..61a3a2286f8 100644 --- a/source/blender/makesdna/DNA_light_types.h +++ b/source/blender/makesdna/DNA_light_types.h @@ -39,8 +39,6 @@ typedef struct Light { float radius; /* Spot Light. */ - short spot_shape; - short _pad3; float spotsize; float spotblend; @@ -79,6 +77,7 @@ typedef struct Light { float spec_fac, att_dist; float shadow_softness_factor; float shadow_trace_distance; + float _pad3; /* Preview */ struct PreviewImage *preview; @@ -140,6 +139,7 @@ enum { // LA_SHOW_SHADOW_BOX = 1 << 18, LA_SHAD_CONTACT = 1 << 19, LA_CUSTOM_ATTENUATION = 1 << 20, + LA_USE_FALLOFF = 1 << 21, }; /** #Light::falloff_type */ @@ -161,9 +161,3 @@ enum { LA_AREA_DISK = 4, LA_AREA_ELLIPSE = 5, }; - -/** #Light::spot_shape */ -enum { - LA_SPOT_SPHERE = 0, - LA_SPOT_DISK = 1, -}; diff --git a/source/blender/makesrna/intern/rna_light.cc b/source/blender/makesrna/intern/rna_light.cc index a4cb9291b62..6b545f2496c 100644 --- a/source/blender/makesrna/intern/rna_light.cc +++ b/source/blender/makesrna/intern/rna_light.cc @@ -337,17 +337,6 @@ static void rna_def_light_shadow(StructRNA *srna, bool sun) } } -const EnumPropertyItem prop_spotshape_items[] = { - {LA_SPOT_SPHERE, "SPHERE", 0, "Sphere", "Emits light from a double-sided sphere geometry"}, - {LA_SPOT_DISK, - "DISK", - 0, - "Aligned Disk", - "The light appears to be a disk pointing towards the shading point. This shape is not " - "physically feasible, but gives softer shadows compared to sphere light"}, - {0, nullptr, 0, nullptr, nullptr}, -}; - static void rna_def_point_light(BlenderRNA *brna) { StructRNA *srna; @@ -358,10 +347,13 @@ static void rna_def_point_light(BlenderRNA *brna) RNA_def_struct_ui_icon(srna, ICON_LIGHT_POINT); PropertyRNA *prop; - prop = RNA_def_property(srna, "shape", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "spot_shape"); - RNA_def_property_enum_items(prop, prop_spotshape_items); - RNA_def_property_ui_text(prop, "Shape", "Sphere or aligned disk"); + prop = RNA_def_property(srna, "use_falloff", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "mode", LA_USE_FALLOFF); + RNA_def_property_ui_text( + prop, + "Use Soft Falloff", + "Apply falloff to avoid sharp edges when the light geometry intersects with other objects"); + RNA_def_property_boolean_default(prop, true); RNA_def_property_update(prop, 0, "rna_Light_draw_update"); rna_def_light_energy(srna, LA_LOCAL); @@ -461,10 +453,13 @@ static void rna_def_spot_light(BlenderRNA *brna) "Display transparent cone in 3D view to visualize which objects are contained in it"); RNA_def_property_update(prop, 0, "rna_Light_draw_update"); - prop = RNA_def_property(srna, "shape", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "spot_shape"); - RNA_def_property_enum_items(prop, prop_spotshape_items); - RNA_def_property_ui_text(prop, "Shape", "Sphere or aligned disk"); + prop = RNA_def_property(srna, "use_falloff", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "mode", LA_USE_FALLOFF); + RNA_def_property_ui_text( + prop, + "Use Soft Falloff", + "Apply falloff to avoid sharp edges when the light geometry intersects with other objects"); + RNA_def_property_boolean_default(prop, true); RNA_def_property_update(prop, 0, "rna_Light_draw_update"); } -- 2.30.2 From 420ab97c1d08e60d26af1b45a53187a3ab699480 Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Tue, 6 Feb 2024 19:41:18 +0100 Subject: [PATCH 06/13] Cleanup: fix typo, renaming, adding TODO for texture --- intern/cycles/kernel/light/point.h | 16 ++++++++++------ intern/cycles/kernel/light/spot.h | 8 ++++---- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/intern/cycles/kernel/light/point.h b/intern/cycles/kernel/light/point.h index fd9e3e45a30..34660cedc84 100644 --- a/intern/cycles/kernel/light/point.h +++ b/intern/cycles/kernel/light/point.h @@ -55,7 +55,7 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, ls->P = ls->Ng * klight->spot.radius + klight->co; } else { - /* Point light with ad-hoc radius based on orienteded disk. */ + /* Point light with ad-hoc radius based on oriented disk. */ ls->P = klight->co; if (r_sq > 0.0f) { ls->P += disk_light_sample(lightN, rand) * klight->spot.radius; @@ -67,6 +67,8 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, /* PDF. */ const float invarea = (r_sq > 0.0f) ? 1.0f / (r_sq * M_PI_F) : 1.0f; ls->pdf = invarea * light_pdf_area_to_solid_angle(lightN, -ls->D, ls->t); + + /* TODO: texture coordinates. */ } /* Texture coordinates. */ @@ -79,7 +81,7 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, return true; } -ccl_device_forceinline float point_light_pdf( +ccl_device_forceinline float sphere_light_pdf( const float d_sq, const float r_sq, const float3 N, const float3 D, const uint32_t path_flag) { if (d_sq > r_sq) { @@ -106,9 +108,9 @@ ccl_device_forceinline void point_light_mnee_sample_update(const ccl_global Kern const float t_sq = sqr(ls->t); /* NOTE : preserve pdf in area measure. */ - const float pdf_solid_angle_to_area = 0.5f * fabsf(d_sq - r_sq - t_sq) / - (radius * ls->t * t_sq); - ls->pdf = point_light_pdf(d_sq, r_sq, N, ls->D, path_flag) * pdf_solid_angle_to_area; + const float jacobian_solid_angle_to_area = 0.5f * fabsf(d_sq - r_sq - t_sq) / + (radius * ls->t * t_sq); + ls->pdf = sphere_light_pdf(d_sq, r_sq, N, ls->D, path_flag) * jacobian_solid_angle_to_area; ls->Ng = normalize(ls->P - klight->co); } @@ -161,7 +163,7 @@ ccl_device_inline bool point_light_sample_from_intersection( if (klight->spot.is_sphere) { const float d_sq = len_squared(ray_P - klight->co); - ls->pdf = point_light_pdf(d_sq, r_sq, N, ray_D, path_flag); + ls->pdf = sphere_light_pdf(d_sq, r_sq, N, ray_D, path_flag); ls->Ng = normalize(ls->P - klight->co); } else { @@ -174,6 +176,8 @@ ccl_device_inline bool point_light_sample_from_intersection( ls->pdf = 0.0f; } ls->Ng = -ray_D; + + // TODO: texture coordinates. } /* Texture coordinates. */ diff --git a/intern/cycles/kernel/light/spot.h b/intern/cycles/kernel/light/spot.h index d22a8b24595..cbf03da724b 100644 --- a/intern/cycles/kernel/light/spot.h +++ b/intern/cycles/kernel/light/spot.h @@ -118,7 +118,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight, spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v); } else { - /* Point light with ad-hoc radius based on orienteded disk. */ + /* Point light with ad-hoc radius based on oriented disk. */ ls->P = klight->co; if (r_sq > 0.0f) { ls->P += disk_light_sample(lightN, rand) * klight->spot.radius; @@ -179,10 +179,10 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne const float t_sq = sqr(ls->t); /* NOTE : preserve pdf in area measure. */ - const float pdf_solid_angle_to_area = 0.5f * fabsf(d_sq - r_sq - t_sq) / - (radius * ls->t * t_sq); + 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) * - pdf_solid_angle_to_area; + jacobian_solid_angle_to_area; ls->Ng = normalize(ls->P - klight->co); -- 2.30.2 From 42d6b5cbb0d11f156931c84317bbe274eb5b017e Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Tue, 6 Feb 2024 19:42:03 +0100 Subject: [PATCH 07/13] Fix pdf for MNEE --- intern/cycles/kernel/light/point.h | 4 +++- intern/cycles/kernel/light/spot.h | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/intern/cycles/kernel/light/point.h b/intern/cycles/kernel/light/point.h index 34660cedc84..3ac37d7dfed 100644 --- a/intern/cycles/kernel/light/point.h +++ b/intern/cycles/kernel/light/point.h @@ -115,7 +115,9 @@ ccl_device_forceinline void point_light_mnee_sample_update(const ccl_global Kern ls->Ng = normalize(ls->P - klight->co); } else { - /* PDF does not change. */ + /* NOTE : preserve pdf in area measure. */ + ls->pdf = ls->eval_fac * 4.0f * M_PI_F; + ls->Ng = -ls->D; } diff --git a/intern/cycles/kernel/light/spot.h b/intern/cycles/kernel/light/spot.h index cbf03da724b..0e6ea5b5669 100644 --- a/intern/cycles/kernel/light/spot.h +++ b/intern/cycles/kernel/light/spot.h @@ -189,7 +189,9 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne use_attenuation = (d_sq > r_sq); } else { - /* PDF does not change. */ + /* NOTE : preserve pdf in area measure. */ + ls->pdf = ls->eval_fac * 4.0f * M_PI_F; + ls->Ng = -ls->D; } -- 2.30.2 From da1540a6415090175e161ae97dee8e23809d75c1 Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Wed, 7 Feb 2024 11:55:04 +0100 Subject: [PATCH 08/13] Renaming, set default in DNA --- intern/cycles/blender/addon/ui.py | 2 +- intern/cycles/blender/light.cpp | 4 ++-- scripts/startup/bl_ui/properties_data_light.py | 2 +- source/blender/draw/engines/eevee/eevee_lights.cc | 2 +- .../blender/draw/engines/eevee_next/eevee_light.cc | 2 +- source/blender/makesdna/DNA_light_defaults.h | 2 +- source/blender/makesdna/DNA_light_types.h | 2 +- source/blender/makesrna/intern/rna_light.cc | 14 ++++++-------- 8 files changed, 14 insertions(+), 16 deletions(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index c4649a94205..ec7f9a31d3a 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1573,7 +1573,7 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel): col.separator() if light.type in {'POINT', 'SPOT'}: - col.prop(light, "use_falloff", text="Use Soft Falloff") + col.prop(light, "use_soft_falloff") col.prop(light, "shadow_soft_size", text="Radius") elif light.type == 'SUN': col.prop(light, "angle") diff --git a/intern/cycles/blender/light.cpp b/intern/cycles/blender/light.cpp index fcc55228a72..6eaa4529250 100644 --- a/intern/cycles/blender/light.cpp +++ b/intern/cycles/blender/light.cpp @@ -48,7 +48,7 @@ void BlenderSync::sync_light(BL::Object &b_parent, BL::PointLight b_point_light(b_light); light->set_size(b_point_light.shadow_soft_size()); light->set_light_type(LIGHT_POINT); - light->set_is_sphere(!b_point_light.use_falloff()); + light->set_is_sphere(!b_point_light.use_soft_falloff()); break; } case BL::Light::type_SPOT: { @@ -57,7 +57,7 @@ void BlenderSync::sync_light(BL::Object &b_parent, light->set_light_type(LIGHT_SPOT); light->set_spot_angle(b_spot_light.spot_size()); light->set_spot_smooth(b_spot_light.spot_blend()); - light->set_is_sphere(!b_spot_light.use_falloff()); + light->set_is_sphere(!b_spot_light.use_soft_falloff()); break; } /* Hemi were removed from 2.8 */ diff --git a/scripts/startup/bl_ui/properties_data_light.py b/scripts/startup/bl_ui/properties_data_light.py index c2aa7f2a42f..472bf7d86f5 100644 --- a/scripts/startup/bl_ui/properties_data_light.py +++ b/scripts/startup/bl_ui/properties_data_light.py @@ -98,7 +98,7 @@ class DATA_PT_EEVEE_light(DataButtonsPanel, Panel): col.separator() if light.type in {'POINT', 'SPOT'}: - col.prop(light, "use_falloff", text="Use Soft Falloff") + col.prop(light, "use_soft_falloff") col.prop(light, "shadow_soft_size", text="Radius") elif light.type == 'SUN': col.prop(light, "angle") diff --git a/source/blender/draw/engines/eevee/eevee_lights.cc b/source/blender/draw/engines/eevee/eevee_lights.cc index ac4f7889634..b4fdc7bfa0f 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.cc +++ b/source/blender/draw/engines/eevee/eevee_lights.cc @@ -191,7 +191,7 @@ static void eevee_light_setup(Object *ob, EEVEE_Light *evli) if ((la->type == LA_AREA) && ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) { evli->light_type = LAMPTYPE_AREA_ELLIPSE; } - else if (la->mode & LA_USE_FALLOFF) { + else if (la->mode & LA_USE_SOFT_FALLOFF) { if (la->type == LA_LOCAL) { evli->light_type = LAMPTYPE_OMNI_DISK; } diff --git a/source/blender/draw/engines/eevee_next/eevee_light.cc b/source/blender/draw/engines/eevee_next/eevee_light.cc index 9cec9b610e7..3d495ec6314 100644 --- a/source/blender/draw/engines/eevee_next/eevee_light.cc +++ b/source/blender/draw/engines/eevee_next/eevee_light.cc @@ -78,7 +78,7 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold) this->power[LIGHT_SPECULAR] = la->spec_fac * shape_power; this->power[LIGHT_VOLUME] = la->volume_fac * point_power; - eLightType new_type = to_light_type(la->type, la->area_shape, la->mode & LA_USE_FALLOFF); + eLightType new_type = to_light_type(la->type, la->area_shape, la->mode & LA_USE_SOFT_FALLOFF); if (assign_if_different(this->type, new_type)) { shadow_discard_safe(shadows); } diff --git a/source/blender/makesdna/DNA_light_defaults.h b/source/blender/makesdna/DNA_light_defaults.h index 8122f280c92..cff2cf7b5a9 100644 --- a/source/blender/makesdna/DNA_light_defaults.h +++ b/source/blender/makesdna/DNA_light_defaults.h @@ -24,7 +24,7 @@ .energy_deprecated = 10.0f, \ .spotsize = DEG2RADF(45.0f), \ .spotblend = 0.15f, \ - .mode = LA_SHADOW, \ + .mode = LA_SHADOW | LA_USE_SOFT_FALLOFF, \ .clipsta = 0.05f, \ .clipend = 40.0f, \ .bias = 1.0f, \ diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h index 61a3a2286f8..6b20f307421 100644 --- a/source/blender/makesdna/DNA_light_types.h +++ b/source/blender/makesdna/DNA_light_types.h @@ -139,7 +139,7 @@ enum { // LA_SHOW_SHADOW_BOX = 1 << 18, LA_SHAD_CONTACT = 1 << 19, LA_CUSTOM_ATTENUATION = 1 << 20, - LA_USE_FALLOFF = 1 << 21, + LA_USE_SOFT_FALLOFF = 1 << 21, }; /** #Light::falloff_type */ diff --git a/source/blender/makesrna/intern/rna_light.cc b/source/blender/makesrna/intern/rna_light.cc index 6b545f2496c..227881573ca 100644 --- a/source/blender/makesrna/intern/rna_light.cc +++ b/source/blender/makesrna/intern/rna_light.cc @@ -347,13 +347,12 @@ static void rna_def_point_light(BlenderRNA *brna) RNA_def_struct_ui_icon(srna, ICON_LIGHT_POINT); PropertyRNA *prop; - prop = RNA_def_property(srna, "use_falloff", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "mode", LA_USE_FALLOFF); + prop = RNA_def_property(srna, "use_soft_falloff", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "mode", LA_USE_SOFT_FALLOFF); RNA_def_property_ui_text( prop, - "Use Soft Falloff", + "Soft Falloff", "Apply falloff to avoid sharp edges when the light geometry intersects with other objects"); - RNA_def_property_boolean_default(prop, true); RNA_def_property_update(prop, 0, "rna_Light_draw_update"); rna_def_light_energy(srna, LA_LOCAL); @@ -453,13 +452,12 @@ static void rna_def_spot_light(BlenderRNA *brna) "Display transparent cone in 3D view to visualize which objects are contained in it"); RNA_def_property_update(prop, 0, "rna_Light_draw_update"); - prop = RNA_def_property(srna, "use_falloff", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "mode", LA_USE_FALLOFF); + prop = RNA_def_property(srna, "use_soft_falloff", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "mode", LA_USE_SOFT_FALLOFF); RNA_def_property_ui_text( prop, - "Use Soft Falloff", + "Soft Falloff", "Apply falloff to avoid sharp edges when the light geometry intersects with other objects"); - RNA_def_property_boolean_default(prop, true); RNA_def_property_update(prop, 0, "rna_Light_draw_update"); } -- 2.30.2 From 4f9c66374802f02e520f36f33128bc9195a9995b Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Wed, 7 Feb 2024 12:52:36 +0100 Subject: [PATCH 09/13] Cleanup: Remove TODO --- intern/cycles/kernel/light/point.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/intern/cycles/kernel/light/point.h b/intern/cycles/kernel/light/point.h index 3ac37d7dfed..595aece8089 100644 --- a/intern/cycles/kernel/light/point.h +++ b/intern/cycles/kernel/light/point.h @@ -67,8 +67,6 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, /* PDF. */ const float invarea = (r_sq > 0.0f) ? 1.0f / (r_sq * M_PI_F) : 1.0f; ls->pdf = invarea * light_pdf_area_to_solid_angle(lightN, -ls->D, ls->t); - - /* TODO: texture coordinates. */ } /* Texture coordinates. */ @@ -178,8 +176,6 @@ ccl_device_inline bool point_light_sample_from_intersection( ls->pdf = 0.0f; } ls->Ng = -ray_D; - - // TODO: texture coordinates. } /* Texture coordinates. */ -- 2.30.2 From 93eae86b308f3e4d050c0430df80aa6049ff02ef Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Wed, 7 Feb 2024 13:19:17 +0100 Subject: [PATCH 10/13] Add versioning for files created before 4.0 --- source/blender/blenloader/intern/versioning_400.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 76009101187..773e7a21df7 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -2848,6 +2848,15 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + /* Keep point/spot light soft falloff for files created before 4.0. */ + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 0)) { + LISTBASE_FOREACH (Light *, light, &bmain->lights) { + if (light->type == LA_LOCAL || light->type == LA_SPOT) { + light->mode |= LA_USE_SOFT_FALLOFF; + } + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. -- 2.30.2 From 244be1637b95c50b4537d7cfa60af99be9db0438 Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Wed, 7 Feb 2024 14:25:16 +0100 Subject: [PATCH 11/13] Address reviews --- .../draw/engines/eevee/shaders/lights_lib.glsl | 18 ++++++++++++------ .../draw/engines/eevee_next/eevee_light.cc | 6 +++--- .../engines/eevee_next/eevee_shader_shared.hh | 5 +++++ .../eevee_next/shaders/eevee_light_lib.glsl | 10 +++++----- .../shaders/eevee_shadow_tracing_lib.glsl | 2 +- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl index 2a5bbb72394..041ccee29f6 100644 --- a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl @@ -87,12 +87,13 @@ uniform depth2DArrayShadow shadowCascadeTexture; * \{ */ /* Type. Consistent with DNA `Light::type`, except for `OMNI_DISK`/`SPOT_DISK` and `AREA_ELLIPSE`, - * which are reinterpreted from `light.spot_type` and `light.area_type`. */ -#define SUN 1.0 - + * which are reinterpreted from `light.mode` and `light.area_type`. The decimal numbers are chosen + * so that they can be exactly represented by float, and guarantee an increasing sequence. */ #define OMNI_SPHERE 0.0 #define OMNI_DISK 0.5 +#define SUN 1.0 + #define SPOT_SPHERE 2.0 #define SPOT_DISK 2.5 @@ -315,6 +316,11 @@ float light_visibility(LightData ld, vec3 P, vec4 l_vector) return light_shadowing(ld, P, l_atten); } +bool is_sphere_light(float type) +{ + return type == OMNI_SPHERE || type == SPOT_SPHERE; +} + float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector) { if (ld.l_type == AREA_RECT) { @@ -334,7 +340,7 @@ float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector) return ltc_evaluate_disk(N, V, mat3(1.0), points); } - else if (ld.l_type == OMNI_SPHERE) { + else if (is_sphere_light(ld.l_type)) { if (l_vector.w < ld.l_radius) { /* Inside, treat as hemispherical light. */ return 1.0; @@ -371,7 +377,7 @@ float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector) return ltc_evaluate_quad(corners, vec3(0.0, 0.0, 1.0)); } - else if ((ld.l_type == OMNI_SPHERE || ld.l_type == SPOT_SPHERE) && l_vector.w < ld.l_radius) { + else if (is_sphere_light(ld.l_type) && l_vector.w < ld.l_radius) { /* Inside the sphere light, integrate over the hemisphere. */ return 1.0; } @@ -380,7 +386,7 @@ float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector) float radius_x = is_ellipse ? ld.l_sizex : ld.l_radius; float radius_y = is_ellipse ? ld.l_sizey : ld.l_radius; - if (ld.l_type == OMNI_SPHERE || ld.l_type == SPOT_SPHERE) { + if (is_sphere_light(ld.l_type)) { /* The sine of the half-angle spanned by a sphere light is equal to the tangent of the * half-angle spanned by a disk light with the same radius. */ radius_x *= inversesqrt(1.0 - sqr(radius_x / l_vector.w)); diff --git a/source/blender/draw/engines/eevee_next/eevee_light.cc b/source/blender/draw/engines/eevee_next/eevee_light.cc index 3d495ec6314..26ee49d5e45 100644 --- a/source/blender/draw/engines/eevee_next/eevee_light.cc +++ b/source/blender/draw/engines/eevee_next/eevee_light.cc @@ -24,16 +24,16 @@ namespace blender::eevee { static eLightType to_light_type(short blender_light_type, short blender_area_type, - bool use_falloff) + bool use_soft_falloff) { switch (blender_light_type) { default: case LA_LOCAL: - return use_falloff ? LIGHT_OMNI_DISK : LIGHT_OMNI_SPHERE; + return use_soft_falloff ? LIGHT_OMNI_DISK : LIGHT_OMNI_SPHERE; case LA_SUN: return LIGHT_SUN; case LA_SPOT: - return use_falloff ? LIGHT_SPOT_DISK : LIGHT_SPOT_SPHERE; + return use_soft_falloff ? LIGHT_SPOT_DISK : LIGHT_SPOT_SPHERE; case LA_AREA: return ELEM(blender_area_type, LA_AREA_DISK, LA_AREA_ELLIPSE) ? LIGHT_ELLIPSE : LIGHT_RECT; } diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index cf71706768d..61d9f966db6 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -740,6 +740,11 @@ static inline bool is_spot_light(eLightType type) return type == LIGHT_SPOT_SPHERE || type == LIGHT_SPOT_DISK; } +static inline bool is_sphere_light(eLightType type) +{ + return type == LIGHT_SPOT_SPHERE || type == LIGHT_OMNI_SPHERE; +} + static inline bool is_sun_light(eLightType type) { return type < LIGHT_OMNI_SPHERE; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl index 9dc502a3ca7..4f8762975a3 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl @@ -183,7 +183,7 @@ float light_sphere_disk_radius(float sphere_radius, float distance_to_sphere) float light_ltc( sampler2DArray utility_tx, LightData light, vec3 N, vec3 V, LightVector lv, vec4 ltc_mat) { - if (light.type == LIGHT_OMNI_SPHERE && lv.dist < light._radius) { + if (is_sphere_light(light.type) && lv.dist < light._radius) { /* Inside the sphere light, integrate over the hemisphere. */ return 1.0; } @@ -214,16 +214,16 @@ float light_ltc( } vec2 size; - if (light.type == LIGHT_OMNI_SPHERE) { - /* Sphere. */ + if (is_sphere_light(light.type)) { + /* Spherical omni or spot light. */ size = vec2(light_sphere_disk_radius(light._radius, lv.dist)); } - else if (light.type == LIGHT_OMNI_DISK) { + else if (light.type == LIGHT_OMNI_DISK || light.type == LIGHT_SPOT_DISK) { /* View direction-aligned disk. */ size = vec2(light._radius); } else { - /* Elliptical area light. */ + /* Sun light and elliptical area light. */ size = vec2(light._area_size_x, light._area_size_y); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl index 2619876305c..2b08ebd5f00 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_shadow_tracing_lib.glsl @@ -331,7 +331,7 @@ ShadowRayPunctual shadow_ray_generate_punctual(LightData light, /* Disk rotated towards light vector. */ vec3 right, up; make_orthonormal_basis(L, right, up); - if (light.type == LIGHT_OMNI_SPHERE || light.type == LIGHT_SPOT_SPHERE) { + if (is_sphere_light(light.type)) { /* FIXME(weizhen): this is not well-defined when `dist < light._radius`. */ random_2d *= light_sphere_disk_radius(light._radius, dist); } -- 2.30.2 From 53c803e0dbe59c1de107c0bcaa3306deef2f803b Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Wed, 7 Feb 2024 16:40:08 +0100 Subject: [PATCH 12/13] Fix missing shadow for point light --- source/blender/draw/engines/eevee/eevee_shadows_cube.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cube.cc b/source/blender/draw/engines/eevee/eevee_shadows_cube.cc index 8f7479ba977..117659a9ab8 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows_cube.cc +++ b/source/blender/draw/engines/eevee/eevee_shadows_cube.cc @@ -189,11 +189,13 @@ void EEVEE_shadows_draw_cubemap(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, for (int j = 0; j < 6; j++) { /* Optimization: Only render the needed faces. */ /* Skip all but -Z face. */ - if (evli->light_type == LA_SPOT && j != 5 && spot_angle_fit_single_face(evli)) { + if ((evli->light_type == LA_SPOT || evli->light_type == LAMPTYPE_SPOT_DISK) && j != 5 && + spot_angle_fit_single_face(evli)) + { continue; } /* Skip +Z face. */ - if (evli->light_type != LA_LOCAL && j == 4) { + if ((evli->light_type != LA_LOCAL || evli->light_type != LAMPTYPE_OMNI_DISK) && j == 4) { continue; } /* TODO(fclem): some cube sides can be invisible in the main views. Cull them. */ -- 2.30.2 From 6fc37504dea9cc6fc24aa7e5b2221db2d617bd68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Wed, 7 Feb 2024 17:31:13 +0100 Subject: [PATCH 13/13] Fix broken condition --- source/blender/draw/engines/eevee/eevee_shadows_cube.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cube.cc b/source/blender/draw/engines/eevee/eevee_shadows_cube.cc index 117659a9ab8..846d33c5f6a 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows_cube.cc +++ b/source/blender/draw/engines/eevee/eevee_shadows_cube.cc @@ -195,7 +195,7 @@ void EEVEE_shadows_draw_cubemap(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, continue; } /* Skip +Z face. */ - if ((evli->light_type != LA_LOCAL || evli->light_type != LAMPTYPE_OMNI_DISK) && j == 4) { + if (!(evli->light_type == LA_LOCAL || evli->light_type == LAMPTYPE_OMNI_DISK) && j == 4) { continue; } /* TODO(fclem): some cube sides can be invisible in the main views. Cull them. */ -- 2.30.2