Lights: Option to use old point light falloff #117832
|
@ -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) {
|
||||
weizhen marked this conversation as resolved
Outdated
|
||||
/* 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)
|
||||
|
|
Loading…
Reference in New Issue
You need to account for the spot case here.