diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 3f03118b105..dd96cfd5058 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -285,10 +285,16 @@ set(SRC_KERNEL_INTEGRATOR_HEADERS ) set(SRC_KERNEL_LIGHT_HEADERS - light/light.h + light/area.h light/background.h light/common.h + light/distant.h + light/distribution.h + light/light.h + light/point.h light/sample.h + light/spot.h + light/triangle.h ) set(SRC_KERNEL_SAMPLE_HEADERS diff --git a/intern/cycles/kernel/data_template.h b/intern/cycles/kernel/data_template.h index c7b50b20c70..9a44b21543c 100644 --- a/intern/cycles/kernel/data_template.h +++ b/intern/cycles/kernel/data_template.h @@ -23,24 +23,19 @@ KERNEL_STRUCT_MEMBER(background, int, volume_shader) KERNEL_STRUCT_MEMBER(background, float, volume_step_size) KERNEL_STRUCT_MEMBER(background, int, transparent) KERNEL_STRUCT_MEMBER(background, float, transparent_roughness_squared_threshold) -/* Portal sampling. */ -KERNEL_STRUCT_MEMBER(background, float, portal_weight) -KERNEL_STRUCT_MEMBER(background, int, num_portals) -KERNEL_STRUCT_MEMBER(background, int, portal_offset) /* Sun sampling. */ KERNEL_STRUCT_MEMBER(background, float, sun_weight) /* Importance map sampling. */ KERNEL_STRUCT_MEMBER(background, float, map_weight) +KERNEL_STRUCT_MEMBER(background, float, portal_weight) KERNEL_STRUCT_MEMBER(background, int, map_res_x) KERNEL_STRUCT_MEMBER(background, int, map_res_y) /* Multiple importance sampling. */ KERNEL_STRUCT_MEMBER(background, int, use_mis) /* Lightgroup. */ KERNEL_STRUCT_MEMBER(background, int, lightgroup) -/* Padding. */ -KERNEL_STRUCT_MEMBER(background, int, pad1) -KERNEL_STRUCT_MEMBER(background, int, pad2) -KERNEL_STRUCT_MEMBER(background, int, pad3) +/* Light Index. */ +KERNEL_STRUCT_MEMBER(background, int, light_index) KERNEL_STRUCT_END(KernelBackground) /* BVH: own BVH2 if no native device acceleration struct used. */ @@ -147,10 +142,17 @@ KERNEL_STRUCT_END(KernelFilm) KERNEL_STRUCT_BEGIN(KernelIntegrator, integrator) /* Emission. */ KERNEL_STRUCT_MEMBER(integrator, int, use_direct_light) +KERNEL_STRUCT_MEMBER(integrator, int, use_light_mis) +KERNEL_STRUCT_MEMBER(integrator, int, num_lights) +KERNEL_STRUCT_MEMBER(integrator, int, num_distant_lights) +KERNEL_STRUCT_MEMBER(integrator, int, num_background_lights) +/* Portal sampling. */ +KERNEL_STRUCT_MEMBER(integrator, int, num_portals) +KERNEL_STRUCT_MEMBER(integrator, int, portal_offset) +/* Flat light distribution. */ KERNEL_STRUCT_MEMBER(integrator, int, num_distribution) -KERNEL_STRUCT_MEMBER(integrator, int, num_all_lights) -KERNEL_STRUCT_MEMBER(integrator, float, pdf_triangles) -KERNEL_STRUCT_MEMBER(integrator, float, pdf_lights) +KERNEL_STRUCT_MEMBER(integrator, float, distribution_pdf_triangles) +KERNEL_STRUCT_MEMBER(integrator, float, distribution_pdf_lights) KERNEL_STRUCT_MEMBER(integrator, float, light_inv_rr_threshold) /* Bounces. */ KERNEL_STRUCT_MEMBER(integrator, int, min_bounce) @@ -177,8 +179,6 @@ KERNEL_STRUCT_MEMBER(integrator, int, seed) /* Clamp. */ KERNEL_STRUCT_MEMBER(integrator, float, sample_clamp_direct) KERNEL_STRUCT_MEMBER(integrator, float, sample_clamp_indirect) -/* MIS. */ -KERNEL_STRUCT_MEMBER(integrator, int, use_lamp_mis) /* Caustics. */ KERNEL_STRUCT_MEMBER(integrator, int, use_caustics) /* Sampling pattern. */ @@ -195,7 +195,6 @@ KERNEL_STRUCT_MEMBER(integrator, int, has_shadow_catcher) KERNEL_STRUCT_MEMBER(integrator, int, filter_closures) /* MIS debugging. */ KERNEL_STRUCT_MEMBER(integrator, int, direct_light_sampling_type) - /* Path Guiding */ KERNEL_STRUCT_MEMBER(integrator, float, surface_guiding_probability) KERNEL_STRUCT_MEMBER(integrator, float, volume_guiding_probability) diff --git a/intern/cycles/kernel/integrator/intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h index b9a81e25bcc..dc171885755 100644 --- a/intern/cycles/kernel/integrator/intersect_closest.h +++ b/intern/cycles/kernel/integrator/intersect_closest.h @@ -11,10 +11,10 @@ #include "kernel/integrator/path_state.h" #include "kernel/integrator/shadow_catcher.h" -#include "kernel/light/light.h" - #include "kernel/geom/geom.h" +#include "kernel/light/light.h" + #include "kernel/bvh/bvh.h" CCL_NAMESPACE_BEGIN @@ -387,7 +387,7 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg, #endif /* __MNEE__ */ /* Light intersection for MIS. */ - if (kernel_data.integrator.use_lamp_mis) { + if (kernel_data.integrator.use_light_mis) { /* NOTE: if we make lights visible to camera rays, we'll need to initialize * these in the path_state_init. */ const int last_type = INTEGRATOR_STATE(state, isect, type); diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index 23885306885..7f5f2c97497 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -108,48 +108,6 @@ ccl_device_inline float mat22_inverse(const float4 m, ccl_private float4 &m_inve return det; } -/* Update light sample */ -ccl_device_forceinline void mnee_update_light_sample(KernelGlobals kg, - const float3 P, - ccl_private LightSample *ls) -{ - /* correct light sample position/direction and pdf - * NOTE: preserve pdf in area measure */ - const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp); - - if (ls->type == LIGHT_POINT || ls->type == LIGHT_SPOT) { - ls->D = normalize_len(ls->P - P, &ls->t); - ls->Ng = -ls->D; - - float2 uv = map_to_sphere(ls->Ng); - ls->u = uv.x; - ls->v = uv.y; - - float invarea = klight->spot.invarea; - ls->eval_fac = (0.25f * M_1_PI_F) * invarea; - ls->pdf = invarea; - - if (ls->type == LIGHT_SPOT) { - /* spot light attenuation */ - float3 dir = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]); - ls->eval_fac *= spot_light_attenuation( - dir, klight->spot.spot_angle, klight->spot.spot_smooth, ls->Ng); - } - } - else if (ls->type == LIGHT_AREA) { - float invarea = fabsf(klight->area.invarea); - ls->D = normalize_len(ls->P - P, &ls->t); - ls->pdf = invarea; - if (klight->area.tan_spread > 0.f) { - ls->eval_fac = 0.25f * invarea; - ls->eval_fac *= light_spread_attenuation( - ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread); - } - } - - ls->pdf *= kernel_data.integrator.pdf_lights; -} - /* Manifold vertex setup from ray and intersection data */ ccl_device_forceinline void mnee_setup_manifold_vertex(KernelGlobals kg, ccl_private ManifoldVertex *vtx, @@ -819,7 +777,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, /* Update light sample with new position / direct.ion * and keep pdf in vertex area measure */ - mnee_update_light_sample(kg, vertices[vertex_count - 1].p, ls); + light_sample_update_position(kg, ls, vertices[vertex_count - 1].p); /* Save state path bounce info in case a light path node is used in the refractive interface or * light shader graph. */ diff --git a/intern/cycles/kernel/integrator/shade_background.h b/intern/cycles/kernel/integrator/shade_background.h index f35a6d308f7..81fc7a46a0c 100644 --- a/intern/cycles/kernel/integrator/shade_background.h +++ b/intern/cycles/kernel/integrator/shade_background.h @@ -69,9 +69,9 @@ ccl_device_inline void integrate_background(KernelGlobals kg, bool eval_background = true; float transparent = 0.0f; + int path_flag = INTEGRATOR_STATE(state, path, flag); const bool is_transparent_background_ray = kernel_data.background.transparent && - (INTEGRATOR_STATE(state, path, flag) & - PATH_RAY_TRANSPARENT_BACKGROUND); + (path_flag & PATH_RAY_TRANSPARENT_BACKGROUND); if (is_transparent_background_ray) { transparent = average(INTEGRATOR_STATE(state, path, throughput)); @@ -86,7 +86,7 @@ ccl_device_inline void integrate_background(KernelGlobals kg, #ifdef __MNEE__ if (INTEGRATOR_STATE(state, path, mnee) & PATH_MNEE_CULL_LIGHT_CONNECTION) { if (kernel_data.background.use_mis) { - for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { + for (int lamp = 0; lamp < kernel_data.integrator.num_lights; lamp++) { /* This path should have been resolved with mnee, it will * generate a firefly for small lights since it is improbable. */ const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp); @@ -116,14 +116,7 @@ ccl_device_inline void integrate_background(KernelGlobals kg, /* Check if background light exists or if we should skip pdf. */ if (!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_MIS_SKIP) && kernel_data.background.use_mis) { - const float3 ray_P = INTEGRATOR_STATE(state, ray, P); - const float3 ray_D = INTEGRATOR_STATE(state, ray, D); - const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); - - /* multiple importance sampling, get background light pdf for ray - * direction, and compute weight with respect to BSDF pdf */ - const float pdf = background_light_pdf(kg, ray_P, ray_D); - mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf); + mis_weight = light_sample_mis_weight_forward_background(kg, state, path_flag); } guiding_record_background(kg, state, L, mis_weight); @@ -142,8 +135,8 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg, const float3 ray_D = INTEGRATOR_STATE(state, ray, D); const float ray_time = INTEGRATOR_STATE(state, ray, time); LightSample ls ccl_optional_struct_init; - for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { - if (light_sample_from_distant_ray(kg, ray_D, lamp, &ls)) { + for (int lamp = 0; lamp < kernel_data.integrator.num_lights; lamp++) { + if (distant_light_sample_from_intersection(kg, ray_D, lamp, &ls)) { /* Use visibility flag to skip lights. */ #ifdef __PASSES__ const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); @@ -182,10 +175,7 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg, /* MIS weighting. */ float mis_weight = 1.0f; if (!(path_flag & PATH_RAY_MIS_SKIP)) { - /* multiple importance sampling, get regular light pdf, - * and compute weight with respect to BSDF pdf */ - const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); - mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, ls.pdf); + mis_weight = light_sample_mis_weight_forward_distant(kg, state, path_flag, &ls); } /* Write to render buffer. */ diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h index e0b0500dc78..1f09ac4e6d8 100644 --- a/intern/cycles/kernel/integrator/shade_light.h +++ b/intern/cycles/kernel/integrator/shade_light.h @@ -61,10 +61,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg, /* MIS weighting. */ float mis_weight = 1.0f; if (!(path_flag & PATH_RAY_MIS_SKIP)) { - /* multiple importance sampling, get regular light pdf, - * and compute weight with respect to BSDF pdf */ - const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); - mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, ls.pdf); + mis_weight = light_sample_mis_weight_forward_lamp(kg, state, path_flag, &ls, ray_P); } /* Write to render buffer. */ diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index 067d35ef9e3..05b42af0bfc 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -15,7 +15,6 @@ #include "kernel/integrator/surface_shader.h" #include "kernel/integrator/volume_stack.h" -#include "kernel/light/light.h" #include "kernel/light/sample.h" CCL_NAMESPACE_BEGIN @@ -120,13 +119,7 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg, if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS)) #endif { - const float bsdf_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); - const float t = sd->ray_length; - - /* Multiple importance sampling, get triangle light pdf, - * and compute weight with respect to BSDF pdf. */ - float pdf = triangle_light_pdf(kg, sd, t); - mis_weight = light_sample_mis_weight_forward(kg, bsdf_pdf, pdf); + mis_weight = light_sample_mis_weight_forward_surface(kg, state, path_flag, sd); } guiding_record_surface_emission(kg, state, L, mis_weight); @@ -154,8 +147,17 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, const uint bounce = INTEGRATOR_STATE(state, path, bounce); const float2 rand_light = path_state_rng_2D(kg, rng_state, PRNG_LIGHT); - if (!light_distribution_sample_from_position( - kg, rand_light.x, rand_light.y, sd->time, sd->P, bounce, path_flag, &ls)) { + if (!light_sample_from_position(kg, + rng_state, + rand_light.x, + rand_light.y, + sd->time, + sd->P, + sd->N, + sd->flag, + bounce, + path_flag, + &ls)) { return; } } diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h index 46d13f59c6a..d84f41c1269 100644 --- a/intern/cycles/kernel/integrator/shade_volume.h +++ b/intern/cycles/kernel/integrator/shade_volume.h @@ -690,6 +690,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous( ccl_device_forceinline bool integrate_volume_sample_light( KernelGlobals kg, IntegratorState state, + ccl_private const Ray *ccl_restrict ray, ccl_private const ShaderData *ccl_restrict sd, ccl_private const RNGState *ccl_restrict rng_state, ccl_private LightSample *ccl_restrict ls) @@ -704,8 +705,16 @@ ccl_device_forceinline bool integrate_volume_sample_light( const uint bounce = INTEGRATOR_STATE(state, path, bounce); const float2 rand_light = path_state_rng_2D(kg, rng_state, PRNG_LIGHT); - if (!light_distribution_sample_from_volume_segment( - kg, rand_light.x, rand_light.y, sd->time, sd->P, bounce, path_flag, ls)) { + if (!light_sample_from_volume_segment(kg, + rand_light.x, + rand_light.y, + sd->time, + sd->P, + ray->D, + ray->tmax - ray->tmin, + bounce, + path_flag, + ls)) { return false; } @@ -737,18 +746,32 @@ ccl_device_forceinline void integrate_volume_direct_light( return; } - /* Sample position on the same light again, now from the shading - * point where we scattered. + /* Sample position on the same light again, now from the shading point where we scattered. * - * TODO: decorrelate random numbers and use light_sample_new_position to - * avoid resampling the CDF. */ + * Note that this means we sample the light tree twice when equiangular sampling is used. + * We could consider sampling the light tree just once and use the same light position again. + * + * This would make the PDFs for MIS weights more complicated due to having to account for + * both distance/equiangular and direct/indirect light sampling, but could be more accurate. + * Additionally we could end up behind the light or outside a spot light cone, which might + * waste a sample. Though on the other hand it would be possible to prevent that with + * equiangular sampling restricted to a smaller sub-segment where the light has influence. */ { const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); const uint bounce = INTEGRATOR_STATE(state, path, bounce); const float2 rand_light = path_state_rng_2D(kg, rng_state, PRNG_LIGHT); - if (!light_distribution_sample_from_position( - kg, rand_light.x, rand_light.y, sd->time, P, bounce, path_flag, ls)) { + if (!light_sample_from_position(kg, + rng_state, + rand_light.x, + rand_light.y, + sd->time, + P, + zero_float3(), + 0, + bounce, + path_flag, + ls)) { return; } } @@ -987,7 +1010,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg, const bool need_light_sample = !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE); const bool have_equiangular_sample = need_light_sample && integrate_volume_sample_light( - kg, state, &sd, &rng_state, &ls) && + kg, state, ray, &sd, &rng_state, &ls) && (ls.t != FLT_MAX); VolumeSampleMethod direct_sample_method = (have_equiangular_sample) ? diff --git a/intern/cycles/kernel/light/area.h b/intern/cycles/kernel/light/area.h new file mode 100644 index 00000000000..212631d363d --- /dev/null +++ b/intern/cycles/kernel/light/area.h @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +#include "kernel/light/common.h" + +CCL_NAMESPACE_BEGIN + +/* Importance sampling. + * + * An Area-Preserving Parametrization for Spherical Rectangles. + * Carlos Urena et al. + * + * NOTE: light_p is modified when sample_coord is true. */ +ccl_device_inline float area_light_rect_sample(float3 P, + ccl_private float3 *light_p, + float3 extentu, + float3 extentv, + float randu, + float randv, + bool sample_coord) +{ + /* In our name system we're using P for the center, which is o in the paper. */ + float3 corner = *light_p - extentu * 0.5f - extentv * 0.5f; + float extentu_len, extentv_len; + /* Compute local reference system R. */ + float3 x = normalize_len(extentu, &extentu_len); + float3 y = normalize_len(extentv, &extentv_len); + float3 z = cross(x, y); + /* Compute rectangle coords in local reference system. */ + float3 dir = corner - P; + float z0 = dot(dir, z); + /* Flip 'z' to make it point against Q. */ + if (z0 > 0.0f) { + z *= -1.0f; + z0 *= -1.0f; + } + float x0 = dot(dir, x); + float y0 = dot(dir, y); + float x1 = x0 + extentu_len; + float y1 = y0 + extentv_len; + /* Compute internal angles (gamma_i). */ + float4 diff = make_float4(x0, y1, x1, y0) - make_float4(x1, y0, x0, y1); + float4 nz = make_float4(y0, x1, y1, x0) * diff; + nz = nz / sqrt(z0 * z0 * diff * diff + nz * nz); + float g0 = safe_acosf(-nz.x * nz.y); + float g1 = safe_acosf(-nz.y * nz.z); + float g2 = safe_acosf(-nz.z * nz.w); + float g3 = safe_acosf(-nz.w * nz.x); + /* Compute predefined constants. */ + float b0 = nz.x; + float b1 = nz.z; + float b0sq = b0 * b0; + float k = M_2PI_F - g2 - g3; + /* Compute solid angle from internal angles. */ + float S = g0 + g1 - k; + + if (sample_coord) { + /* Compute cu. */ + float au = randu * S + k; + float fu = (cosf(au) * b0 - b1) / sinf(au); + float cu = 1.0f / sqrtf(fu * fu + b0sq) * (fu > 0.0f ? 1.0f : -1.0f); + cu = clamp(cu, -1.0f, 1.0f); + /* Compute xu. */ + float xu = -(cu * z0) / max(sqrtf(1.0f - cu * cu), 1e-7f); + xu = clamp(xu, x0, x1); + /* Compute yv. */ + float z0sq = z0 * z0; + float y0sq = y0 * y0; + float y1sq = y1 * y1; + float d = sqrtf(xu * xu + z0sq); + float h0 = y0 / sqrtf(d * d + y0sq); + float h1 = y1 / sqrtf(d * d + y1sq); + float hv = h0 + randv * (h1 - h0), hv2 = hv * hv; + float yv = (hv2 < 1.0f - 1e-6f) ? (hv * d) / sqrtf(1.0f - hv2) : y1; + + /* Transform (xu, yv, z0) to world coords. */ + *light_p = P + xu * x + yv * y + z0 * z; + } + + /* return pdf */ + if (S != 0.0f) + return 1.0f / S; + else + return 0.0f; +} + +/* Light spread. */ + +ccl_device float area_light_spread_attenuation(const float3 D, + const float3 lightNg, + const float tan_spread, + const float normalize_spread) +{ + /* Model a soft-box grid, computing the ratio of light not hidden by the + * slats of the grid at a given angle. (see D10594). */ + const float cos_a = -dot(D, lightNg); + const float sin_a = safe_sqrtf(1.0f - sqr(cos_a)); + const float tan_a = sin_a / cos_a; + return max((1.0f - (tan_spread * tan_a)) * normalize_spread, 0.0f); +} + +/* Compute subset of area light that actually has an influence on the shading point, to + * reduce noise with low spread. */ +ccl_device bool area_light_spread_clamp_area_light(const float3 P, + const float3 lightNg, + ccl_private float3 *lightP, + ccl_private float3 *extentu, + ccl_private float3 *extentv, + const float tan_spread) +{ + /* Closest point in area light plane and distance to that plane. */ + const float3 closest_P = P - dot(lightNg, P - *lightP) * lightNg; + const float t = len(closest_P - P); + + /* Radius of circle on area light that actually affects the shading point. */ + const float radius = t / tan_spread; + + /* TODO: would be faster to store as normalized vector + length, also in area_light_rect_sample. + */ + float len_u, len_v; + const float3 u = normalize_len(*extentu, &len_u); + const float3 v = normalize_len(*extentv, &len_v); + + /* Local uv coordinates of closest point. */ + const float closest_u = dot(u, closest_P - *lightP); + const float closest_v = dot(v, closest_P - *lightP); + + /* Compute rectangle encompassing the circle that affects the shading point, + * clamped to the bounds of the area light. */ + const float min_u = max(closest_u - radius, -len_u * 0.5f); + const float max_u = min(closest_u + radius, len_u * 0.5f); + const float min_v = max(closest_v - radius, -len_v * 0.5f); + const float max_v = min(closest_v + radius, len_v * 0.5f); + + /* Skip if rectangle is empty. */ + if (min_u >= max_u || min_v >= max_v) { + return false; + } + + /* Compute new area light center position and axes from rectangle in local + * uv coordinates. */ + const float new_center_u = 0.5f * (min_u + max_u); + const float new_center_v = 0.5f * (min_v + max_v); + const float new_len_u = max_u - min_u; + const float new_len_v = max_v - min_v; + + *lightP = *lightP + new_center_u * u + new_center_v * v; + *extentu = u * new_len_u; + *extentv = v * new_len_v; + + return true; +} + +/* Common API. */ + +template +ccl_device_inline bool area_light_sample(const ccl_global KernelLight *klight, + const float randu, + const float randv, + const float3 P, + ccl_private LightSample *ls) +{ + ls->P = klight->co; + + float3 extentu = klight->area.extentu; + float3 extentv = klight->area.extentv; + float3 Ng = klight->area.dir; + float invarea = fabsf(klight->area.invarea); + bool is_round = (klight->area.invarea < 0.0f); + + if (!in_volume_segment) { + if (dot(ls->P - P, Ng) > 0.0f) { + return false; + } + } + + float3 inplane; + + if (is_round || in_volume_segment) { + inplane = ellipse_sample(extentu * 0.5f, extentv * 0.5f, randu, randv); + ls->P += inplane; + ls->pdf = invarea; + } + else { + inplane = ls->P; + + float3 sample_extentu = extentu; + float3 sample_extentv = extentv; + + if (!in_volume_segment && klight->area.tan_spread > 0.0f) { + if (!area_light_spread_clamp_area_light( + P, Ng, &ls->P, &sample_extentu, &sample_extentv, klight->area.tan_spread)) { + return false; + } + } + + ls->pdf = area_light_rect_sample( + P, &ls->P, sample_extentu, sample_extentv, randu, randv, true); + inplane = ls->P - inplane; + } + + const float light_u = dot(inplane, extentu) * (1.0f / dot(extentu, extentu)); + const float light_v = dot(inplane, extentv) * (1.0f / dot(extentv, extentv)); + + /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */ + ls->u = light_v + 0.5f; + ls->v = -light_u - light_v; + + ls->Ng = Ng; + ls->D = normalize_len(ls->P - P, &ls->t); + + ls->eval_fac = 0.25f * invarea; + + if (klight->area.tan_spread > 0.0f) { + /* Area Light spread angle attenuation */ + ls->eval_fac *= area_light_spread_attenuation( + ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread); + } + + if (is_round) { + ls->pdf *= lamp_light_pdf(Ng, -ls->D, ls->t); + } + + return true; +} + +ccl_device_forceinline void area_light_update_position(const ccl_global KernelLight *klight, + ccl_private LightSample *ls, + 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_spread > 0.f) { + ls->eval_fac = 0.25f * invarea; + ls->eval_fac *= area_light_spread_attenuation( + ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread); + } +} + +ccl_device_inline bool area_light_intersect(const ccl_global KernelLight *klight, + const ccl_private Ray *ccl_restrict ray, + ccl_private float *t, + ccl_private float *u, + ccl_private float *v) +{ + /* Area light. */ + const float invarea = fabsf(klight->area.invarea); + const bool is_round = (klight->area.invarea < 0.0f); + if (invarea == 0.0f) { + return false; + } + + const float3 extentu = klight->area.extentu; + const float3 extentv = klight->area.extentv; + const float3 Ng = klight->area.dir; + + /* One sided. */ + if (dot(ray->D, Ng) >= 0.0f) { + return false; + } + + const float3 light_P = klight->co; + + float3 P; + return ray_quad_intersect( + ray->P, ray->D, ray->tmin, ray->tmax, light_P, extentu, extentv, Ng, &P, t, u, v, is_round); +} + +ccl_device_inline bool area_light_sample_from_intersection( + const ccl_global KernelLight *klight, + ccl_private const Intersection *ccl_restrict isect, + const float3 ray_P, + const float3 ray_D, + ccl_private LightSample *ccl_restrict ls) +{ + + /* area light */ + float invarea = fabsf(klight->area.invarea); + + float3 extentu = klight->area.extentu; + float3 extentv = klight->area.extentv; + float3 Ng = klight->area.dir; + float3 light_P = klight->co; + + ls->u = isect->u; + ls->v = isect->v; + ls->D = ray_D; + ls->Ng = Ng; + + const bool is_round = (klight->area.invarea < 0.0f); + if (is_round) { + ls->pdf = invarea * lamp_light_pdf(Ng, -ray_D, ls->t); + } + else { + float3 sample_extentu = extentu; + float3 sample_extentv = extentv; + + if (klight->area.tan_spread > 0.0f) { + if (!area_light_spread_clamp_area_light( + ray_P, Ng, &light_P, &sample_extentu, &sample_extentv, klight->area.tan_spread)) { + return false; + } + } + + ls->pdf = area_light_rect_sample(ray_P, &light_P, sample_extentu, sample_extentv, 0, 0, false); + } + ls->eval_fac = 0.25f * invarea; + + if (klight->area.tan_spread > 0.0f) { + /* Area Light spread angle attenuation */ + ls->eval_fac *= area_light_spread_attenuation( + ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread); + if (ls->eval_fac == 0.0f) { + return false; + } + } + + return true; +} +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/light/background.h b/intern/cycles/kernel/light/background.h index 951620ff1cb..894aca0a154 100644 --- a/intern/cycles/kernel/light/background.h +++ b/intern/cycles/kernel/light/background.h @@ -3,6 +3,7 @@ #pragma once +#include "kernel/light/area.h" #include "kernel/light/common.h" CCL_NAMESPACE_BEGIN @@ -130,11 +131,11 @@ ccl_device float background_map_pdf(KernelGlobals kg, float3 direction) ccl_device_inline bool background_portal_data_fetch_and_check_side( KernelGlobals kg, float3 P, int index, ccl_private float3 *lightpos, ccl_private float3 *dir) { - int portal = kernel_data.background.portal_offset + index; + int portal = kernel_data.integrator.portal_offset + index; const ccl_global KernelLight *klight = &kernel_data_fetch(lights, portal); - *lightpos = make_float3(klight->co[0], klight->co[1], klight->co[2]); - *dir = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]); + *lightpos = klight->co; + *dir = klight->area.dir; /* Check whether portal is on the right side. */ if (dot(*dir, P - *lightpos) > 1e-4f) @@ -149,7 +150,7 @@ ccl_device_inline float background_portal_pdf( float portal_pdf = 0.0f; int num_possible = 0; - for (int p = 0; p < kernel_data.background.num_portals; p++) { + for (int p = 0; p < kernel_data.integrator.num_portals; p++) { if (p == ignore_portal) continue; @@ -163,12 +164,10 @@ ccl_device_inline float background_portal_pdf( } num_possible++; - int portal = kernel_data.background.portal_offset + p; + int portal = kernel_data.integrator.portal_offset + p; const ccl_global KernelLight *klight = &kernel_data_fetch(lights, portal); - float3 axisu = make_float3( - klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); - float3 axisv = make_float3( - klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); + float3 extentu = klight->area.extentu; + float3 extentv = klight->area.extentv; bool is_round = (klight->area.invarea < 0.0f); if (!ray_quad_intersect(P, @@ -176,8 +175,8 @@ ccl_device_inline float background_portal_pdf( 1e-4f, FLT_MAX, lightpos, - axisu, - axisv, + extentu, + extentv, dir, NULL, NULL, @@ -189,10 +188,10 @@ 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(kg, dir, -D, t); + portal_pdf += fabsf(klight->area.invarea) * lamp_light_pdf(dir, -D, t); } else { - portal_pdf += rect_light_sample(P, &lightpos, axisu, axisv, 0.0f, 0.0f, false); + portal_pdf += area_light_rect_sample(P, &lightpos, extentu, extentv, 0.0f, 0.0f, false); } } @@ -207,7 +206,7 @@ ccl_device_inline float background_portal_pdf( ccl_device int background_num_possible_portals(KernelGlobals kg, float3 P) { int num_possible_portals = 0; - for (int p = 0; p < kernel_data.background.num_portals; p++) { + for (int p = 0; p < kernel_data.integrator.num_portals; p++) { float3 lightpos, dir; if (background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir)) num_possible_portals++; @@ -231,7 +230,7 @@ ccl_device float3 background_portal_sample(KernelGlobals kg, /* TODO(sergey): Some smarter way of finding portal to sample * is welcome. */ - for (int p = 0; p < kernel_data.background.num_portals; p++) { + for (int p = 0; p < kernel_data.integrator.num_portals; p++) { /* Search for the sampled portal. */ float3 lightpos, dir; if (!background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir)) @@ -239,23 +238,21 @@ ccl_device float3 background_portal_sample(KernelGlobals kg, if (portal == 0) { /* p is the portal to be sampled. */ - int portal = kernel_data.background.portal_offset + p; + int portal = kernel_data.integrator.portal_offset + p; const ccl_global KernelLight *klight = &kernel_data_fetch(lights, portal); - float3 axisu = make_float3( - klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); - float3 axisv = make_float3( - klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); + float3 extentu = klight->area.extentu; + float3 extentv = klight->area.extentv; bool is_round = (klight->area.invarea < 0.0f); float3 D; if (is_round) { - lightpos += ellipse_sample(axisu * 0.5f, axisv * 0.5f, randu, randv); + lightpos += ellipse_sample(extentu * 0.5f, extentv * 0.5f, randu, randv); float t; D = normalize_len(lightpos - P, &t); - *pdf = fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t); + *pdf = fabsf(klight->area.invarea) * lamp_light_pdf(dir, -D, t); } else { - *pdf = rect_light_sample(P, &lightpos, axisu, axisv, randu, randv, true); + *pdf = area_light_rect_sample(P, &lightpos, extentu, extentv, randu, randv, true); D = normalize(lightpos - P); } @@ -414,7 +411,7 @@ ccl_device float background_light_pdf(KernelGlobals kg, float3 P, float3 directi float pdf_fac = (portal_method_pdf + sun_method_pdf + map_method_pdf); if (pdf_fac == 0.0f) { /* Use uniform as a fallback if we can't use any strategy. */ - return kernel_data.integrator.pdf_lights / M_4PI_F; + return 1.0f / M_4PI_F; } pdf_fac = 1.0f / pdf_fac; @@ -430,7 +427,6 @@ ccl_device float background_light_pdf(KernelGlobals kg, float3 P, float3 directi pdf += background_map_pdf(kg, direction) * map_method_pdf; } - return pdf * kernel_data.integrator.pdf_lights; + return pdf; } - CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/light/common.h b/intern/cycles/kernel/light/common.h index 0daae16eebe..5f0a3218ae1 100644 --- a/intern/cycles/kernel/light/common.h +++ b/intern/cycles/kernel/light/common.h @@ -7,92 +7,26 @@ CCL_NAMESPACE_BEGIN -/* Area light sampling */ +/* Light Sample Result */ -/* Uses the following paper: - * - * Carlos Urena et al. - * An Area-Preserving Parametrization for Spherical Rectangles. - * - * https://www.solidangle.com/research/egsr2013_spherical_rectangle.pdf - * - * NOTE: light_p is modified when sample_coord is true. - */ -ccl_device_inline float rect_light_sample(float3 P, - ccl_private float3 *light_p, - float3 axisu, - float3 axisv, - float randu, - float randv, - bool sample_coord) -{ - /* In our name system we're using P for the center, - * which is o in the paper. - */ +typedef struct LightSample { + float3 P; /* position on light, or direction for distant light */ + float3 Ng; /* normal on light */ + float3 D; /* direction from shading point to light */ + float t; /* distance to light (FLT_MAX for distant light) */ + float u, v; /* parametric coordinate on primitive */ + float pdf; /* pdf for selecting light and point on light */ + float pdf_selection; /* pdf for selecting light */ + float eval_fac; /* intensity multiplier */ + int object; /* object id for triangle/curve lights */ + int prim; /* primitive id for triangle/curve lights */ + int shader; /* shader id */ + int lamp; /* lamp id */ + int group; /* lightgroup */ + LightType type; /* type of light */ +} LightSample; - float3 corner = *light_p - axisu * 0.5f - axisv * 0.5f; - float axisu_len, axisv_len; - /* Compute local reference system R. */ - float3 x = normalize_len(axisu, &axisu_len); - float3 y = normalize_len(axisv, &axisv_len); - float3 z = cross(x, y); - /* Compute rectangle coords in local reference system. */ - float3 dir = corner - P; - float z0 = dot(dir, z); - /* Flip 'z' to make it point against Q. */ - if (z0 > 0.0f) { - z *= -1.0f; - z0 *= -1.0f; - } - float x0 = dot(dir, x); - float y0 = dot(dir, y); - float x1 = x0 + axisu_len; - float y1 = y0 + axisv_len; - /* Compute internal angles (gamma_i). */ - float4 diff = make_float4(x0, y1, x1, y0) - make_float4(x1, y0, x0, y1); - float4 nz = make_float4(y0, x1, y1, x0) * diff; - nz = nz / sqrt(z0 * z0 * diff * diff + nz * nz); - float g0 = safe_acosf(-nz.x * nz.y); - float g1 = safe_acosf(-nz.y * nz.z); - float g2 = safe_acosf(-nz.z * nz.w); - float g3 = safe_acosf(-nz.w * nz.x); - /* Compute predefined constants. */ - float b0 = nz.x; - float b1 = nz.z; - float b0sq = b0 * b0; - float k = M_2PI_F - g2 - g3; - /* Compute solid angle from internal angles. */ - float S = g0 + g1 - k; - - if (sample_coord) { - /* Compute cu. */ - float au = randu * S + k; - float fu = (cosf(au) * b0 - b1) / sinf(au); - float cu = 1.0f / sqrtf(fu * fu + b0sq) * (fu > 0.0f ? 1.0f : -1.0f); - cu = clamp(cu, -1.0f, 1.0f); - /* Compute xu. */ - float xu = -(cu * z0) / max(sqrtf(1.0f - cu * cu), 1e-7f); - xu = clamp(xu, x0, x1); - /* Compute yv. */ - float z0sq = z0 * z0; - float y0sq = y0 * y0; - float y1sq = y1 * y1; - float d = sqrtf(xu * xu + z0sq); - float h0 = y0 / sqrtf(d * d + y0sq); - float h1 = y1 / sqrtf(d * d + y1sq); - float hv = h0 + randv * (h1 - h0), hv2 = hv * hv; - float yv = (hv2 < 1.0f - 1e-6f) ? (hv * d) / sqrtf(1.0f - hv2) : y1; - - /* Transform (xu, yv, z0) to world coords. */ - *light_p = P + xu * x + yv * y + z0 * z; - } - - /* return pdf */ - if (S != 0.0f) - return 1.0f / S; - else - return 0.0f; -} +/* Utilities */ ccl_device_inline float3 ellipse_sample(float3 ru, float3 rv, float randu, float randv) { @@ -109,99 +43,7 @@ ccl_device float3 disk_light_sample(float3 v, float randu, float randv) return ellipse_sample(ru, rv, randu, randv); } -ccl_device float3 distant_light_sample(float3 D, float radius, float randu, float randv) -{ - return normalize(D + disk_light_sample(D, randu, randv) * radius); -} - -ccl_device float3 -sphere_light_sample(float3 P, float3 center, float radius, float randu, float randv) -{ - return disk_light_sample(normalize(P - center), randu, randv) * radius; -} - -ccl_device float spot_light_attenuation(float3 dir, float spot_angle, float spot_smooth, float3 N) -{ - float attenuation = dot(dir, N); - - if (attenuation <= spot_angle) { - attenuation = 0.0f; - } - else { - float t = attenuation - spot_angle; - - if (t < spot_smooth && spot_smooth != 0.0f) - attenuation *= smoothstepf(t / spot_smooth); - } - - return attenuation; -} - -ccl_device float light_spread_attenuation(const float3 D, - const float3 lightNg, - const float tan_spread, - const float normalize_spread) -{ - /* Model a soft-box grid, computing the ratio of light not hidden by the - * slats of the grid at a given angle. (see D10594). */ - const float cos_a = -dot(D, lightNg); - const float sin_a = safe_sqrtf(1.0f - sqr(cos_a)); - const float tan_a = sin_a / cos_a; - return max((1.0f - (tan_spread * tan_a)) * normalize_spread, 0.0f); -} - -/* Compute subset of area light that actually has an influence on the shading point, to - * reduce noise with low spread. */ -ccl_device bool light_spread_clamp_area_light(const float3 P, - const float3 lightNg, - ccl_private float3 *lightP, - ccl_private float3 *axisu, - ccl_private float3 *axisv, - const float tan_spread) -{ - /* Closest point in area light plane and distance to that plane. */ - const float3 closest_P = P - dot(lightNg, P - *lightP) * lightNg; - const float t = len(closest_P - P); - - /* Radius of circle on area light that actually affects the shading point. */ - const float radius = t / tan_spread; - - /* TODO: would be faster to store as normalized vector + length, also in rect_light_sample. */ - float len_u, len_v; - const float3 u = normalize_len(*axisu, &len_u); - const float3 v = normalize_len(*axisv, &len_v); - - /* Local uv coordinates of closest point. */ - const float closest_u = dot(u, closest_P - *lightP); - const float closest_v = dot(v, closest_P - *lightP); - - /* Compute rectangle encompassing the circle that affects the shading point, - * clamped to the bounds of the area light. */ - const float min_u = max(closest_u - radius, -len_u * 0.5f); - const float max_u = min(closest_u + radius, len_u * 0.5f); - const float min_v = max(closest_v - radius, -len_v * 0.5f); - const float max_v = min(closest_v + radius, len_v * 0.5f); - - /* Skip if rectangle is empty. */ - if (min_u >= max_u || min_v >= max_v) { - return false; - } - - /* Compute new area light center position and axes from rectangle in local - * uv coordinates. */ - const float new_center_u = 0.5f * (min_u + max_u); - const float new_center_v = 0.5f * (min_v + max_v); - const float new_len_u = max_u - min_u; - const float new_len_v = max_v - min_v; - - *lightP = *lightP + new_center_u * u + new_center_v * v; - *axisu = u * new_len_u; - *axisv = v * new_len_v; - - return true; -} - -ccl_device float lamp_light_pdf(KernelGlobals kg, const float3 Ng, const float3 I, float t) +ccl_device float lamp_light_pdf(const float3 Ng, const float3 I, float t) { float cos_pi = dot(Ng, I); diff --git a/intern/cycles/kernel/light/distant.h b/intern/cycles/kernel/light/distant.h new file mode 100644 index 00000000000..46ced631601 --- /dev/null +++ b/intern/cycles/kernel/light/distant.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +#include "kernel/geom/geom.h" + +#include "kernel/light/common.h" + +CCL_NAMESPACE_BEGIN + +ccl_device_inline bool distant_light_sample(const ccl_global KernelLight *klight, + const float randu, + const float randv, + ccl_private LightSample *ls) +{ + /* distant light */ + float3 lightD = klight->co; + float3 D = lightD; + float radius = klight->distant.radius; + float invarea = klight->distant.invarea; + + if (radius > 0.0f) { + D = normalize(D + disk_light_sample(D, randu, randv) * radius); + } + + ls->P = D; + ls->Ng = D; + ls->D = -D; + ls->t = FLT_MAX; + + float costheta = dot(lightD, D); + ls->pdf = invarea / (costheta * costheta * costheta); + ls->eval_fac = ls->pdf; + + return true; +} + +ccl_device bool distant_light_sample_from_intersection(KernelGlobals kg, + const float3 ray_D, + const int lamp, + ccl_private LightSample *ccl_restrict ls) +{ + ccl_global const KernelLight *klight = &kernel_data_fetch(lights, lamp); + const int shader = klight->shader_id; + const float radius = klight->distant.radius; + const LightType type = (LightType)klight->type; + + if (type != LIGHT_DISTANT) { + return false; + } + if (!(shader & SHADER_USE_MIS)) { + return false; + } + if (radius == 0.0f) { + return false; + } + + /* a distant light is infinitely far away, but equivalent to a disk + * shaped light exactly 1 unit away from the current shading point. + * + * radius t^2/cos(theta) + * <----------> t = sqrt(1^2 + tan(theta)^2) + * tan(th) area = radius*radius*pi + * <-----> + * \ | (1 + tan(theta)^2)/cos(theta) + * \ | (1 + tan(acos(cos(theta)))^2)/cos(theta) + * t \th| 1 simplifies to + * \-| 1/(cos(theta)^3) + * \| magic! + * P + */ + + float3 lightD = klight->co; + float costheta = dot(-lightD, ray_D); + float cosangle = klight->distant.cosangle; + + /* Workaround to prevent a hang in the classroom scene with AMD HIP drivers 22.10, + * Remove when a compiler fix is available. */ +#ifdef __HIP__ + ls->shader = klight->shader_id; +#endif + + if (costheta < cosangle) + return false; + + ls->type = type; +#ifndef __HIP__ + ls->shader = klight->shader_id; +#endif + ls->object = PRIM_NONE; + ls->prim = PRIM_NONE; + ls->lamp = lamp; + /* todo: missing texture coordinates */ + ls->u = 0.0f; + ls->v = 0.0f; + ls->t = FLT_MAX; + ls->P = -ray_D; + ls->Ng = -ray_D; + ls->D = ray_D; + ls->group = lamp_lightgroup(kg, lamp); + + /* compute pdf */ + float invarea = klight->distant.invarea; + ls->pdf = invarea / (costheta * costheta * costheta); + ls->eval_fac = ls->pdf; + + return true; +} +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/light/distribution.h b/intern/cycles/kernel/light/distribution.h new file mode 100644 index 00000000000..45d6811d521 --- /dev/null +++ b/intern/cycles/kernel/light/distribution.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +#include "kernel/light/light.h" +#include "kernel/light/triangle.h" + +CCL_NAMESPACE_BEGIN + +/* Simple CDF based sampling over all lights in the scene, without taking into + * account shading position or normal. */ + +ccl_device int light_distribution_sample(KernelGlobals kg, ccl_private float *randu) +{ + /* This is basically std::upper_bound as used by PBRT, to find a point light or + * triangle to emit from, proportional to area. a good improvement would be to + * also sample proportional to power, though it's not so well defined with + * arbitrary shaders. */ + int first = 0; + int len = kernel_data.integrator.num_distribution + 1; + float r = *randu; + + do { + int half_len = len >> 1; + int middle = first + half_len; + + if (r < kernel_data_fetch(light_distribution, middle).totarea) { + len = half_len; + } + else { + first = middle + 1; + len = len - half_len - 1; + } + } while (len > 0); + + /* Clamping should not be needed but float rounding errors seem to + * make this fail on rare occasions. */ + int index = clamp(first - 1, 0, kernel_data.integrator.num_distribution - 1); + + /* Rescale to reuse random number. this helps the 2D samples within + * each area light be stratified as well. */ + float distr_min = kernel_data_fetch(light_distribution, index).totarea; + float distr_max = kernel_data_fetch(light_distribution, index + 1).totarea; + *randu = (r - distr_min) / (distr_max - distr_min); + + return index; +} + +template +ccl_device_noinline bool light_distribution_sample(KernelGlobals kg, + float randu, + const float randv, + const float time, + const float3 P, + const int bounce, + const uint32_t path_flag, + ccl_private LightSample *ls) +{ + /* Sample light index from distribution. */ + const int index = light_distribution_sample(kg, &randu); + ccl_global const KernelLightDistribution *kdistribution = &kernel_data_fetch(light_distribution, + index); + const int prim = kdistribution->prim; + + if (prim >= 0) { + /* Mesh light. */ + const int object = kdistribution->mesh_light.object_id; + + /* Exclude synthetic meshes from shadow catcher pass. */ + if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) && + !(kernel_data_fetch(object_flag, object) & SD_OBJECT_SHADOW_CATCHER)) { + return false; + } + + const int shader_flag = kdistribution->mesh_light.shader_flag; + triangle_light_sample(kg, prim, object, randu, randv, time, ls, P); + ls->shader |= shader_flag; + return (ls->pdf > 0.0f); + } + + const int lamp = -prim - 1; + + if (UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) { + return false; + } + + if (!light_sample(kg, lamp, randu, randv, P, path_flag, ls)) { + return false; + } + + ls->pdf_selection = kernel_data.integrator.distribution_pdf_lights; + ls->pdf *= ls->pdf_selection; + + return true; +} + +ccl_device_inline float light_distribution_pdf_lamp(KernelGlobals kg) +{ + return kernel_data.integrator.distribution_pdf_lights; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/light/light.h b/intern/cycles/kernel/light/light.h index 12a6f21b58d..b33aecf8b62 100644 --- a/intern/cycles/kernel/light/light.h +++ b/intern/cycles/kernel/light/light.h @@ -3,31 +3,18 @@ #pragma once -#include "kernel/geom/geom.h" +#include "kernel/light/area.h" #include "kernel/light/background.h" +#include "kernel/light/distant.h" +#include "kernel/light/point.h" +#include "kernel/light/spot.h" +#include "kernel/light/triangle.h" + #include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN -/* Light Sample result */ - -typedef struct LightSample { - float3 P; /* position on light, or direction for distant light */ - float3 Ng; /* normal on light */ - float3 D; /* direction from shading point to light */ - float t; /* distance to light (FLT_MAX for distant light) */ - float u, v; /* parametric coordinate on primitive */ - float pdf; /* light sampling probability density function */ - float eval_fac; /* intensity multiplier */ - int object; /* object id for triangle/curve lights */ - int prim; /* primitive id for triangle/curve lights */ - int shader; /* shader id */ - int lamp; /* lamp id */ - int group; /* lightgroup */ - LightType type; /* type of light */ -} LightSample; - -/* Regular Light */ +/* Sample point on an individual light. */ template ccl_device_inline bool light_sample(KernelGlobals kg, @@ -63,28 +50,15 @@ ccl_device_inline bool light_sample(KernelGlobals kg, ls->Ng = zero_float3(); ls->D = zero_float3(); ls->pdf = 1.0f; + ls->eval_fac = 0.0f; ls->t = FLT_MAX; return true; } if (type == LIGHT_DISTANT) { - /* distant light */ - float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]); - float3 D = lightD; - float radius = klight->distant.radius; - float invarea = klight->distant.invarea; - - if (radius > 0.0f) - D = distant_light_sample(D, radius, randu, randv); - - ls->P = D; - ls->Ng = D; - ls->D = -D; - ls->t = FLT_MAX; - - float costheta = dot(lightD, D); - ls->pdf = invarea / (costheta * costheta * costheta); - ls->eval_fac = ls->pdf; + if (!distant_light_sample(klight, randu, randv, ls)) { + return false; + } } else if (type == LIGHT_BACKGROUND) { /* infinite area light (e.g. light dome or env light) */ @@ -96,139 +70,28 @@ ccl_device_inline bool light_sample(KernelGlobals kg, ls->t = FLT_MAX; ls->eval_fac = 1.0f; } + else if (type == LIGHT_SPOT) { + if (!spot_light_sample(klight, randu, randv, P, ls)) { + return false; + } + } + else if (type == LIGHT_POINT) { + if (!point_light_sample(klight, randu, randv, P, ls)) { + return false; + } + } else { - ls->P = make_float3(klight->co[0], klight->co[1], klight->co[2]); - - if (type == LIGHT_SPOT) { - const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]); - const float radius = klight->spot.radius; - const float3 dir = make_float3( - klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]); - /* disk oriented normal */ - const float3 lightN = normalize(P - center); - ls->P = center; - - if (radius > 0.0f) - /* disk light */ - ls->P += disk_light_sample(lightN, randu, randv) * radius; - - const float invarea = klight->spot.invarea; - ls->pdf = invarea; - - ls->D = normalize_len(ls->P - P, &ls->t); - /* we set the light normal to the outgoing direction to support texturing */ - ls->Ng = -ls->D; - - ls->eval_fac = (0.25f * M_1_PI_F) * invarea; - - /* spot light attenuation */ - ls->eval_fac *= spot_light_attenuation( - dir, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D); - if (!in_volume_segment && ls->eval_fac == 0.0f) { - return false; - } - - float2 uv = map_to_sphere(ls->Ng); - ls->u = uv.x; - ls->v = uv.y; - - ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t); - } - else if (type == LIGHT_POINT) { - float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]); - float radius = klight->spot.radius; - /* disk oriented normal */ - const float3 lightN = normalize(P - center); - ls->P = center; - - if (radius > 0.0f) { - ls->P += disk_light_sample(lightN, randu, randv) * radius; - } - ls->pdf = klight->spot.invarea; - - ls->D = normalize_len(ls->P - P, &ls->t); - /* we set the light normal to the outgoing direction to support texturing */ - ls->Ng = -ls->D; - - ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea; - if (!in_volume_segment && ls->eval_fac == 0.0f) { - return false; - } - - float2 uv = map_to_sphere(ls->Ng); - ls->u = uv.x; - ls->v = uv.y; - ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t); - } - else { - /* area light */ - float3 axisu = make_float3( - klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); - float3 axisv = make_float3( - klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); - float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]); - float invarea = fabsf(klight->area.invarea); - bool is_round = (klight->area.invarea < 0.0f); - - if (!in_volume_segment) { - if (dot(ls->P - P, Ng) > 0.0f) { - return false; - } - } - - float3 inplane; - - if (is_round || in_volume_segment) { - inplane = ellipse_sample(axisu * 0.5f, axisv * 0.5f, randu, randv); - ls->P += inplane; - ls->pdf = invarea; - } - else { - inplane = ls->P; - - float3 sample_axisu = axisu; - float3 sample_axisv = axisv; - - if (!in_volume_segment && klight->area.tan_spread > 0.0f) { - if (!light_spread_clamp_area_light( - P, Ng, &ls->P, &sample_axisu, &sample_axisv, klight->area.tan_spread)) { - return false; - } - } - - ls->pdf = rect_light_sample(P, &ls->P, sample_axisu, sample_axisv, randu, randv, true); - inplane = ls->P - inplane; - } - - const float light_u = dot(inplane, axisu) * (1.0f / dot(axisu, axisu)); - const float light_v = dot(inplane, axisv) * (1.0f / dot(axisv, axisv)); - - /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */ - ls->u = light_v + 0.5f; - ls->v = -light_u - light_v; - - ls->Ng = Ng; - ls->D = normalize_len(ls->P - P, &ls->t); - - ls->eval_fac = 0.25f * invarea; - - if (klight->area.tan_spread > 0.0f) { - /* Area Light spread angle attenuation */ - ls->eval_fac *= light_spread_attenuation( - ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread); - } - - if (is_round) { - ls->pdf *= lamp_light_pdf(kg, Ng, -ls->D, ls->t); - } + /* area light */ + if (!area_light_sample(klight, randu, randv, P, ls)) { + return false; } } - ls->pdf *= kernel_data.integrator.pdf_lights; - return in_volume_segment || (ls->pdf > 0.0f); } +/* Intersect ray with individual light. */ + ccl_device bool lights_intersect(KernelGlobals kg, IntegratorState state, ccl_private const Ray *ccl_restrict ray, @@ -238,7 +101,7 @@ ccl_device bool lights_intersect(KernelGlobals kg, const int last_type, const uint32_t path_flag) { - for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { + for (int lamp = 0; lamp < kernel_data.integrator.num_lights; lamp++) { const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp); if (path_flag & PATH_RAY_CAMERA) { @@ -271,76 +134,17 @@ ccl_device bool lights_intersect(KernelGlobals kg, float t = 0.0f, u = 0.0f, v = 0.0f; if (type == LIGHT_SPOT) { - /* Spot/Disk light. */ - const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); - const float radius = klight->spot.radius; - if (radius == 0.0f) { - continue; - } - /* disk oriented normal */ - const float3 lightN = normalize(ray->P - lightP); - /* One sided. */ - if (dot(ray->D, lightN) >= 0.0f) { - continue; - } - - float3 P; - if (!ray_disk_intersect( - ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, &t)) { + if (!spot_light_intersect(klight, ray, &t)) { continue; } } else if (type == LIGHT_POINT) { - /* Sphere light (aka, aligned disk light). */ - const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); - const float radius = klight->spot.radius; - if (radius == 0.0f) { - continue; - } - - /* disk oriented normal */ - const float3 lightN = normalize(ray->P - lightP); - float3 P; - if (!ray_disk_intersect( - ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, &t)) { + if (!point_light_intersect(klight, ray, &t)) { continue; } } else if (type == LIGHT_AREA) { - /* Area light. */ - const float invarea = fabsf(klight->area.invarea); - const bool is_round = (klight->area.invarea < 0.0f); - if (invarea == 0.0f) { - continue; - } - - const float3 axisu = make_float3( - klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); - const float3 axisv = make_float3( - klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); - const float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]); - - /* One sided. */ - if (dot(ray->D, Ng) >= 0.0f) { - continue; - } - - const float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]); - - float3 P; - if (!ray_quad_intersect(ray->P, - ray->D, - ray->tmin, - ray->tmax, - light_P, - axisu, - axisv, - Ng, - &P, - &t, - &u, - &v, - is_round)) { + if (!area_light_intersect(klight, ray, &t, &u, &v)) { continue; } } @@ -362,78 +166,7 @@ ccl_device bool lights_intersect(KernelGlobals kg, return isect->prim != PRIM_NONE; } -ccl_device bool light_sample_from_distant_ray(KernelGlobals kg, - const float3 ray_D, - const int lamp, - ccl_private LightSample *ccl_restrict ls) -{ - ccl_global const KernelLight *klight = &kernel_data_fetch(lights, lamp); - const int shader = klight->shader_id; - const float radius = klight->distant.radius; - const LightType type = (LightType)klight->type; - - if (type != LIGHT_DISTANT) { - return false; - } - if (!(shader & SHADER_USE_MIS)) { - return false; - } - if (radius == 0.0f) { - return false; - } - - /* a distant light is infinitely far away, but equivalent to a disk - * shaped light exactly 1 unit away from the current shading point. - * - * radius t^2/cos(theta) - * <----------> t = sqrt(1^2 + tan(theta)^2) - * tan(th) area = radius*radius*pi - * <-----> - * \ | (1 + tan(theta)^2)/cos(theta) - * \ | (1 + tan(acos(cos(theta)))^2)/cos(theta) - * t \th| 1 simplifies to - * \-| 1/(cos(theta)^3) - * \| magic! - * P - */ - - float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]); - float costheta = dot(-lightD, ray_D); - float cosangle = klight->distant.cosangle; - - /* Workaround to prevent a hang in the classroom scene with AMD HIP drivers 22.10, - * Remove when a compiler fix is available. */ -#ifdef __HIP__ - ls->shader = klight->shader_id; -#endif - - if (costheta < cosangle) - return false; - - ls->type = type; -#ifndef __HIP__ - ls->shader = klight->shader_id; -#endif - ls->object = PRIM_NONE; - ls->prim = PRIM_NONE; - ls->lamp = lamp; - /* todo: missing texture coordinates */ - ls->u = 0.0f; - ls->v = 0.0f; - ls->t = FLT_MAX; - ls->P = -ray_D; - ls->Ng = -ray_D; - ls->D = ray_D; - ls->group = lamp_lightgroup(kg, lamp); - - /* compute pdf */ - float invarea = klight->distant.invarea; - ls->pdf = invarea / (costheta * costheta * costheta); - ls->eval_fac = ls->pdf; - ls->pdf *= kernel_data.integrator.pdf_lights; - - return true; -} +/* Setup light sample from intersection. */ ccl_device bool light_sample_from_intersection(KernelGlobals kg, ccl_private const Intersection *ccl_restrict isect, @@ -456,102 +189,18 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg, ls->group = lamp_lightgroup(kg, lamp); if (type == LIGHT_SPOT) { - const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]); - const float3 dir = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]); - /* the normal of the oriented disk */ - const float3 lightN = normalize(ray_P - center); - /* We set the light normal to the outgoing direction to support texturing. */ - ls->Ng = -ls->D; - - float invarea = klight->spot.invarea; - ls->eval_fac = (0.25f * M_1_PI_F) * invarea; - ls->pdf = invarea; - - /* spot light attenuation */ - ls->eval_fac *= spot_light_attenuation( - dir, klight->spot.spot_angle, klight->spot.spot_smooth, -ls->D); - - if (ls->eval_fac == 0.0f) { + if (!spot_light_sample_from_intersection(klight, isect, ray_P, ray_D, ls)) { return false; } - - float2 uv = map_to_sphere(ls->Ng); - ls->u = uv.x; - ls->v = uv.y; - - /* compute pdf */ - if (ls->t != FLT_MAX) - ls->pdf *= lamp_light_pdf(kg, lightN, -ls->D, ls->t); - else - ls->pdf = 0.f; } else if (type == LIGHT_POINT) { - const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]); - const float3 lighN = normalize(ray_P - center); - - /* We set the light normal to the outgoing direction to support texturing. */ - ls->Ng = -ls->D; - - float invarea = klight->spot.invarea; - ls->eval_fac = (0.25f * M_1_PI_F) * invarea; - ls->pdf = invarea; - - if (ls->eval_fac == 0.0f) { + if (!point_light_sample_from_intersection(klight, isect, ray_P, ray_D, ls)) { return false; } - - float2 uv = map_to_sphere(ls->Ng); - ls->u = uv.x; - ls->v = uv.y; - - /* compute pdf */ - if (ls->t != FLT_MAX) - ls->pdf *= lamp_light_pdf(kg, lighN, -ls->D, ls->t); - else - ls->pdf = 0.f; } else if (type == LIGHT_AREA) { - /* area light */ - float invarea = fabsf(klight->area.invarea); - - float3 axisu = make_float3( - klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); - float3 axisv = make_float3( - klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); - float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]); - float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]); - - ls->u = isect->u; - ls->v = isect->v; - ls->D = ray_D; - ls->Ng = Ng; - - const bool is_round = (klight->area.invarea < 0.0f); - if (is_round) { - ls->pdf = invarea * lamp_light_pdf(kg, Ng, -ray_D, ls->t); - } - else { - float3 sample_axisu = axisu; - float3 sample_axisv = axisv; - - if (klight->area.tan_spread > 0.0f) { - if (!light_spread_clamp_area_light( - ray_P, Ng, &light_P, &sample_axisu, &sample_axisv, klight->area.tan_spread)) { - return false; - } - } - - ls->pdf = rect_light_sample(ray_P, &light_P, sample_axisu, sample_axisv, 0, 0, false); - } - ls->eval_fac = 0.25f * invarea; - - if (klight->area.tan_spread > 0.0f) { - /* Area Light spread angle attenuation */ - ls->eval_fac *= light_spread_attenuation( - ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread); - if (ls->eval_fac == 0.0f) { - return false; - } + if (!area_light_sample_from_intersection(klight, isect, ray_P, ray_D, ls)) { + return false; } } else { @@ -559,411 +208,33 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg, return false; } - ls->pdf *= kernel_data.integrator.pdf_lights; - return true; } -/* Triangle Light */ +/* Update light sample for changed new position, for MNEE. */ -/* returns true if the triangle is has motion blur or an instancing transform applied */ -ccl_device_inline bool triangle_world_space_vertices( - KernelGlobals kg, int object, int prim, float time, float3 V[3]) -{ - bool has_motion = false; - const int object_flag = kernel_data_fetch(object_flag, object); - - if (object_flag & SD_OBJECT_HAS_VERTEX_MOTION && time >= 0.0f) { - motion_triangle_vertices(kg, object, prim, time, V); - has_motion = true; - } - else { - triangle_vertices(kg, prim, V); - } - - if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { -#ifdef __OBJECT_MOTION__ - float object_time = (time >= 0.0f) ? time : 0.5f; - Transform tfm = object_fetch_transform_motion_test(kg, object, object_time, NULL); -#else - Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); -#endif - V[0] = transform_point(&tfm, V[0]); - V[1] = transform_point(&tfm, V[1]); - V[2] = transform_point(&tfm, V[2]); - has_motion = true; - } - return has_motion; -} - -ccl_device_inline float triangle_light_pdf_area(KernelGlobals kg, - const float3 Ng, - const float3 I, - float t) -{ - float pdf = kernel_data.integrator.pdf_triangles; - float cos_pi = fabsf(dot(Ng, I)); - - if (cos_pi == 0.0f) - return 0.0f; - - return t * t * pdf / cos_pi; -} - -ccl_device_forceinline float triangle_light_pdf(KernelGlobals kg, - ccl_private const ShaderData *sd, - float t) -{ - /* A naive heuristic to decide between costly solid angle sampling - * and simple area sampling, comparing the distance to the triangle plane - * to the length of the edges of the triangle. */ - - float3 V[3]; - bool has_motion = triangle_world_space_vertices(kg, sd->object, sd->prim, sd->time, V); - - const float3 e0 = V[1] - V[0]; - const float3 e1 = V[2] - V[0]; - const float3 e2 = V[2] - V[1]; - const float longest_edge_squared = max(len_squared(e0), max(len_squared(e1), len_squared(e2))); - const float3 N = cross(e0, e1); - const float distance_to_plane = fabsf(dot(N, sd->I * t)) / dot(N, N); - - if (longest_edge_squared > distance_to_plane * distance_to_plane) { - /* sd contains the point on the light source - * calculate Px, the point that we're shading */ - const float3 Px = sd->P + sd->I * t; - const float3 v0_p = V[0] - Px; - const float3 v1_p = V[1] - Px; - const float3 v2_p = V[2] - Px; - - const float3 u01 = safe_normalize(cross(v0_p, v1_p)); - const float3 u02 = safe_normalize(cross(v0_p, v2_p)); - const float3 u12 = safe_normalize(cross(v1_p, v2_p)); - - const float alpha = fast_acosf(dot(u02, u01)); - const float beta = fast_acosf(-dot(u01, u12)); - const float gamma = fast_acosf(dot(u02, u12)); - const float solid_angle = alpha + beta + gamma - M_PI_F; - - /* pdf_triangles is calculated over triangle area, but we're not sampling over its area */ - if (UNLIKELY(solid_angle == 0.0f)) { - return 0.0f; - } - else { - float area = 1.0f; - if (has_motion) { - /* get the center frame vertices, this is what the PDF was calculated from */ - triangle_world_space_vertices(kg, sd->object, sd->prim, -1.0f, V); - area = triangle_area(V[0], V[1], V[2]); - } - else { - area = 0.5f * len(N); - } - const float pdf = area * kernel_data.integrator.pdf_triangles; - return pdf / solid_angle; - } - } - else { - float pdf = triangle_light_pdf_area(kg, sd->Ng, sd->I, t); - if (has_motion) { - const float area = 0.5f * len(N); - if (UNLIKELY(area == 0.0f)) { - return 0.0f; - } - /* scale the PDF. - * area = the area the sample was taken from - * area_pre = the are from which pdf_triangles was calculated from */ - triangle_world_space_vertices(kg, sd->object, sd->prim, -1.0f, V); - const float area_pre = triangle_area(V[0], V[1], V[2]); - pdf = pdf * area_pre / area; - } - return pdf; - } -} - -template -ccl_device_forceinline void triangle_light_sample(KernelGlobals kg, - int prim, - int object, - float randu, - float randv, - float time, +ccl_device_forceinline void light_update_position(KernelGlobals kg, ccl_private LightSample *ls, const float3 P) { - /* A naive heuristic to decide between costly solid angle sampling - * and simple area sampling, comparing the distance to the triangle plane - * to the length of the edges of the triangle. */ + const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp); - float3 V[3]; - bool has_motion = triangle_world_space_vertices(kg, object, prim, time, V); - - const float3 e0 = V[1] - V[0]; - const float3 e1 = V[2] - V[0]; - const float3 e2 = V[2] - V[1]; - const float longest_edge_squared = max(len_squared(e0), max(len_squared(e1), len_squared(e2))); - const float3 N0 = cross(e0, e1); - float Nl = 0.0f; - ls->Ng = safe_normalize_len(N0, &Nl); - float area = 0.5f * Nl; - - /* flip normal if necessary */ - const int object_flag = kernel_data_fetch(object_flag, object); - if (object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { - ls->Ng = -ls->Ng; + if (ls->type == LIGHT_POINT) { + point_light_update_position(klight, ls, P); } - ls->eval_fac = 1.0f; - ls->shader = kernel_data_fetch(tri_shader, prim); - ls->object = object; - ls->prim = prim; - ls->lamp = LAMP_NONE; - ls->shader |= SHADER_USE_MIS; - ls->type = LIGHT_TRIANGLE; - ls->group = object_lightgroup(kg, object); - - float distance_to_plane = fabsf(dot(N0, V[0] - P) / dot(N0, N0)); - - if (!in_volume_segment && (longest_edge_squared > distance_to_plane * distance_to_plane)) { - /* see James Arvo, "Stratified Sampling of Spherical Triangles" - * http://www.graphics.cornell.edu/pubs/1995/Arv95c.pdf */ - - /* project the triangle to the unit sphere - * and calculate its edges and angles */ - const float3 v0_p = V[0] - P; - const float3 v1_p = V[1] - P; - const float3 v2_p = V[2] - P; - - const float3 u01 = safe_normalize(cross(v0_p, v1_p)); - const float3 u02 = safe_normalize(cross(v0_p, v2_p)); - const float3 u12 = safe_normalize(cross(v1_p, v2_p)); - - const float3 A = safe_normalize(v0_p); - const float3 B = safe_normalize(v1_p); - const float3 C = safe_normalize(v2_p); - - const float cos_alpha = dot(u02, u01); - const float cos_beta = -dot(u01, u12); - const float cos_gamma = dot(u02, u12); - - /* calculate dihedral angles */ - const float alpha = fast_acosf(cos_alpha); - const float beta = fast_acosf(cos_beta); - const float gamma = fast_acosf(cos_gamma); - /* the area of the unit spherical triangle = solid angle */ - const float solid_angle = alpha + beta + gamma - M_PI_F; - - /* precompute a few things - * these could be re-used to take several samples - * as they are independent of randu/randv */ - const float cos_c = dot(A, B); - const float sin_alpha = fast_sinf(alpha); - const float product = sin_alpha * cos_c; - - /* Select a random sub-area of the spherical triangle - * and calculate the third vertex C_ of that new triangle */ - const float phi = randu * solid_angle - alpha; - float s, t; - fast_sincosf(phi, &s, &t); - const float u = t - cos_alpha; - const float v = s + product; - - const float3 U = safe_normalize(C - dot(C, A) * A); - - float q = 1.0f; - const float det = ((v * s + u * t) * sin_alpha); - if (det != 0.0f) { - q = ((v * t - u * s) * cos_alpha - v) / det; - } - const float temp = max(1.0f - q * q, 0.0f); - - const float3 C_ = safe_normalize(q * A + sqrtf(temp) * U); - - /* Finally, select a random point along the edge of the new triangle - * That point on the spherical triangle is the sampled ray direction */ - const float z = 1.0f - randv * (1.0f - dot(C_, B)); - ls->D = z * B + safe_sqrtf(1.0f - z * z) * safe_normalize(C_ - dot(C_, B) * B); - - /* calculate intersection with the planar triangle */ - if (!ray_triangle_intersect( - P, ls->D, 0.0f, FLT_MAX, V[0], V[1], V[2], &ls->u, &ls->v, &ls->t)) { - ls->pdf = 0.0f; - return; - } - - ls->P = P + ls->D * ls->t; - - /* pdf_triangles is calculated over triangle area, but we're sampling over solid angle */ - if (UNLIKELY(solid_angle == 0.0f)) { - ls->pdf = 0.0f; - return; - } - else { - if (has_motion) { - /* get the center frame vertices, this is what the PDF was calculated from */ - triangle_world_space_vertices(kg, object, prim, -1.0f, V); - area = triangle_area(V[0], V[1], V[2]); - } - const float pdf = area * kernel_data.integrator.pdf_triangles; - ls->pdf = pdf / solid_angle; - } + else if (ls->type == LIGHT_SPOT) { + spot_light_update_position(klight, ls, P); } - else { - /* compute random point in triangle. From Eric Heitz's "A Low-Distortion Map Between Triangle - * and Square" */ - float u = randu; - float v = randv; - if (v > u) { - u *= 0.5f; - v -= u; - } - else { - v *= 0.5f; - u -= v; - } - - const float t = 1.0f - u - v; - ls->P = u * V[0] + v * V[1] + t * V[2]; - /* compute incoming direction, distance and pdf */ - ls->D = normalize_len(ls->P - P, &ls->t); - ls->pdf = triangle_light_pdf_area(kg, ls->Ng, -ls->D, ls->t); - if (has_motion && area != 0.0f) { - /* scale the PDF. - * area = the area the sample was taken from - * area_pre = the are from which pdf_triangles was calculated from */ - triangle_world_space_vertices(kg, object, prim, -1.0f, V); - const float area_pre = triangle_area(V[0], V[1], V[2]); - ls->pdf = ls->pdf * area_pre / area; - } - ls->u = u; - ls->v = v; + else if (ls->type == LIGHT_AREA) { + area_light_update_position(klight, ls, P); } } -/* Light Distribution */ - -ccl_device int light_distribution_sample(KernelGlobals kg, ccl_private float *randu) -{ - /* This is basically std::upper_bound as used by PBRT, to find a point light or - * triangle to emit from, proportional to area. a good improvement would be to - * also sample proportional to power, though it's not so well defined with - * arbitrary shaders. */ - int first = 0; - int len = kernel_data.integrator.num_distribution + 1; - float r = *randu; - - do { - int half_len = len >> 1; - int middle = first + half_len; - - if (r < kernel_data_fetch(light_distribution, middle).totarea) { - len = half_len; - } - else { - first = middle + 1; - len = len - half_len - 1; - } - } while (len > 0); - - /* Clamping should not be needed but float rounding errors seem to - * make this fail on rare occasions. */ - int index = clamp(first - 1, 0, kernel_data.integrator.num_distribution - 1); - - /* Rescale to reuse random number. this helps the 2D samples within - * each area light be stratified as well. */ - float distr_min = kernel_data_fetch(light_distribution, index).totarea; - float distr_max = kernel_data_fetch(light_distribution, index + 1).totarea; - *randu = (r - distr_min) / (distr_max - distr_min); - - return index; -} - -/* Generic Light */ +/* Light info. */ ccl_device_inline bool light_select_reached_max_bounces(KernelGlobals kg, int index, int bounce) { return (bounce > kernel_data_fetch(lights, index).max_bounces); } -template -ccl_device_noinline bool light_distribution_sample(KernelGlobals kg, - float randu, - const float randv, - const float time, - const float3 P, - const int bounce, - const uint32_t path_flag, - ccl_private LightSample *ls) -{ - /* Sample light index from distribution. */ - const int index = light_distribution_sample(kg, &randu); - ccl_global const KernelLightDistribution *kdistribution = &kernel_data_fetch(light_distribution, - index); - const int prim = kdistribution->prim; - - if (prim >= 0) { - /* Mesh light. */ - const int object = kdistribution->mesh_light.object_id; - - /* Exclude synthetic meshes from shadow catcher pass. */ - if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) && - !(kernel_data_fetch(object_flag, object) & SD_OBJECT_SHADOW_CATCHER)) { - return false; - } - - const int shader_flag = kdistribution->mesh_light.shader_flag; - triangle_light_sample(kg, prim, object, randu, randv, time, ls, P); - ls->shader |= shader_flag; - return (ls->pdf > 0.0f); - } - - const int lamp = -prim - 1; - - if (UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) { - return false; - } - - return light_sample(kg, lamp, randu, randv, P, path_flag, ls); -} - -ccl_device_inline bool light_distribution_sample_from_volume_segment(KernelGlobals kg, - float randu, - const float randv, - const float time, - const float3 P, - const int bounce, - const uint32_t path_flag, - ccl_private LightSample *ls) -{ - return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); -} - -ccl_device_inline bool light_distribution_sample_from_position(KernelGlobals kg, - float randu, - const float randv, - const float time, - const float3 P, - const int bounce, - const uint32_t path_flag, - ccl_private LightSample *ls) -{ - return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); -} - -ccl_device_inline bool light_distribution_sample_new_position(KernelGlobals kg, - const float randu, - const float randv, - const float time, - const float3 P, - ccl_private LightSample *ls) -{ - /* Sample a new position on the same light, for volume sampling. */ - if (ls->type == LIGHT_TRIANGLE) { - triangle_light_sample(kg, ls->prim, ls->object, randu, randv, time, ls, P); - return (ls->pdf > 0.0f); - } - else { - return light_sample(kg, ls->lamp, randu, randv, P, 0, ls); - } -} - CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/light/point.h b/intern/cycles/kernel/light/point.h new file mode 100644 index 00000000000..90a1b1ce502 --- /dev/null +++ b/intern/cycles/kernel/light/point.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +#include "kernel/light/common.h" + +CCL_NAMESPACE_BEGIN + +template +ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, + const float randu, + const float randv, + const float3 P, + ccl_private LightSample *ls) +{ + float3 center = klight->co; + float radius = klight->spot.radius; + /* disk oriented normal */ + const float3 lightN = normalize(P - center); + ls->P = center; + + if (radius > 0.0f) { + ls->P += disk_light_sample(lightN, randu, randv) * radius; + } + ls->pdf = klight->spot.invarea; + + ls->D = normalize_len(ls->P - P, &ls->t); + /* we set the light normal to the outgoing direction to support texturing */ + ls->Ng = -ls->D; + + ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea; + if (!in_volume_segment && ls->eval_fac == 0.0f) { + return false; + } + + float2 uv = map_to_sphere(ls->Ng); + ls->u = uv.x; + ls->v = uv.y; + ls->pdf *= lamp_light_pdf(lightN, -ls->D, ls->t); + return true; +} + +ccl_device_forceinline void point_light_update_position(const ccl_global KernelLight *klight, + ccl_private LightSample *ls, + const float3 P) +{ + ls->D = normalize_len(ls->P - P, &ls->t); + ls->Ng = -ls->D; + + float2 uv = map_to_sphere(ls->Ng); + ls->u = uv.x; + ls->v = uv.y; + + float invarea = klight->spot.invarea; + ls->eval_fac = (0.25f * M_1_PI_F) * invarea; + ls->pdf = invarea; +} + +ccl_device_inline bool point_light_intersect(const ccl_global KernelLight *klight, + const ccl_private Ray *ccl_restrict ray, + ccl_private float *t) +{ + /* Sphere light (aka, aligned disk light). */ + const float3 lightP = klight->co; + const float radius = klight->spot.radius; + if (radius == 0.0f) { + return false; + } + + /* disk oriented normal */ + const float3 lightN = normalize(ray->P - lightP); + float3 P; + return ray_disk_intersect(ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, t); +} + +ccl_device_inline bool point_light_sample_from_intersection( + const ccl_global KernelLight *klight, + ccl_private const Intersection *ccl_restrict isect, + const float3 ray_P, + const float3 ray_D, + ccl_private LightSample *ccl_restrict ls) +{ + const float3 lighN = normalize(ray_P - klight->co); + + /* We set the light normal to the outgoing direction to support texturing. */ + ls->Ng = -ls->D; + + float invarea = klight->spot.invarea; + ls->eval_fac = (0.25f * M_1_PI_F) * invarea; + ls->pdf = invarea; + + if (ls->eval_fac == 0.0f) { + return false; + } + + float2 uv = map_to_sphere(ls->Ng); + ls->u = uv.x; + ls->v = uv.y; + + /* compute pdf */ + if (ls->t != FLT_MAX) { + ls->pdf *= lamp_light_pdf(lighN, -ls->D, ls->t); + } + else { + ls->pdf = 0.f; + } + + return true; +} +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h index e0d4f221bef..2dfd26bc680 100644 --- a/intern/cycles/kernel/light/sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -6,6 +6,7 @@ #include "kernel/integrator/path_state.h" #include "kernel/integrator/surface_shader.h" +#include "kernel/light/distribution.h" #include "kernel/light/light.h" #include "kernel/sample/mapping.h" @@ -277,6 +278,8 @@ ccl_device_inline void light_sample_to_volume_shadow_ray( shadow_ray_setup(sd, ls, P, ray, false); } +/* Multiple importance sampling weights. */ + ccl_device_inline float light_sample_mis_weight_forward(KernelGlobals kg, const float forward_pdf, const float nee_pdf) @@ -309,4 +312,138 @@ ccl_device_inline float light_sample_mis_weight_nee(KernelGlobals kg, return power_heuristic(nee_pdf, forward_pdf); } +/* Next event estimation sampling. + * + * Sample a position on a light in the scene, from a position on a surface or + * from a volume segment. */ + +ccl_device_inline bool light_sample_from_volume_segment(KernelGlobals kg, + float randu, + const float randv, + const float time, + const float3 P, + const float3 D, + const float t, + const int bounce, + const uint32_t path_flag, + ccl_private LightSample *ls) +{ + return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); +} + +ccl_device bool light_sample_from_position(KernelGlobals kg, + ccl_private const RNGState *rng_state, + const float randu, + const float randv, + const float time, + const float3 P, + const float3 N, + const int shader_flags, + const int bounce, + const uint32_t path_flag, + ccl_private LightSample *ls) +{ + return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); +} + +ccl_device_inline bool light_sample_new_position(KernelGlobals kg, + const float randu, + const float randv, + const float time, + const float3 P, + ccl_private LightSample *ls) +{ + /* Sample a new position on the same light, for volume sampling. */ + if (ls->type == LIGHT_TRIANGLE) { + if (!triangle_light_sample(kg, ls->prim, ls->object, randu, randv, time, ls, P)) { + return false; + } + return true; + } + else { + if (!light_sample(kg, ls->lamp, randu, randv, P, 0, ls)) { + return false; + } + ls->pdf *= ls->pdf_selection; + return true; + } +} + +ccl_device_forceinline void light_sample_update_position(KernelGlobals kg, + ccl_private LightSample *ls, + const float3 P) +{ + /* Update light sample for new shading point position, while keeping + * position on the light fixed. */ + + /* NOTE : preserve pdf in area measure. */ + light_update_position(kg, ls, P); + + /* Re-apply already computed selection pdf. */ + ls->pdf *= ls->pdf_selection; +} + +/* Forward sampling. + * + * Multiple importance sampling weights for hitting surface, light or background + * through indirect light ray. + * + * The BSDF or phase pdf from the previous bounce was stored in mis_ray_pdf and + * is used for balancing with the light sampling pdf. */ + +ccl_device_inline float light_sample_mis_weight_forward_surface(KernelGlobals kg, + IntegratorState state, + const uint32_t path_flag, + const ccl_private ShaderData *sd) +{ + const float bsdf_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); + const float t = sd->ray_length; + float pdf = triangle_light_pdf(kg, sd, t); + + /* Light selection pdf. */ + /* Handled in triangle_light_pdf for effeciency. */ + + return light_sample_mis_weight_forward(kg, bsdf_pdf, pdf); +} + +ccl_device_inline float light_sample_mis_weight_forward_lamp(KernelGlobals kg, + IntegratorState state, + const uint32_t path_flag, + const ccl_private LightSample *ls, + const float3 P) +{ + const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); + float pdf = ls->pdf; + + /* Light selection pdf. */ + pdf *= light_distribution_pdf_lamp(kg); + + return light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf); +} + +ccl_device_inline float light_sample_mis_weight_forward_distant(KernelGlobals kg, + IntegratorState state, + const uint32_t path_flag, + const ccl_private LightSample *ls) +{ + const float3 ray_P = INTEGRATOR_STATE(state, ray, P); + return light_sample_mis_weight_forward_lamp(kg, state, path_flag, ls, ray_P); +} + +ccl_device_inline float light_sample_mis_weight_forward_background(KernelGlobals kg, + IntegratorState state, + const uint32_t path_flag) +{ + const float3 ray_P = INTEGRATOR_STATE(state, ray, P); + const float3 ray_D = INTEGRATOR_STATE(state, ray, D); + const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); + + float pdf = background_light_pdf(kg, ray_P, ray_D); + + /* Light selection pdf. */ + pdf *= light_distribution_pdf_lamp(kg); + + return light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/light/spot.h b/intern/cycles/kernel/light/spot.h new file mode 100644 index 00000000000..9bccfd489e8 --- /dev/null +++ b/intern/cycles/kernel/light/spot.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +#include "kernel/light/common.h" + +CCL_NAMESPACE_BEGIN + +ccl_device float spot_light_attenuation(float3 dir, + float cos_half_spot_angle, + float spot_smooth, + float3 N) +{ + float attenuation = dot(dir, N); + + if (attenuation <= cos_half_spot_angle) { + attenuation = 0.0f; + } + else { + float t = attenuation - cos_half_spot_angle; + + if (t < spot_smooth && spot_smooth != 0.0f) + attenuation *= smoothstepf(t / spot_smooth); + } + + return attenuation; +} + +template +ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight, + const float randu, + const float randv, + const float3 P, + ccl_private LightSample *ls) +{ + ls->P = klight->co; + + const float3 center = klight->co; + const float radius = klight->spot.radius; + /* disk oriented normal */ + const float3 lightN = normalize(P - center); + ls->P = center; + + if (radius > 0.0f) { + /* disk light */ + ls->P += disk_light_sample(lightN, randu, randv) * radius; + } + + const float invarea = klight->spot.invarea; + ls->pdf = invarea; + + ls->D = normalize_len(ls->P - P, &ls->t); + /* we set the light normal to the outgoing direction to support texturing */ + ls->Ng = -ls->D; + + ls->eval_fac = (0.25f * M_1_PI_F) * invarea; + + /* spot light attenuation */ + ls->eval_fac *= spot_light_attenuation( + klight->spot.dir, klight->spot.cos_half_spot_angle, klight->spot.spot_smooth, -ls->D); + if (!in_volume_segment && ls->eval_fac == 0.0f) { + return false; + } + + float2 uv = map_to_sphere(ls->Ng); + ls->u = uv.x; + ls->v = uv.y; + + ls->pdf *= lamp_light_pdf(lightN, -ls->D, ls->t); + return true; +} + +ccl_device_forceinline void spot_light_update_position(const ccl_global KernelLight *klight, + ccl_private LightSample *ls, + const float3 P) +{ + ls->D = normalize_len(ls->P - P, &ls->t); + ls->Ng = -ls->D; + + float2 uv = map_to_sphere(ls->Ng); + ls->u = uv.x; + ls->v = uv.y; + + float invarea = klight->spot.invarea; + ls->eval_fac = (0.25f * M_1_PI_F) * invarea; + ls->pdf = invarea; + + /* spot light attenuation */ + ls->eval_fac *= spot_light_attenuation( + klight->spot.dir, klight->spot.cos_half_spot_angle, klight->spot.spot_smooth, ls->Ng); +} + +ccl_device_inline bool spot_light_intersect(const ccl_global KernelLight *klight, + const ccl_private Ray *ccl_restrict ray, + ccl_private float *t) +{ + /* Spot/Disk light. */ + const float3 lightP = klight->co; + const float radius = klight->spot.radius; + if (radius == 0.0f) { + return false; + } + /* disk oriented normal */ + const float3 lightN = normalize(ray->P - lightP); + /* One sided. */ + if (dot(ray->D, lightN) >= 0.0f) { + return false; + } + + float3 P; + return ray_disk_intersect(ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, t); +} + +ccl_device_inline bool spot_light_sample_from_intersection( + const ccl_global KernelLight *klight, + ccl_private const Intersection *ccl_restrict isect, + const float3 ray_P, + const float3 ray_D, + ccl_private LightSample *ccl_restrict ls) +{ + /* the normal of the oriented disk */ + const float3 lightN = normalize(ray_P - klight->co); + /* We set the light normal to the outgoing direction to support texturing. */ + ls->Ng = -ls->D; + + float invarea = klight->spot.invarea; + ls->eval_fac = (0.25f * M_1_PI_F) * invarea; + ls->pdf = invarea; + + /* spot light attenuation */ + ls->eval_fac *= spot_light_attenuation( + klight->spot.dir, klight->spot.cos_half_spot_angle, klight->spot.spot_smooth, -ls->D); + + if (ls->eval_fac == 0.0f) { + return false; + } + + float2 uv = map_to_sphere(ls->Ng); + ls->u = uv.x; + ls->v = uv.y; + + /* compute pdf */ + if (ls->t != FLT_MAX) { + ls->pdf *= lamp_light_pdf(lightN, -ls->D, ls->t); + } + else { + ls->pdf = 0.f; + } + + return true; +} +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/light/triangle.h b/intern/cycles/kernel/light/triangle.h new file mode 100644 index 00000000000..1ea5153105e --- /dev/null +++ b/intern/cycles/kernel/light/triangle.h @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +#include "kernel/geom/geom.h" + +CCL_NAMESPACE_BEGIN + +/* returns true if the triangle is has motion blur or an instancing transform applied */ +ccl_device_inline bool triangle_world_space_vertices( + KernelGlobals kg, int object, int prim, float time, float3 V[3]) +{ + bool has_motion = false; + const int object_flag = kernel_data_fetch(object_flag, object); + + if (object_flag & SD_OBJECT_HAS_VERTEX_MOTION && time >= 0.0f) { + motion_triangle_vertices(kg, object, prim, time, V); + has_motion = true; + } + else { + triangle_vertices(kg, prim, V); + } + + if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { +#ifdef __OBJECT_MOTION__ + float object_time = (time >= 0.0f) ? time : 0.5f; + Transform tfm = object_fetch_transform_motion_test(kg, object, object_time, NULL); +#else + Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); +#endif + V[0] = transform_point(&tfm, V[0]); + V[1] = transform_point(&tfm, V[1]); + V[2] = transform_point(&tfm, V[2]); + has_motion = true; + } + return has_motion; +} + +ccl_device_inline float triangle_light_pdf_area_sampling(const float3 Ng, const float3 I, float t) +{ + float cos_pi = fabsf(dot(Ng, I)); + + if (cos_pi == 0.0f) + return 0.0f; + + return t * t / cos_pi; +} + +ccl_device_forceinline float triangle_light_pdf(KernelGlobals kg, + ccl_private const ShaderData *sd, + float t) +{ + /* A naive heuristic to decide between costly solid angle sampling + * and simple area sampling, comparing the distance to the triangle plane + * to the length of the edges of the triangle. */ + + float3 V[3]; + bool has_motion = triangle_world_space_vertices(kg, sd->object, sd->prim, sd->time, V); + + const float3 e0 = V[1] - V[0]; + const float3 e1 = V[2] - V[0]; + const float3 e2 = V[2] - V[1]; + const float longest_edge_squared = max(len_squared(e0), max(len_squared(e1), len_squared(e2))); + const float3 N = cross(e0, e1); + const float distance_to_plane = fabsf(dot(N, sd->I * t)) / dot(N, N); + const float area = 0.5f * len(N); + + float pdf; + + if (longest_edge_squared > distance_to_plane * distance_to_plane) { + /* sd contains the point on the light source + * calculate Px, the point that we're shading */ + const float3 Px = sd->P + sd->I * t; + const float3 v0_p = V[0] - Px; + const float3 v1_p = V[1] - Px; + const float3 v2_p = V[2] - Px; + + const float3 u01 = safe_normalize(cross(v0_p, v1_p)); + const float3 u02 = safe_normalize(cross(v0_p, v2_p)); + const float3 u12 = safe_normalize(cross(v1_p, v2_p)); + + const float alpha = fast_acosf(dot(u02, u01)); + const float beta = fast_acosf(-dot(u01, u12)); + const float gamma = fast_acosf(dot(u02, u12)); + const float solid_angle = alpha + beta + gamma - M_PI_F; + + /* distribution_pdf_triangles is calculated over triangle area, but we're not sampling over + * its area */ + if (UNLIKELY(solid_angle == 0.0f)) { + return 0.0f; + } + else { + pdf = 1.0f / solid_angle; + } + } + else { + if (UNLIKELY(area == 0.0f)) { + return 0.0f; + } + + pdf = triangle_light_pdf_area_sampling(sd->Ng, sd->I, t) / area; + } + + /* Belongs in distribution.h but can reuse computations here. */ + float distribution_area = area; + + if (has_motion && area != 0.0f) { + /* For motion blur need area of triangle at fixed time as used in the CDF. */ + triangle_world_space_vertices(kg, sd->object, sd->prim, -1.0f, V); + distribution_area = triangle_area(V[0], V[1], V[2]); + } + + pdf *= distribution_area * kernel_data.integrator.distribution_pdf_triangles; + + return pdf; +} + +template +ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg, + int prim, + int object, + float randu, + float randv, + float time, + ccl_private LightSample *ls, + const float3 P) +{ + /* A naive heuristic to decide between costly solid angle sampling + * and simple area sampling, comparing the distance to the triangle plane + * to the length of the edges of the triangle. */ + + float3 V[3]; + bool has_motion = triangle_world_space_vertices(kg, object, prim, time, V); + + const float3 e0 = V[1] - V[0]; + const float3 e1 = V[2] - V[0]; + const float3 e2 = V[2] - V[1]; + const float longest_edge_squared = max(len_squared(e0), max(len_squared(e1), len_squared(e2))); + const float3 N0 = cross(e0, e1); + float Nl = 0.0f; + ls->Ng = safe_normalize_len(N0, &Nl); + const float area = 0.5f * Nl; + + /* flip normal if necessary */ + const int object_flag = kernel_data_fetch(object_flag, object); + if (object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { + ls->Ng = -ls->Ng; + } + ls->eval_fac = 1.0f; + ls->shader = kernel_data_fetch(tri_shader, prim); + ls->object = object; + ls->prim = prim; + ls->lamp = LAMP_NONE; + ls->shader |= SHADER_USE_MIS; + ls->type = LIGHT_TRIANGLE; + ls->group = object_lightgroup(kg, object); + + float distance_to_plane = fabsf(dot(N0, V[0] - P) / dot(N0, N0)); + + if (!in_volume_segment && (longest_edge_squared > distance_to_plane * distance_to_plane)) { + /* see James Arvo, "Stratified Sampling of Spherical Triangles" + * http://www.graphics.cornell.edu/pubs/1995/Arv95c.pdf */ + + /* project the triangle to the unit sphere + * and calculate its edges and angles */ + const float3 v0_p = V[0] - P; + const float3 v1_p = V[1] - P; + const float3 v2_p = V[2] - P; + + const float3 u01 = safe_normalize(cross(v0_p, v1_p)); + const float3 u02 = safe_normalize(cross(v0_p, v2_p)); + const float3 u12 = safe_normalize(cross(v1_p, v2_p)); + + const float3 A = safe_normalize(v0_p); + const float3 B = safe_normalize(v1_p); + const float3 C = safe_normalize(v2_p); + + const float cos_alpha = dot(u02, u01); + const float cos_beta = -dot(u01, u12); + const float cos_gamma = dot(u02, u12); + + /* calculate dihedral angles */ + const float alpha = fast_acosf(cos_alpha); + const float beta = fast_acosf(cos_beta); + const float gamma = fast_acosf(cos_gamma); + /* the area of the unit spherical triangle = solid angle */ + const float solid_angle = alpha + beta + gamma - M_PI_F; + + /* precompute a few things + * these could be re-used to take several samples + * as they are independent of randu/randv */ + const float cos_c = dot(A, B); + const float sin_alpha = fast_sinf(alpha); + const float product = sin_alpha * cos_c; + + /* Select a random sub-area of the spherical triangle + * and calculate the third vertex C_ of that new triangle */ + const float phi = randu * solid_angle - alpha; + float s, t; + fast_sincosf(phi, &s, &t); + const float u = t - cos_alpha; + const float v = s + product; + + const float3 U = safe_normalize(C - dot(C, A) * A); + + float q = 1.0f; + const float det = ((v * s + u * t) * sin_alpha); + if (det != 0.0f) { + q = ((v * t - u * s) * cos_alpha - v) / det; + } + const float temp = max(1.0f - q * q, 0.0f); + + const float3 C_ = safe_normalize(q * A + sqrtf(temp) * U); + + /* Finally, select a random point along the edge of the new triangle + * That point on the spherical triangle is the sampled ray direction */ + const float z = 1.0f - randv * (1.0f - dot(C_, B)); + ls->D = z * B + safe_sqrtf(1.0f - z * z) * safe_normalize(C_ - dot(C_, B) * B); + + /* calculate intersection with the planar triangle */ + if (!ray_triangle_intersect( + P, ls->D, 0.0f, FLT_MAX, V[0], V[1], V[2], &ls->u, &ls->v, &ls->t)) { + ls->pdf = 0.0f; + return false; + } + + ls->P = P + ls->D * ls->t; + + /* distribution_pdf_triangles is calculated over triangle area, but we're sampling over solid + * angle */ + if (UNLIKELY(solid_angle == 0.0f)) { + ls->pdf = 0.0f; + return false; + } + else { + ls->pdf = 1.0f / solid_angle; + } + } + else { + if (UNLIKELY(area == 0.0f)) { + return 0.0f; + } + + /* compute random point in triangle. From Eric Heitz's "A Low-Distortion Map Between Triangle + * and Square" */ + float u = randu; + float v = randv; + if (v > u) { + u *= 0.5f; + v -= u; + } + else { + v *= 0.5f; + u -= v; + } + + const float t = 1.0f - u - v; + ls->P = u * V[0] + v * V[1] + t * V[2]; + /* compute incoming direction, distance and pdf */ + ls->D = normalize_len(ls->P - P, &ls->t); + ls->pdf = triangle_light_pdf_area_sampling(ls->Ng, -ls->D, ls->t) / area; + ls->u = u; + ls->v = v; + } + + /* Belongs in distribution.h but can reuse computations here. */ + float distribution_area = area; + + if (has_motion && area != 0.0f) { + /* For motion blur need area of triangle at fixed time as used in the CDF. */ + triangle_world_space_vertices(kg, object, prim, -1.0f, V); + distribution_area = triangle_area(V[0], V[1], V[2]); + } + + ls->pdf_selection = distribution_area * kernel_data.integrator.distribution_pdf_triangles; + ls->pdf *= ls->pdf_selection; + + return (ls->pdf > 0.0f); +} +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index c737e1f7f80..1a2210fd52a 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -1268,20 +1268,20 @@ static_assert_align(KernelCurveSegment, 8); typedef struct KernelSpotLight { float radius; float invarea; - float spot_angle; + float cos_half_spot_angle; float spot_smooth; - float dir[3]; + packed_float3 dir; float pad; } KernelSpotLight; /* PointLight is SpotLight with only radius and invarea being used. */ typedef struct KernelAreaLight { - float axisu[3]; + packed_float3 extentu; float invarea; - float axisv[3]; + packed_float3 extentv; float tan_spread; - float dir[3]; + packed_float3 dir; float normalize_spread; } KernelAreaLight; @@ -1294,7 +1294,7 @@ typedef struct KernelDistantLight { typedef struct KernelLight { int type; - float co[3]; + packed_float3 co; int shader_id; float max_bounces; float random; diff --git a/intern/cycles/scene/background.cpp b/intern/cycles/scene/background.cpp index bffc8895bfd..d29a4c02423 100644 --- a/intern/cycles/scene/background.cpp +++ b/intern/cycles/scene/background.cpp @@ -4,6 +4,7 @@ #include "scene/background.h" #include "device/device.h" #include "scene/integrator.h" +#include "scene/light.h" #include "scene/scene.h" #include "scene/shader.h" #include "scene/shader_graph.h" diff --git a/intern/cycles/scene/light.cpp b/intern/cycles/scene/light.cpp index ea1f45793fa..3d5149cc647 100644 --- a/intern/cycles/scene/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -263,30 +263,20 @@ bool LightManager::object_usable_as_light(Object *object) return false; } -void LightManager::device_update_distribution(Device *, +void LightManager::device_update_distribution(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress) { + KernelIntegrator *kintegrator = &dscene->data.integrator; + KernelFilm *kfilm = &dscene->data.film; + + /* Update CDF over lights. */ progress.set_status("Updating Lights", "Computing distribution"); - /* count */ - size_t num_lights = 0; - size_t num_portals = 0; - size_t num_background_lights = 0; + /* Counts emissive triangles in the scene. */ size_t num_triangles = 0; - bool background_mis = false; - - foreach (Light *light, scene->lights) { - if (light->is_enabled) { - num_lights++; - } - if (light->is_portal) { - num_portals++; - } - } - foreach (Object *object, scene->objects) { if (progress.get_cancel()) return; @@ -295,9 +285,10 @@ void LightManager::device_update_distribution(Device *, continue; } - /* Count triangles. */ + /* Count emissive triangles. */ Mesh *mesh = static_cast(object->get_geometry()); size_t mesh_num_triangles = mesh->num_triangles(); + for (size_t i = 0; i < mesh_num_triangles; i++) { int shader_index = mesh->get_shader()[i]; Shader *shader = (shader_index < mesh->get_used_shaders().size()) ? @@ -310,14 +301,20 @@ void LightManager::device_update_distribution(Device *, } } - size_t num_distribution = num_triangles + num_lights; + const size_t num_lights = kintegrator->num_lights; + const size_t num_background_lights = kintegrator->num_background_lights; + const size_t num_distribution = num_triangles + num_lights; + + /* Distribution size. */ + kintegrator->num_distribution = num_distribution; + VLOG_INFO << "Total " << num_distribution << " of light distribution primitives."; - /* emission area */ + /* Emission area. */ KernelLightDistribution *distribution = dscene->light_distribution.alloc(num_distribution + 1); float totarea = 0.0f; - /* triangles */ + /* Triangles. */ size_t offset = 0; int j = 0; @@ -390,9 +387,9 @@ void LightManager::device_update_distribution(Device *, j++; } - float trianglearea = totarea; - /* point lights */ - bool use_lamp_mis = false; + const float trianglearea = totarea; + + /* Lights. */ int light_index = 0; if (num_lights > 0) { @@ -407,20 +404,6 @@ void LightManager::device_update_distribution(Device *, distribution[offset].lamp.size = light->size; totarea += lightarea; - if (light->light_type == LIGHT_DISTANT) { - use_lamp_mis |= (light->angle > 0.0f && light->use_mis); - } - else if (light->light_type == LIGHT_POINT || light->light_type == LIGHT_SPOT) { - use_lamp_mis |= (light->size > 0.0f && light->use_mis); - } - else if (light->light_type == LIGHT_AREA) { - use_lamp_mis |= light->use_mis; - } - else if (light->light_type == LIGHT_BACKGROUND) { - num_background_lights++; - background_mis |= light->use_mis; - } - light_index++; offset++; } @@ -441,82 +424,42 @@ void LightManager::device_update_distribution(Device *, if (progress.get_cancel()) return; - /* update device */ - KernelIntegrator *kintegrator = &dscene->data.integrator; - KernelBackground *kbackground = &dscene->data.background; - KernelFilm *kfilm = &dscene->data.film; + /* Update integrator state. */ kintegrator->use_direct_light = (totarea > 0.0f); - if (kintegrator->use_direct_light) { - /* number of emissives */ - kintegrator->num_distribution = num_distribution; - - /* precompute pdfs */ - kintegrator->pdf_triangles = 0.0f; - kintegrator->pdf_lights = 0.0f; - - /* sample one, with 0.5 probability of light or triangle */ - kintegrator->num_all_lights = num_lights; - - if (trianglearea > 0.0f) { - kintegrator->pdf_triangles = 1.0f / trianglearea; - if (num_lights) - kintegrator->pdf_triangles *= 0.5f; - } + /* Precompute pdfs for distribution sampling. + * Sample one, with 0.5 probability of light or triangle. */ + kintegrator->distribution_pdf_triangles = 0.0f; + kintegrator->distribution_pdf_lights = 0.0f; + if (trianglearea > 0.0f) { + kintegrator->distribution_pdf_triangles = 1.0f / trianglearea; if (num_lights) { - kintegrator->pdf_lights = 1.0f / num_lights; - if (trianglearea > 0.0f) - kintegrator->pdf_lights *= 0.5f; + kintegrator->distribution_pdf_triangles *= 0.5f; } - - kintegrator->use_lamp_mis = use_lamp_mis; - - /* bit of an ugly hack to compensate for emitting triangles influencing - * amount of samples we get for this pass */ - kfilm->pass_shadow_scale = 1.0f; - - if (kintegrator->pdf_triangles != 0.0f) - kfilm->pass_shadow_scale /= 0.5f; - - if (num_background_lights < num_lights) - kfilm->pass_shadow_scale /= (float)(num_lights - num_background_lights) / (float)num_lights; - - /* CDF */ - dscene->light_distribution.copy_to_device(); - - /* Portals */ - if (num_portals > 0) { - kbackground->portal_offset = light_index; - kbackground->num_portals = num_portals; - kbackground->portal_weight = 1.0f; - } - else { - kbackground->num_portals = 0; - kbackground->portal_offset = 0; - kbackground->portal_weight = 0.0f; - } - - /* Map */ - kbackground->map_weight = background_mis ? 1.0f : 0.0f; } - else { - dscene->light_distribution.free(); - kintegrator->num_distribution = 0; - kintegrator->num_all_lights = 0; - kintegrator->pdf_triangles = 0.0f; - kintegrator->pdf_lights = 0.0f; - kintegrator->use_lamp_mis = false; - - kbackground->num_portals = 0; - kbackground->portal_offset = 0; - kbackground->portal_weight = 0.0f; - kbackground->sun_weight = 0.0f; - kbackground->map_weight = 0.0f; - - kfilm->pass_shadow_scale = 1.0f; + if (num_lights) { + kintegrator->distribution_pdf_lights = 1.0f / num_lights; + if (trianglearea > 0.0f) { + kintegrator->distribution_pdf_lights *= 0.5f; + } } + + /* bit of an ugly hack to compensate for emitting triangles influencing + * amount of samples we get for this pass */ + kfilm->pass_shadow_scale = 1.0f; + + if (kintegrator->distribution_pdf_triangles != 0.0f) { + kfilm->pass_shadow_scale /= 0.5f; + } + + if (num_background_lights < num_lights) { + kfilm->pass_shadow_scale /= (float)(num_lights - num_background_lights) / (float)num_lights; + } + + /* Copy distribution to device. */ + dscene->light_distribution.copy_to_device(); } static void background_cdf( @@ -564,31 +507,34 @@ void LightManager::device_update_background(Device *device, Scene *scene, Progress &progress) { + KernelIntegrator *kintegrator = &dscene->data.integrator; KernelBackground *kbackground = &dscene->data.background; Light *background_light = NULL; + bool background_mis = false; + /* find background light */ foreach (Light *light, scene->lights) { - if (light->light_type == LIGHT_BACKGROUND) { + if (light->light_type == LIGHT_BACKGROUND && light->is_enabled) { background_light = light; - break; + background_mis |= light->use_mis; } } + kbackground->portal_weight = kintegrator->num_portals > 0 ? 1.0f : 0.0f; + kbackground->map_weight = background_mis ? 1.0f : 0.0f; + kbackground->sun_weight = 0.0f; + /* no background light found, signal renderer to skip sampling */ if (!background_light || !background_light->is_enabled) { kbackground->map_res_x = 0; kbackground->map_res_y = 0; - kbackground->map_weight = 0.0f; - kbackground->sun_weight = 0.0f; kbackground->use_mis = (kbackground->portal_weight > 0.0f); return; } progress.set_status("Updating Lights", "Importance map"); - assert(dscene->data.integrator.use_direct_light); - int2 environment_res = make_int2(0, 0); Shader *shader = scene->background->get_shader(scene); int num_suns = 0; @@ -632,6 +578,7 @@ void LightManager::device_update_background(Device *device, kbackground->sun = make_float4( sun_direction.x, sun_direction.y, sun_direction.z, half_angle); + /* empirical value */ kbackground->sun_weight = 4.0f; environment_res.x = max(environment_res.x, 512); environment_res.y = max(environment_res.y, 256); @@ -714,27 +661,83 @@ void LightManager::device_update_background(Device *device, dscene->light_background_conditional_cdf.copy_to_device(); } -void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *scene) +void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *scene) { - int num_scene_lights = scene->lights.size(); + /* Counts lights in the scene. */ + size_t num_lights = 0; + size_t num_portals = 0; + size_t num_background_lights = 0; + size_t num_distant_lights = 0; + bool use_light_mis = false; - int num_lights = 0; foreach (Light *light, scene->lights) { - if (light->is_enabled || light->is_portal) { + if (light->is_enabled) { num_lights++; + + if (light->light_type == LIGHT_DISTANT) { + num_distant_lights++; + } + else if (light->light_type == LIGHT_POINT || light->light_type == LIGHT_SPOT) { + use_light_mis |= (light->size > 0.0f && light->use_mis); + } + else if (light->light_type == LIGHT_AREA) { + use_light_mis |= light->use_mis; + } + else if (light->light_type == LIGHT_BACKGROUND) { + num_distant_lights++; + num_background_lights++; + } + } + if (light->is_portal) { + num_portals++; } } - KernelLight *klights = dscene->lights.alloc(num_lights); + /* Update integrator settings. */ + KernelIntegrator *kintegrator = &dscene->data.integrator; + kintegrator->num_lights = num_lights; + kintegrator->num_distant_lights = num_distant_lights; + kintegrator->num_background_lights = num_background_lights; + kintegrator->use_light_mis = use_light_mis; - if (num_lights == 0) { - VLOG_WORK << "No effective light, ignoring points update."; - return; - } + kintegrator->num_portals = num_portals; + kintegrator->portal_offset = num_lights; + + /* Create KernelLight for every portal and enabled light in the scene. */ + KernelLight *klights = dscene->lights.alloc(num_lights + num_portals); int light_index = 0; + int portal_index = num_lights; foreach (Light *light, scene->lights) { + /* Consider moving portals update to their own function + * keeping this one more manageable. */ + if (light->is_portal) { + assert(light->light_type == LIGHT_AREA); + + float3 extentu = light->axisu * (light->sizeu * light->size); + float3 extentv = light->axisv * (light->sizev * light->size); + float area = len(extentu) * len(extentv); + if (light->round) { + area *= -M_PI_4_F; + } + float invarea = (area != 0.0f) ? 1.0f / area : 1.0f; + float3 dir = light->dir; + + dir = safe_normalize(dir); + + klights[portal_index].co = light->co; + klights[portal_index].area.extentu = extentu; + klights[portal_index].area.extentv = extentv; + klights[portal_index].area.invarea = invarea; + klights[portal_index].area.dir = dir; + klights[portal_index].tfm = light->tfm; + klights[portal_index].itfm = transform_inverse(light->tfm); + + portal_index++; + continue; + } + if (!light->is_enabled) { continue; } @@ -781,10 +784,7 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc if (light->use_mis && radius > 0.0f) shader_id |= SHADER_USE_MIS; - klights[light_index].co[0] = co.x; - klights[light_index].co[1] = co.y; - klights[light_index].co[2] = co.z; - + klights[light_index].co = co; klights[light_index].spot.radius = radius; klights[light_index].spot.invarea = invarea; } @@ -803,10 +803,7 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc if (light->use_mis && area > 0.0f) shader_id |= SHADER_USE_MIS; - klights[light_index].co[0] = dir.x; - klights[light_index].co[1] = dir.y; - klights[light_index].co[2] = dir.z; - + klights[light_index].co = dir; klights[light_index].distant.invarea = invarea; klights[light_index].distant.radius = radius; klights[light_index].distant.cosangle = cosangle; @@ -814,6 +811,8 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc else if (light->light_type == LIGHT_BACKGROUND) { uint visibility = scene->background->get_visibility(); + dscene->data.background.light_index = light_index; + shader_id &= ~SHADER_AREA_LIGHT; shader_id |= SHADER_USE_MIS; @@ -831,9 +830,9 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc } } else if (light->light_type == LIGHT_AREA) { - float3 axisu = light->axisu * (light->sizeu * light->size); - float3 axisv = light->axisv * (light->sizev * light->size); - float area = len(axisu) * len(axisv); + float3 extentu = light->axisu * (light->sizeu * light->size); + float3 extentv = light->axisv * (light->sizev * light->size); + float area = len(extentu) * len(extentv); if (light->round) { area *= -M_PI_4_F; } @@ -854,20 +853,11 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc if (light->use_mis && area != 0.0f) shader_id |= SHADER_USE_MIS; - klights[light_index].co[0] = co.x; - klights[light_index].co[1] = co.y; - klights[light_index].co[2] = co.z; - - klights[light_index].area.axisu[0] = axisu.x; - klights[light_index].area.axisu[1] = axisu.y; - klights[light_index].area.axisu[2] = axisu.z; - klights[light_index].area.axisv[0] = axisv.x; - klights[light_index].area.axisv[1] = axisv.y; - klights[light_index].area.axisv[2] = axisv.z; + klights[light_index].co = co; + klights[light_index].area.extentu = extentu; + klights[light_index].area.extentv = extentv; klights[light_index].area.invarea = invarea; - klights[light_index].area.dir[0] = dir.x; - klights[light_index].area.dir[1] = dir.y; - klights[light_index].area.dir[2] = dir.z; + klights[light_index].area.dir = dir; klights[light_index].area.tan_spread = tan_spread; klights[light_index].area.normalize_spread = normalize_spread; } @@ -876,8 +866,8 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc float radius = light->size; float invarea = (radius > 0.0f) ? 1.0f / (M_PI_F * radius * radius) : 1.0f; - float spot_angle = cosf(light->spot_angle * 0.5f); - float spot_smooth = (1.0f - spot_angle) * light->spot_smooth; + float cos_half_spot_angle = cosf(light->spot_angle * 0.5f); + float spot_smooth = (1.0f - cos_half_spot_angle) * light->spot_smooth; float3 dir = light->dir; dir = safe_normalize(dir); @@ -885,17 +875,12 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc if (light->use_mis && radius > 0.0f) shader_id |= SHADER_USE_MIS; - klights[light_index].co[0] = co.x; - klights[light_index].co[1] = co.y; - klights[light_index].co[2] = co.z; - + klights[light_index].co = co; klights[light_index].spot.radius = radius; klights[light_index].spot.invarea = invarea; - klights[light_index].spot.spot_angle = spot_angle; + klights[light_index].spot.cos_half_spot_angle = cos_half_spot_angle; klights[light_index].spot.spot_smooth = spot_smooth; - klights[light_index].spot.dir[0] = dir.x; - klights[light_index].spot.dir[1] = dir.y; - klights[light_index].spot.dir[2] = dir.z; + klights[light_index].spot.dir = dir; } klights[light_index].shader_id = shader_id; @@ -918,49 +903,7 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc light_index++; } - /* TODO(sergey): Consider moving portals update to their own function - * keeping this one more manageable. - */ - foreach (Light *light, scene->lights) { - if (!light->is_portal) - continue; - assert(light->light_type == LIGHT_AREA); - - float3 co = light->co; - float3 axisu = light->axisu * (light->sizeu * light->size); - float3 axisv = light->axisv * (light->sizev * light->size); - float area = len(axisu) * len(axisv); - if (light->round) { - area *= -M_PI_4_F; - } - float invarea = (area != 0.0f) ? 1.0f / area : 1.0f; - float3 dir = light->dir; - - dir = safe_normalize(dir); - - klights[light_index].co[0] = co.x; - klights[light_index].co[1] = co.y; - klights[light_index].co[2] = co.z; - - klights[light_index].area.axisu[0] = axisu.x; - klights[light_index].area.axisu[1] = axisu.y; - klights[light_index].area.axisu[2] = axisu.z; - klights[light_index].area.axisv[0] = axisv.x; - klights[light_index].area.axisv[1] = axisv.y; - klights[light_index].area.axisv[2] = axisv.z; - klights[light_index].area.invarea = invarea; - klights[light_index].area.dir[0] = dir.x; - klights[light_index].area.dir[1] = dir.y; - klights[light_index].area.dir[2] = dir.z; - klights[light_index].tfm = light->tfm; - klights[light_index].itfm = transform_inverse(light->tfm); - - light_index++; - } - - VLOG_INFO << "Number of lights sent to the device: " << light_index; - - VLOG_INFO << "Number of lights without contribution: " << num_scene_lights - light_index; + VLOG_INFO << "Number of lights sent to the device: " << num_lights; dscene->lights.copy_to_device(); } @@ -986,11 +929,7 @@ void LightManager::device_update(Device *device, device_free(device, dscene, need_update_background); - device_update_points(device, dscene, scene); - if (progress.get_cancel()) - return; - - device_update_distribution(device, dscene, scene, progress); + device_update_lights(device, dscene, scene); if (progress.get_cancel()) return; @@ -1000,6 +939,10 @@ void LightManager::device_update(Device *device, return; } + device_update_distribution(device, dscene, scene, progress); + if (progress.get_cancel()) + return; + device_update_ies(dscene); if (progress.get_cancel()) return; diff --git a/intern/cycles/scene/light.h b/intern/cycles/scene/light.h index 5b852f210fc..b0e8f3ca390 100644 --- a/intern/cycles/scene/light.h +++ b/intern/cycles/scene/light.h @@ -127,11 +127,12 @@ class LightManager { */ void test_enabled_lights(Scene *scene); - void device_update_points(Device *device, DeviceScene *dscene, Scene *scene); + void device_update_lights(Device *device, DeviceScene *dscene, Scene *scene); void device_update_distribution(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); + void device_update_tree(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); void device_update_background(Device *device, DeviceScene *dscene, Scene *scene,