Cycles/EEVEE: change point light to double-sided sphere light #108506

Merged
Weizhen Huang merged 18 commits from weizhen/blender:point_light_to_sphere_light into main 2023-06-20 12:23:12 +02:00
5 changed files with 31 additions and 30 deletions
Showing only changes of commit ef9a90caaf - Show all commits

View File

@ -16,15 +16,15 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight,
ccl_private LightSample *ls)
{
float3 lightN = P - klight->co;
const float d_sqr = len_squared(lightN);
const float d = sqrtf(d_sqr);
const float d_sq = len_squared(lightN);
weizhen marked this conversation as resolved Outdated

The Blender convention is to use _sq suffix.

The Blender convention is to use `_sq` suffix.
const float d = sqrtf(d_sq);
lightN /= d;
const float r_sqr = sqr(klight->spot.radius);
const float r_sq = sqr(klight->spot.radius);
float cos_theta;
if (d_sqr > r_sqr) {
const float one_minus_cos = sin_sqr_to_one_minus_cos(r_sqr / d_sqr);
if (d_sq > r_sq) {
const float one_minus_cos = sin_sqr_to_one_minus_cos(r_sq / d_sq);
sample_uniform_cone_concentric(-lightN, one_minus_cos, rand, &cos_theta, &ls->D, &ls->pdf);
}
else {
@ -40,13 +40,12 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight,
}
/* Law of cosines. */
ls->t = d * cos_theta -
copysignf(safe_sqrtf(r_sqr - d_sqr + d_sqr * sqr(cos_theta)), d_sqr - r_sqr);
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 = M_1_PI_F * 0.25f * klight->spot.invarea;
if (r_sqr == 0) {
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. */
@ -68,10 +67,10 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight,
}
ccl_device_forceinline float point_light_pdf(
const float d_sqr, const float r_sqr, const float3 N, const float3 D, const uint32_t path_flag)
const float d_sq, const float r_sq, const float3 N, const float3 D, const uint32_t path_flag)
{
if (d_sqr > r_sqr) {
return M_1_2PI_F / sin_sqr_to_one_minus_cos(r_sqr / d_sqr);
if (d_sq > r_sq) {
return M_1_2PI_F / sin_sqr_to_one_minus_cos(r_sq / d_sq);
}
const bool has_transmission = (path_flag & PATH_RAY_MIS_HAD_TRANSMISSION);
@ -89,14 +88,14 @@ ccl_device_forceinline void point_light_mnee_sample_update(const ccl_global Kern
const float radius = klight->spot.radius;
if (radius > 0) {
const float d_sqr = len_squared(P - klight->co);
const float r_sqr = sqr(radius);
const float t_sqr = sqr(ls->t);
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_sqr, r_sqr, N, ls->D, path_flag);
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_sqr - r_sqr - t_sqr) / (radius * ls->t * t_sqr);
ls->pdf *= 0.5f * fabsf(d_sq - r_sq - t_sq) / (radius * ls->t * t_sq);
ls->Ng = normalize(ls->P - klight->co);
}

View File

@ -748,10 +748,10 @@ ccl_device_inline float cos_from_sin(const float s)
return safe_sqrtf(1.0f - sqr(s));
}
ccl_device_inline float sin_sqr_to_one_minus_cos(const float s2)
ccl_device_inline float sin_sqr_to_one_minus_cos(const float s_sq)
{
/* Using second-order Taylor expansion at small angles for better accuracy. */
return s2 > 0.0004f ? 1.0f - safe_sqrtf(1.0f - s2) : 0.5f * s2;
return s_sq > 0.0004f ? 1.0f - safe_sqrtf(1.0f - s_sq) : 0.5f * s_sq;
}
ccl_device_inline float pow20(float a)

View File

@ -19,24 +19,24 @@ ccl_device bool ray_sphere_intersect(float3 ray_P,
ccl_private float *isect_t)
{
const float3 d_vec = sphere_P - ray_P;
const float r_sqr = sphere_radius * sphere_radius;
const float d_sqr = dot(d_vec, d_vec);
const float r_sq = sphere_radius * sphere_radius;
const float d_sq = dot(d_vec, d_vec);
const float d_cos_theta = dot(d_vec, ray_D);
if (d_sqr > r_sqr && d_cos_theta < 0.0f) {
if (d_sq > r_sq && d_cos_theta < 0.0f) {
/* Ray origin outside sphere and points away from sphere. */
return false;
}
const float d_sin_theta_sqr = d_sqr - d_cos_theta * d_cos_theta;
const float d_sin_theta_sq = d_sq - d_cos_theta * d_cos_theta;
if (d_sin_theta_sqr > r_sqr) {
if (d_sin_theta_sq > r_sq) {
/* Closest point on ray outside sphere. */
return false;
}
/* Law of cosines. */
const float t = d_cos_theta - copysignf(sqrtf(r_sqr - d_sin_theta_sqr), d_sqr - r_sqr);
const float t = d_cos_theta - copysignf(sqrtf(r_sq - d_sin_theta_sq), d_sq - r_sq);
if (t > ray_tmin && t < ray_tmax) {
*isect_t = t;

View File

@ -334,9 +334,9 @@ float light_diffuse(LightData ld, vec3 N, vec3 V, vec4 l_vector)
/* Outside, treat as disk light spanning the same solid angle. */
/* The result is the same as passing the scaled radius to #ltc_evaluate_disk_simple (see
* #light_specular), using simplified math here. */
float r_sqr = sqr(ld.l_radius / l_vector.w);
float r_sq = sqr(ld.l_radius / l_vector.w);
vec3 L = l_vector.xyz / l_vector.w;
return r_sqr * diffuse_sphere_integral(dot(N, L), r_sqr);
return r_sq * diffuse_sphere_integral(dot(N, L), r_sq);
}
}
else {
@ -371,7 +371,8 @@ float light_specular(LightData ld, vec4 ltc_mat, vec3 N, vec3 V, vec4 l_vector)
float radius_y = is_ellipse ? ld.l_sizey : ld.l_radius;
if (ld.l_type == POINT) {
/* Sphere light spans a larger angle than a disk light with the same radius (sin -> tan). */
/* 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));
radius_y *= inversesqrt(1.0 - sqr(radius_y / l_vector.w));
}

View File

@ -115,8 +115,8 @@ float light_diffuse(sampler2DArray utility_tx,
/* Outside, treat as disk light spanning the same solid angle. */
/* The result is the same as passing the scaled radius to #ltc_evaluate_disk_simple (see
* #light_ltc), using simplified math here. */
float r_sqr = sqr(ld._radius / dist);
return r_sqr * ltc_diffuse_sphere_integral(utility_tx, dot(N, L), r_sqr);
float r_sq = sqr(ld._radius / dist);
return r_sq * ltc_diffuse_sphere_integral(utility_tx, dot(N, L), r_sq);
}
}
else if (is_directional || ld.type == LIGHT_SPOT) {
@ -174,7 +174,8 @@ float light_ltc(sampler2DArray utility_tx,
vec3 points[3];
if (ld.type == LIGHT_POINT) {
/* Sphere light spans a larger angle than a disk light with the same radius (sin -> tan). */
/* The sine of the half-angle spanned by a sphere light is equal to the tangent of the
weizhen marked this conversation as resolved Outdated

This sin -> tan is a bit cryptic. It could help if it was a full sentence.

This `sin -> tan` is a bit cryptic. It could help if it was a full sentence.
* half-angle spanned by a disk light with the same radius. */
float radius = ld._radius * inversesqrt(1.0 - sqr(ld._radius / dist));
points[0] = Px * -radius + Py * -radius;