Fix #107365: keep ls->D fixed in MNEE for area light with zero spread

This commit is contained in:
Weizhen Huang 2023-05-11 16:32:05 +02:00
parent ee08b2ddff
commit 1a1f06bd9a
2 changed files with 30 additions and 11 deletions

@ -398,6 +398,7 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg,
ccl_private const ShaderData *sd,
ccl_private ShaderData *sd_vtx,
ccl_private const LightSample *ls,
const bool light_fixed_direction,
int vertex_count,
ccl_private ManifoldVertex *vertices)
{
@ -413,7 +414,6 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg,
projection_ray.time = sd->time;
Intersection projection_isect;
const bool light_fixed_direction = (ls->t == FLT_MAX);
const float3 light_sample = light_fixed_direction ? ls->D : ls->P;
/* We start gently, potentially ramping up to beta = 1, since target configurations
@ -639,6 +639,7 @@ ccl_device_forceinline Spectrum mnee_eval_bsdf_contribution(ccl_private ShaderCl
/* Compute transfer matrix determinant |T1| = |dx1/dxn| (and |dh/dx| in the process) */
ccl_device_forceinline bool mnee_compute_transfer_matrix(ccl_private const ShaderData *sd,
ccl_private const LightSample *ls,
const bool light_fixed_direction,
int vertex_count,
ccl_private ManifoldVertex *vertices,
ccl_private float *dx1_dxlight,
@ -692,7 +693,7 @@ ccl_device_forceinline bool mnee_compute_transfer_matrix(ccl_private const Shade
float dxn_dwn;
float4 dc_dlight;
if (ls->t == FLT_MAX) {
if (light_fixed_direction) {
/* Constant direction toward light sample. */
float3 wo = ls->D;
@ -764,6 +765,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private ShaderData *sd_mnee,
ccl_private LightSample *ls,
const bool light_fixed_direction,
int vertex_count,
ccl_private ManifoldVertex *vertices,
ccl_private BsdfEval *throughput)
@ -807,7 +809,8 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
float dh_dx;
float dx1_dxlight;
if (!mnee_compute_transfer_matrix(sd, ls, vertex_count, vertices, &dx1_dxlight, &dh_dx))
if (!mnee_compute_transfer_matrix(
sd, ls, light_fixed_direction, vertex_count, vertices, &dx1_dxlight, &dh_dx))
return false;
/* Receiver bsdf eval above already contains |n.wo|. */
@ -882,7 +885,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg,
kg, state, sd_mnee, NULL, PATH_RAY_DIFFUSE, true);
/* Set light looking dir. */
wo = (vi == vertex_count - 1) ? (ls->t == FLT_MAX ? ls->D : ls->P - v.p) :
wo = (vi == vertex_count - 1) ? (light_fixed_direction ? ls->D : ls->P - v.p) :
vertices[vi + 1].p - v.p;
wo = normalize_len(wo, &wo_len);
@ -1039,12 +1042,22 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg,
* discontinuity is visible between direct and indirect contributions */
INTEGRATOR_STATE_WRITE(state, path, mnee) |= PATH_MNEE_VALID;
/* 2. Walk on the specular manifold to find vertices on the
* casters that satisfy snell's law for each interface
*/
if (mnee_newton_solver(kg, sd, sd_mnee, ls, vertex_count, vertices)) {
/* Distant or environment light. */
bool light_fixed_direction = (ls->t == FLT_MAX);
if (ls->type == LIGHT_AREA) {
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
if (klight->area.tan_half_spread == 0.0f) {
/* Area light with zero spread also has fixed direction. */
light_fixed_direction = true;
}
}
/* 2. Walk on the specular manifold to find vertices on the casters that satisfy snell's law for
* each interface. */
if (mnee_newton_solver(kg, sd, sd_mnee, ls, light_fixed_direction, vertex_count, vertices)) {
/* 3. If a solution exists, calculate contribution of the corresponding path */
if (!mnee_path_contribution(kg, state, sd, sd_mnee, ls, vertex_count, vertices, throughput))
if (!mnee_path_contribution(
kg, state, sd, sd_mnee, ls, light_fixed_direction, vertex_count, vertices, throughput))
return 0;
return vertex_count;

@ -340,8 +340,14 @@ ccl_device_forceinline void area_light_update_position(const ccl_global KernelLi
const float3 P)
{
const float invarea = fabsf(klight->area.invarea);
ls->D = normalize_len(ls->P - P, &ls->t);
ls->pdf = invarea;
if (klight->area.tan_half_spread == 0) {
/* Update position on the light to keep the direction fixed. */
area_light_sample<false>(klight, 0.0f, 0.0f, P, ls);
}
else {
ls->D = normalize_len(ls->P - P, &ls->t);
ls->pdf = invarea;
}
if (klight->area.normalize_spread > 0) {
ls->eval_fac = 0.25f * invarea;