Cycles: Change sun lamp to have uniform intensity at high angles #108996
|
@ -14,24 +14,15 @@ ccl_device_inline bool distant_light_sample(const ccl_global KernelLight *klight
|
||||||
const float2 rand,
|
const float2 rand,
|
||||||
ccl_private LightSample *ls)
|
ccl_private LightSample *ls)
|
||||||
{
|
{
|
||||||
/* distant light */
|
float unused;
|
||||||
float3 lightD = klight->co;
|
sample_uniform_cone_concentric(
|
||||||
float3 D = lightD;
|
klight->co, klight->distant.one_minus_cosangle, rand, &unused, &ls->Ng, &ls->pdf);
|
||||||
float radius = klight->distant.radius;
|
|
||||||
float invarea = klight->distant.invarea;
|
|
||||||
|
|
||||||
if (radius > 0.0f) {
|
ls->P = ls->Ng;
|
||||||
D = normalize(D + disk_light_sample(D, rand) * radius);
|
ls->D = -ls->Ng;
|
||||||
}
|
|
||||||
|
|
||||||
ls->P = D;
|
|
||||||
ls->Ng = D;
|
|
||||||
ls->D = -D;
|
|
||||||
ls->t = FLT_MAX;
|
ls->t = FLT_MAX;
|
||||||
|
|
||||||
float costheta = dot(lightD, D);
|
ls->eval_fac = klight->distant.eval_fac;
|
||||||
ls->pdf = invarea / (costheta * costheta * costheta);
|
|
||||||
ls->eval_fac = ls->pdf;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -50,15 +41,11 @@ ccl_device bool distant_light_intersect(const ccl_global KernelLight *klight,
|
||||||
{
|
{
|
||||||
kernel_assert(klight->type == LIGHT_DISTANT);
|
kernel_assert(klight->type == LIGHT_DISTANT);
|
||||||
|
|
||||||
if (klight->distant.radius == 0.0f) {
|
if (klight->distant.angle == 0.0f) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float3 lightD = klight->co;
|
if (vector_angle(-klight->co, ray->D) > klight->distant.angle) {
|
||||||
const float costheta = dot(-lightD, ray->D);
|
|
||||||
const float cosangle = klight->distant.cosangle;
|
|
||||||
|
|
||||||
if (costheta < cosangle) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +63,6 @@ ccl_device bool distant_light_sample_from_intersection(KernelGlobals kg,
|
||||||
{
|
{
|
||||||
ccl_global const KernelLight *klight = &kernel_data_fetch(lights, lamp);
|
ccl_global const KernelLight *klight = &kernel_data_fetch(lights, lamp);
|
||||||
const int shader = klight->shader_id;
|
const int shader = klight->shader_id;
|
||||||
const float radius = klight->distant.radius;
|
|
||||||
const LightType type = (LightType)klight->type;
|
const LightType type = (LightType)klight->type;
|
||||||
|
|
||||||
if (type != LIGHT_DISTANT) {
|
if (type != LIGHT_DISTANT) {
|
||||||
|
@ -85,37 +71,19 @@ ccl_device bool distant_light_sample_from_intersection(KernelGlobals kg,
|
||||||
if (!(shader & SHADER_USE_MIS)) {
|
if (!(shader & SHADER_USE_MIS)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (radius == 0.0f) {
|
if (klight->distant.angle == 0.0f) {
|
||||||
return false;
|
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,
|
/* Workaround to prevent a hang in the classroom scene with AMD HIP drivers 22.10,
|
||||||
* Remove when a compiler fix is available. */
|
* Remove when a compiler fix is available. */
|
||||||
#ifdef __HIP__
|
#ifdef __HIP__
|
||||||
ls->shader = klight->shader_id;
|
ls->shader = klight->shader_id;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (costheta < cosangle)
|
if (vector_angle(-klight->co, ray_D) > klight->distant.angle) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ls->type = type;
|
ls->type = type;
|
||||||
#ifndef __HIP__
|
#ifndef __HIP__
|
||||||
|
@ -133,10 +101,8 @@ ccl_device bool distant_light_sample_from_intersection(KernelGlobals kg,
|
||||||
ls->D = ray_D;
|
ls->D = ray_D;
|
||||||
ls->group = lamp_lightgroup(kg, lamp);
|
ls->group = lamp_lightgroup(kg, lamp);
|
||||||
|
|
||||||
/* compute pdf */
|
ls->pdf = klight->distant.pdf;
|
||||||
float invarea = klight->distant.invarea;
|
ls->eval_fac = klight->distant.eval_fac;
|
||||||
ls->pdf = invarea / (costheta * costheta * costheta);
|
|
||||||
ls->eval_fac = ls->pdf;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,14 +133,35 @@ ccl_device_inline void sample_uniform_cone_concentric(const float3 N,
|
||||||
ccl_private float *pdf)
|
ccl_private float *pdf)
|
||||||
{
|
{
|
||||||
if (one_minus_cos_angle > 0) {
|
if (one_minus_cos_angle > 0) {
|
||||||
/* Map random number from 2D to 1D. */
|
/* Remap radius to get a uniform distribution w.r.t. solid angle on the cone.
|
||||||
|
* The logic to derive this mapping is as follows:
|
||||||
|
*
|
||||||
|
* Sampling a cone is comparable to sampling the hemisphere, we just restrict theta. Therefore,
|
||||||
|
* the same trick of first sampling the unit disk and the projecting the result up towards the
|
||||||
|
* hemisphere by calculating the appropriate z coordinate still works.
|
||||||
|
*
|
||||||
|
* However, by itself this results in cosine-weighted hemisphere sampling, so we need some kind
|
||||||
|
* of remapping. Cosine-weighted hemisphere and uniform cone sampling have the same conditional
|
||||||
|
* PDF for phi (both are constant), so we only need to think about theta, which corresponds
|
||||||
|
* directly to the radius.
|
||||||
|
*
|
||||||
|
* To find this mapping, we consider the simplest sampling strategies for cosine-weighted
|
||||||
|
* hemispheres and uniform cones. In both, phi is chosen as 2pi * random(). For the former,
|
||||||
|
* r_disk(rand) = sqrt(rand). This is just naive disk sampling, since the projection to the
|
||||||
|
* hemisphere doesn't change the radius. For the latter, r_cone(rand) =
|
||||||
|
* sin_from_cos(mix(cos_angle, 1, rand)).
|
||||||
|
*
|
||||||
|
* So, to remap, we just invert r_disk (-> rand(r_disk) = r_disk^2) and insert it into r_cone:
|
||||||
|
* r_cone(r_disk) = r_cone(rand(r_disk)) = sin_from_cos(mix(cos_angle, 1, r_disk^2)). In
|
||||||
|
* practise, we need to replace `rand` with `1 - rand` to preserve the stratification, but
|
||||||
|
* since it's uniform, that's fine. */
|
||||||
float2 xy = concentric_sample_disk(rand);
|
float2 xy = concentric_sample_disk(rand);
|
||||||
const float r2 = len_squared(xy);
|
const float r2 = len_squared(xy);
|
||||||
|
|
||||||
/* Equivalent to `mix(cos_angle, 1.0f, 1.0f - r2)` */
|
/* Equivalent to `mix(cos_angle, 1.0f, 1.0f - r2)` */
|
||||||
*cos_theta = 1.0f - r2 * one_minus_cos_angle;
|
*cos_theta = 1.0f - r2 * one_minus_cos_angle;
|
||||||
|
|
||||||
/* Equivalent to `xy *= sin_theta / sqrt(r2); */
|
/* Remap disk radius to cone radius, equivalent to `xy *= sin_theta / sqrt(r2); */
|
||||||
xy *= safe_sqrtf(one_minus_cos_angle * (2.0f - one_minus_cos_angle * r2));
|
xy *= safe_sqrtf(one_minus_cos_angle * (2.0f - one_minus_cos_angle * r2));
|
||||||
|
|
||||||
float3 T, B;
|
float3 T, B;
|
||||||
|
|
|
@ -1375,10 +1375,10 @@ typedef struct KernelAreaLight {
|
||||||
} KernelAreaLight;
|
} KernelAreaLight;
|
||||||
|
|
||||||
typedef struct KernelDistantLight {
|
typedef struct KernelDistantLight {
|
||||||
float radius;
|
float angle;
|
||||||
float cosangle;
|
float one_minus_cosangle;
|
||||||
float invarea;
|
float pdf;
|
||||||
float pad;
|
float eval_fac;
|
||||||
} KernelDistantLight;
|
} KernelDistantLight;
|
||||||
|
|
||||||
typedef struct KernelLight {
|
typedef struct KernelLight {
|
||||||
|
|
|
@ -1230,22 +1230,23 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
|
||||||
else if (light->light_type == LIGHT_DISTANT) {
|
else if (light->light_type == LIGHT_DISTANT) {
|
||||||
shader_id &= ~SHADER_AREA_LIGHT;
|
shader_id &= ~SHADER_AREA_LIGHT;
|
||||||
|
|
||||||
|
float3 dir = safe_normalize(light->dir);
|
||||||
float angle = light->angle / 2.0f;
|
float angle = light->angle / 2.0f;
|
||||||
float radius = tanf(angle);
|
|
||||||
float cosangle = cosf(angle);
|
|
||||||
float area = M_PI_F * radius * radius;
|
|
||||||
float invarea = (light->normalize && area > 0.0f) ? 1.0f / area : 1.0f;
|
|
||||||
float3 dir = light->dir;
|
|
||||||
|
|
||||||
dir = safe_normalize(dir);
|
if (light->use_mis && angle > 0.0f) {
|
||||||
|
|
||||||
if (light->use_mis && area > 0.0f)
|
|
||||||
shader_id |= SHADER_USE_MIS;
|
shader_id |= SHADER_USE_MIS;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float one_minus_cosangle = 2.0f * sqr(sinf(0.5f * angle));
|
||||||
|
const float pdf = (angle > 0.0f) ? (M_1_2PI_F / one_minus_cosangle) : 1.0f;
|
||||||
|
|
||||||
klights[light_index].co = dir;
|
klights[light_index].co = dir;
|
||||||
klights[light_index].distant.invarea = invarea;
|
klights[light_index].distant.angle = angle;
|
||||||
klights[light_index].distant.radius = radius;
|
klights[light_index].distant.one_minus_cosangle = one_minus_cosangle;
|
||||||
klights[light_index].distant.cosangle = cosangle;
|
klights[light_index].distant.pdf = pdf;
|
||||||
|
klights[light_index].distant.eval_fac = (light->normalize && angle > 0) ?
|
||||||
|
M_1_PI_F / sqr(sinf(angle)) :
|
||||||
|
1.0f;
|
||||||
}
|
}
|
||||||
else if (light->light_type == LIGHT_BACKGROUND) {
|
else if (light->light_type == LIGHT_BACKGROUND) {
|
||||||
uint visibility = scene->background->get_visibility();
|
uint visibility = scene->background->get_visibility();
|
||||||
|
|
|
@ -336,6 +336,12 @@ ccl_device float fast_atan2f(float y, float x)
|
||||||
return copysignf(r, y);
|
return copysignf(r, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Same as precise_angle, but using fast_atan2f. Still much better that acos(dot(a, b)). */
|
||||||
|
ccl_device_inline float vector_angle(float3 a, float3 b)
|
||||||
|
{
|
||||||
|
return 2.0f * fast_atan2f(len(a - b), len(a + b));
|
||||||
|
}
|
||||||
|
|
||||||
/* Based on:
|
/* Based on:
|
||||||
*
|
*
|
||||||
* https://github.com/LiraNuna/glsl-sse2/blob/master/source/vec4.h
|
* https://github.com/LiraNuna/glsl-sse2/blob/master/source/vec4.h
|
||||||
|
|
Loading…
Reference in New Issue