Cycles/EEVEE: change point light to double-sided sphere light #108506
@ -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);
|
||||
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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
* 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;
|
||||
|
Loading…
Reference in New Issue
Block a user