Cycles: store axis and length of an area light instead of their product
This commit is contained in:
parent
6a7917162c
commit
e028662f78
|
@ -15,18 +15,19 @@ CCL_NAMESPACE_BEGIN
|
|||
* 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,
|
||||
const float3 axis_u,
|
||||
const float len_u,
|
||||
const float3 axis_v,
|
||||
const float len_v,
|
||||
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;
|
||||
float3 corner = *light_p - axis_u * len_u * 0.5f - axis_v * len_v * 0.5f;
|
||||
/* Compute local reference system R. */
|
||||
float3 x = normalize_len(extentu, &extentu_len);
|
||||
float3 y = normalize_len(extentv, &extentv_len);
|
||||
float3 x = axis_u;
|
||||
float3 y = axis_v;
|
||||
float3 z = cross(x, y);
|
||||
/* Compute rectangle coords in local reference system. */
|
||||
float3 dir = corner - P;
|
||||
|
@ -38,8 +39,8 @@ ccl_device_inline float area_light_rect_sample(float3 P,
|
|||
}
|
||||
float x0 = dot(dir, x);
|
||||
float y0 = dot(dir, y);
|
||||
float x1 = x0 + extentu_len;
|
||||
float y1 = y0 + extentv_len;
|
||||
float x1 = x0 + len_u;
|
||||
float y1 = y0 + len_v;
|
||||
/* 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;
|
||||
|
@ -106,8 +107,10 @@ ccl_device float area_light_spread_attenuation(const float3 D,
|
|||
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 float3 axis_u,
|
||||
ccl_private float *len_u,
|
||||
const float3 axis_v,
|
||||
ccl_private float *len_v,
|
||||
const float tan_spread)
|
||||
{
|
||||
/* Closest point in area light plane and distance to that plane. */
|
||||
|
@ -117,22 +120,16 @@ ccl_device bool area_light_spread_clamp_area_light(const float3 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);
|
||||
const float closest_u = dot(axis_u, closest_P - *lightP);
|
||||
const float closest_v = dot(axis_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);
|
||||
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) {
|
||||
|
@ -143,12 +140,10 @@ ccl_device bool area_light_spread_clamp_area_light(const float3 P,
|
|||
* 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;
|
||||
*len_u = max_u - min_u;
|
||||
*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;
|
||||
*lightP = *lightP + new_center_u * axis_u + new_center_v * axis_v;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -164,8 +159,10 @@ ccl_device_inline bool area_light_sample(const ccl_global KernelLight *klight,
|
|||
{
|
||||
ls->P = klight->co;
|
||||
|
||||
float3 extentu = klight->area.extentu;
|
||||
float3 extentv = klight->area.extentv;
|
||||
const float3 axis_u = klight->area.axis_u;
|
||||
const float3 axis_v = klight->area.axis_v;
|
||||
const float len_u = klight->area.len_u;
|
||||
const float len_v = klight->area.len_v;
|
||||
float3 Ng = klight->area.dir;
|
||||
float invarea = fabsf(klight->area.invarea);
|
||||
bool is_round = (klight->area.invarea < 0.0f);
|
||||
|
@ -179,30 +176,36 @@ ccl_device_inline bool area_light_sample(const ccl_global KernelLight *klight,
|
|||
float3 inplane;
|
||||
|
||||
if (is_round || in_volume_segment) {
|
||||
inplane = ellipse_sample(extentu * 0.5f, extentv * 0.5f, randu, randv);
|
||||
inplane = ellipse_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, randu, randv);
|
||||
ls->P += inplane;
|
||||
ls->pdf = invarea;
|
||||
}
|
||||
else {
|
||||
inplane = ls->P;
|
||||
|
||||
float3 sample_extentu = extentu;
|
||||
float3 sample_extentv = extentv;
|
||||
float sample_len_u = len_u;
|
||||
float sample_len_v = len_v;
|
||||
|
||||
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)) {
|
||||
if (!area_light_spread_clamp_area_light(P,
|
||||
Ng,
|
||||
&ls->P,
|
||||
axis_u,
|
||||
&sample_len_u,
|
||||
axis_v,
|
||||
&sample_len_v,
|
||||
klight->area.tan_spread)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ls->pdf = area_light_rect_sample(
|
||||
P, &ls->P, sample_extentu, sample_extentv, randu, randv, true);
|
||||
P, &ls->P, axis_u, sample_len_u, axis_v, sample_len_v, 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));
|
||||
const float light_u = dot(inplane, axis_u) / len_u;
|
||||
const float light_v = dot(inplane, axis_v) / len_v;
|
||||
|
||||
/* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */
|
||||
ls->u = light_v + 0.5f;
|
||||
|
@ -254,8 +257,8 @@ ccl_device_inline bool area_light_intersect(const ccl_global KernelLight *klight
|
|||
return false;
|
||||
}
|
||||
|
||||
const float3 extentu = klight->area.extentu;
|
||||
const float3 extentv = klight->area.extentv;
|
||||
const float3 inv_extent_u = klight->area.axis_u / klight->area.len_u;
|
||||
const float3 inv_extent_v = klight->area.axis_v / klight->area.len_v;
|
||||
const float3 Ng = klight->area.dir;
|
||||
|
||||
/* One sided. */
|
||||
|
@ -266,8 +269,19 @@ ccl_device_inline bool area_light_intersect(const ccl_global KernelLight *klight
|
|||
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);
|
||||
return ray_quad_intersect(ray->P,
|
||||
ray->D,
|
||||
ray->tmin,
|
||||
ray->tmax,
|
||||
light_P,
|
||||
inv_extent_u,
|
||||
inv_extent_v,
|
||||
Ng,
|
||||
&P,
|
||||
t,
|
||||
u,
|
||||
v,
|
||||
is_round);
|
||||
}
|
||||
|
||||
ccl_device_inline bool area_light_sample_from_intersection(
|
||||
|
@ -281,8 +295,6 @@ ccl_device_inline bool area_light_sample_from_intersection(
|
|||
/* 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;
|
||||
|
||||
|
@ -296,17 +308,26 @@ ccl_device_inline bool area_light_sample_from_intersection(
|
|||
ls->pdf = invarea * lamp_light_pdf(Ng, -ray_D, ls->t);
|
||||
}
|
||||
else {
|
||||
float3 sample_extentu = extentu;
|
||||
float3 sample_extentv = extentv;
|
||||
const float3 axis_u = klight->area.axis_u;
|
||||
const float3 axis_v = klight->area.axis_v;
|
||||
float sample_len_u = klight->area.len_u;
|
||||
float sample_len_v = klight->area.len_v;
|
||||
|
||||
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)) {
|
||||
if (!area_light_spread_clamp_area_light(ray_P,
|
||||
Ng,
|
||||
&light_P,
|
||||
axis_u,
|
||||
&sample_len_u,
|
||||
axis_v,
|
||||
&sample_len_v,
|
||||
klight->area.tan_spread)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ls->pdf = area_light_rect_sample(ray_P, &light_P, sample_extentu, sample_extentv, 0, 0, false);
|
||||
ls->pdf = area_light_rect_sample(
|
||||
ray_P, &light_P, axis_u, sample_len_u, axis_v, sample_len_v, 0, 0, false);
|
||||
}
|
||||
ls->eval_fac = 0.25f * invarea;
|
||||
|
||||
|
|
|
@ -166,8 +166,14 @@ ccl_device_inline float background_portal_pdf(
|
|||
|
||||
int portal = kernel_data.integrator.portal_offset + p;
|
||||
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, portal);
|
||||
float3 extentu = klight->area.extentu;
|
||||
float3 extentv = klight->area.extentv;
|
||||
|
||||
const float3 axis_u = klight->area.axis_u;
|
||||
const float len_u = klight->area.len_u;
|
||||
const float3 axis_v = klight->area.axis_v;
|
||||
const float len_v = klight->area.len_v;
|
||||
const float3 inv_extent_u = axis_u / len_u;
|
||||
const float3 inv_extent_v = axis_v / len_v;
|
||||
|
||||
bool is_round = (klight->area.invarea < 0.0f);
|
||||
|
||||
if (!ray_quad_intersect(P,
|
||||
|
@ -175,8 +181,8 @@ ccl_device_inline float background_portal_pdf(
|
|||
1e-4f,
|
||||
FLT_MAX,
|
||||
lightpos,
|
||||
extentu,
|
||||
extentv,
|
||||
inv_extent_u,
|
||||
inv_extent_v,
|
||||
dir,
|
||||
NULL,
|
||||
NULL,
|
||||
|
@ -191,7 +197,8 @@ ccl_device_inline float background_portal_pdf(
|
|||
portal_pdf += fabsf(klight->area.invarea) * lamp_light_pdf(dir, -D, t);
|
||||
}
|
||||
else {
|
||||
portal_pdf += area_light_rect_sample(P, &lightpos, extentu, extentv, 0.0f, 0.0f, false);
|
||||
portal_pdf += area_light_rect_sample(
|
||||
P, &lightpos, axis_u, len_u, axis_v, len_v, 0.0f, 0.0f, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,19 +247,22 @@ ccl_device float3 background_portal_sample(KernelGlobals kg,
|
|||
/* p is the portal to be sampled. */
|
||||
int portal = kernel_data.integrator.portal_offset + p;
|
||||
const ccl_global KernelLight *klight = &kernel_data_fetch(lights, portal);
|
||||
float3 extentu = klight->area.extentu;
|
||||
float3 extentv = klight->area.extentv;
|
||||
const float3 axis_u = klight->area.axis_u;
|
||||
const float3 axis_v = klight->area.axis_v;
|
||||
const float len_u = klight->area.len_u;
|
||||
const float len_v = klight->area.len_v;
|
||||
bool is_round = (klight->area.invarea < 0.0f);
|
||||
|
||||
float3 D;
|
||||
if (is_round) {
|
||||
lightpos += ellipse_sample(extentu * 0.5f, extentv * 0.5f, randu, randv);
|
||||
lightpos += ellipse_sample(axis_u * len_u * 0.5f, axis_v * len_v * 0.5f, randu, randv);
|
||||
float t;
|
||||
D = normalize_len(lightpos - P, &t);
|
||||
*pdf = fabsf(klight->area.invarea) * lamp_light_pdf(dir, -D, t);
|
||||
}
|
||||
else {
|
||||
*pdf = area_light_rect_sample(P, &lightpos, extentu, extentv, randu, randv, true);
|
||||
*pdf = area_light_rect_sample(
|
||||
P, &lightpos, axis_u, len_u, axis_v, len_v, randu, randv, true);
|
||||
D = normalize(lightpos - P);
|
||||
}
|
||||
|
||||
|
|
|
@ -1296,12 +1296,15 @@ typedef struct KernelSpotLight {
|
|||
/* PointLight is SpotLight with only radius and invarea being used. */
|
||||
|
||||
typedef struct KernelAreaLight {
|
||||
packed_float3 extentu;
|
||||
float invarea;
|
||||
packed_float3 extentv;
|
||||
float tan_spread;
|
||||
packed_float3 axis_u;
|
||||
float len_u;
|
||||
packed_float3 axis_v;
|
||||
float len_v;
|
||||
packed_float3 dir;
|
||||
float invarea;
|
||||
float tan_spread;
|
||||
float normalize_spread;
|
||||
float pad[2];
|
||||
} KernelAreaLight;
|
||||
|
||||
typedef struct KernelDistantLight {
|
||||
|
|
|
@ -719,7 +719,11 @@ void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *sc
|
|||
|
||||
float3 extentu = light->axisu * (light->sizeu * light->size);
|
||||
float3 extentv = light->axisv * (light->sizev * light->size);
|
||||
float area = len(extentu) * len(extentv);
|
||||
|
||||
float len_u, len_v;
|
||||
float3 axis_u = normalize_len(extentu, &len_u);
|
||||
float3 axis_v = normalize_len(extentv, &len_v);
|
||||
float area = len_u * len_v;
|
||||
if (light->round) {
|
||||
area *= -M_PI_4_F;
|
||||
}
|
||||
|
@ -729,8 +733,10 @@ void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *sc
|
|||
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.axis_u = axis_u;
|
||||
klights[portal_index].area.len_u = len_u;
|
||||
klights[portal_index].area.axis_v = axis_v;
|
||||
klights[portal_index].area.len_v = len_v;
|
||||
klights[portal_index].area.invarea = invarea;
|
||||
klights[portal_index].area.dir = dir;
|
||||
klights[portal_index].tfm = light->tfm;
|
||||
|
@ -834,7 +840,11 @@ void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *sc
|
|||
else if (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);
|
||||
|
||||
float len_u, len_v;
|
||||
float3 axis_u = normalize_len(extentu, &len_u);
|
||||
float3 axis_v = normalize_len(extentv, &len_v);
|
||||
float area = len_u * len_v;
|
||||
if (light->round) {
|
||||
area *= -M_PI_4_F;
|
||||
}
|
||||
|
@ -856,8 +866,10 @@ void LightManager::device_update_lights(Device *, DeviceScene *dscene, Scene *sc
|
|||
shader_id |= SHADER_USE_MIS;
|
||||
|
||||
klights[light_index].co = co;
|
||||
klights[light_index].area.extentu = extentu;
|
||||
klights[light_index].area.extentv = extentv;
|
||||
klights[light_index].area.axis_u = axis_u;
|
||||
klights[light_index].area.len_u = len_u;
|
||||
klights[light_index].area.axis_v = axis_v;
|
||||
klights[light_index].area.len_v = len_v;
|
||||
klights[light_index].area.invarea = invarea;
|
||||
klights[light_index].area.dir = dir;
|
||||
klights[light_index].area.tan_spread = tan_spread;
|
||||
|
|
|
@ -257,8 +257,8 @@ ccl_device bool ray_quad_intersect(float3 ray_P,
|
|||
float ray_tmin,
|
||||
float ray_tmax,
|
||||
float3 quad_P,
|
||||
float3 quad_u,
|
||||
float3 quad_v,
|
||||
float3 inv_quad_u,
|
||||
float3 inv_quad_v,
|
||||
float3 quad_n,
|
||||
ccl_private float3 *isect_P,
|
||||
ccl_private float *isect_t,
|
||||
|
@ -273,11 +273,11 @@ ccl_device bool ray_quad_intersect(float3 ray_P,
|
|||
}
|
||||
const float3 hit = ray_P + t * ray_D;
|
||||
const float3 inplane = hit - quad_P;
|
||||
const float u = dot(inplane, quad_u) / dot(quad_u, quad_u);
|
||||
const float u = dot(inplane, inv_quad_u);
|
||||
if (u < -0.5f || u > 0.5f) {
|
||||
return false;
|
||||
}
|
||||
const float v = dot(inplane, quad_v) / dot(quad_v, quad_v);
|
||||
const float v = dot(inplane, inv_quad_v);
|
||||
if (v < -0.5f || v > 0.5f) {
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue