Lights: Option to use old point light falloff
Add new "Soft Falloff" option on point and spot light that uses the old light behavior from Blender versions before 4.0. Blend files saved with those older versions will use the option. This option is enabled by default on new lights. Fix #114241 Co-authored-by: Weizhen Huang <weizhen@blender.org> Co-authored-by: Clément Foucault <foucault.clem@gmail.com> Pull Request: https://projects.blender.org/blender/blender/pulls/117832
This commit is contained in:
committed by
Brecht Van Lommel
parent
8e80b44796
commit
bd8a44e169
@@ -1587,6 +1587,7 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel):
|
||||
col.separator()
|
||||
|
||||
if light.type in {'POINT', 'SPOT'}:
|
||||
col.prop(light, "use_soft_falloff")
|
||||
col.prop(light, "shadow_soft_size", text="Radius")
|
||||
elif light.type == 'SUN':
|
||||
col.prop(light, "angle")
|
||||
@@ -1601,6 +1602,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")
|
||||
|
||||
|
||||
@@ -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.use_soft_falloff());
|
||||
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.use_soft_falloff());
|
||||
break;
|
||||
}
|
||||
/* Hemi were removed from 2.8 */
|
||||
|
||||
@@ -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<false>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -15,48 +15,61 @@ 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;
|
||||
|
||||
float cos_theta;
|
||||
if (d_sq > r_sq) {
|
||||
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 {
|
||||
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;
|
||||
|
||||
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);
|
||||
/* `ls->Ng` is not well-defined for point light, so use the incoming direction instead. */
|
||||
ls->Ng = -ls->D;
|
||||
}
|
||||
else {
|
||||
ls->Ng = normalize(ls->P - klight->co);
|
||||
/* 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 {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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. */
|
||||
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. */
|
||||
@@ -66,7 +79,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) {
|
||||
@@ -87,26 +100,26 @@ 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);
|
||||
|
||||
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 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);
|
||||
}
|
||||
else {
|
||||
ls->eval_fac = klight->spot.eval_fac;
|
||||
/* NOTE : preserve pdf in area measure. */
|
||||
ls->pdf = ls->eval_fac * 4.0f * M_PI_F;
|
||||
|
||||
ls->Ng = -ls->D;
|
||||
|
||||
/* PDF does not change. */
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
@@ -123,8 +136,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(
|
||||
@@ -136,25 +157,34 @@ ccl_device_inline bool point_light_sample_from_intersection(
|
||||
const uint32_t path_flag,
|
||||
ccl_private LightSample *ccl_restrict ls)
|
||||
{
|
||||
const float r_sq = sqr(klight->spot.radius);
|
||||
|
||||
ls->eval_fac = klight->spot.eval_fac;
|
||||
|
||||
const float radius = klight->spot.radius;
|
||||
|
||||
ls->Ng = radius > 0 ? normalize(ls->P - klight->co) : -ray_D;
|
||||
if (klight->spot.is_sphere) {
|
||||
const float d_sq = len_squared(ray_P - klight->co);
|
||||
ls->pdf = sphere_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;
|
||||
}
|
||||
|
||||
/* 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,16 +205,25 @@ 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);
|
||||
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;
|
||||
|
||||
@@ -43,82 +43,104 @@ 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;
|
||||
|
||||
float cos_theta;
|
||||
ls->t = FLT_MAX;
|
||||
if (d_sq > r_sq) {
|
||||
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);
|
||||
ls->eval_fac = klight->spot.eval_fac;
|
||||
|
||||
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 (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 (!ray_sphere_intersect(P, ls->D, 0.0f, FLT_MAX, center, radius, &ls->P, &ls->t)) {
|
||||
/* Sampled direction does not intersect with the light. */
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
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);
|
||||
}
|
||||
|
||||
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 {
|
||||
/* 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;
|
||||
}
|
||||
ls->Ng = normalize(ls->P - klight->co);
|
||||
ls->P = ls->Ng * klight->spot.radius + klight->co;
|
||||
|
||||
spot_light_uv(local_ray, klight->spot.half_cot_half_spot_angle, &ls->u, &ls->v);
|
||||
/* Texture coordinates. */
|
||||
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 oriented 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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -146,35 +168,40 @@ 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) {
|
||||
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);
|
||||
|
||||
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 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) *
|
||||
jacobian_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 {
|
||||
/* NOTE : preserve pdf in area measure. */
|
||||
ls->pdf = ls->eval_fac * 4.0f * M_PI_F;
|
||||
|
||||
ls->Ng = -ls->D;
|
||||
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
|
||||
/* PDF does not change. */
|
||||
}
|
||||
|
||||
/* 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,22 +226,37 @@ 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 (d_sq > r_sq) {
|
||||
|
||||
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 (!klight->spot.is_sphere || d_sq > r_sq) {
|
||||
ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray);
|
||||
}
|
||||
if (ls->eval_fac == 0) {
|
||||
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,16 +274,30 @@ 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;
|
||||
|
||||
if (in_volume_segment) {
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ class DATA_PT_EEVEE_light(DataButtonsPanel, Panel):
|
||||
col.separator()
|
||||
|
||||
if light.type in {'POINT', 'SPOT'}:
|
||||
col.prop(light, "use_soft_falloff")
|
||||
col.prop(light, "shadow_soft_size", text="Radius")
|
||||
elif light.type == 'SUN':
|
||||
col.prop(light, "angle")
|
||||
|
||||
@@ -2887,6 +2887,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.
|
||||
|
||||
@@ -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->mode & LA_USE_SOFT_FALLOFF) {
|
||||
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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -86,10 +86,17 @@ 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.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 2.0
|
||||
|
||||
#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 +240,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) {
|
||||
@@ -309,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) {
|
||||
@@ -328,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 == POINT) {
|
||||
else if (is_sphere_light(ld.l_type)) {
|
||||
if (l_vector.w < ld.l_radius) {
|
||||
/* Inside, treat as hemispherical light. */
|
||||
return 1.0;
|
||||
@@ -343,6 +355,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 +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 == POINT && 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;
|
||||
}
|
||||
@@ -373,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 == POINT) {
|
||||
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));
|
||||
@@ -381,10 +394,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
bool use_soft_falloff)
|
||||
{
|
||||
switch (blender_light_type) {
|
||||
default:
|
||||
case LA_LOCAL:
|
||||
return LIGHT_POINT;
|
||||
return use_soft_falloff ? LIGHT_OMNI_DISK : LIGHT_OMNI_SPHERE;
|
||||
case LA_SUN:
|
||||
return LIGHT_SUN;
|
||||
case LA_SPOT:
|
||||
return LIGHT_SPOT;
|
||||
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;
|
||||
}
|
||||
@@ -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->mode & LA_USE_SOFT_FALLOFF);
|
||||
if (assign_if_different(this->type, new_type)) {
|
||||
shadow_discard_safe(shadows);
|
||||
}
|
||||
|
||||
@@ -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,19 @@ 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_sphere_light(eLightType type)
|
||||
{
|
||||
return type == LIGHT_SPOT_SPHERE || type == LIGHT_OMNI_SPHERE;
|
||||
}
|
||||
|
||||
static inline bool is_sun_light(eLightType type)
|
||||
{
|
||||
return type < LIGHT_POINT;
|
||||
return type < LIGHT_OMNI_SPHERE;
|
||||
}
|
||||
|
||||
struct LightData {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 (is_sphere_light(light.type) && 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 (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 || light.type == LIGHT_SPOT_DISK) {
|
||||
/* View direction-aligned disk. */
|
||||
size = vec2(light._radius);
|
||||
}
|
||||
else {
|
||||
/* Sun light and 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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 (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);
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -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, \
|
||||
|
||||
@@ -139,6 +139,7 @@ enum {
|
||||
// LA_SHOW_SHADOW_BOX = 1 << 18,
|
||||
LA_SHAD_CONTACT = 1 << 19,
|
||||
LA_CUSTOM_ATTENUATION = 1 << 20,
|
||||
LA_USE_SOFT_FALLOFF = 1 << 21,
|
||||
};
|
||||
|
||||
/** #Light::falloff_type */
|
||||
|
||||
@@ -346,6 +346,15 @@ 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, "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,
|
||||
"Soft Falloff",
|
||||
"Apply falloff to avoid sharp edges when the light geometry intersects with other objects");
|
||||
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 +451,14 @@ 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, "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,
|
||||
"Soft Falloff",
|
||||
"Apply falloff to avoid sharp edges when the light geometry intersects with other objects");
|
||||
RNA_def_property_update(prop, 0, "rna_Light_draw_update");
|
||||
}
|
||||
|
||||
static void rna_def_sun_light(BlenderRNA *brna)
|
||||
|
||||
Reference in New Issue
Block a user