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,
|
||||
ccl_private LightSample *ls)
|
||||
{
|
||||
/* distant light */
|
||||
float3 lightD = klight->co;
|
||||
float3 D = lightD;
|
||||
float radius = klight->distant.radius;
|
||||
float invarea = klight->distant.invarea;
|
||||
float unused;
|
||||
sample_uniform_cone_concentric(
|
||||
klight->co, klight->distant.one_minus_cosangle, rand, &unused, &ls->Ng, &ls->pdf);
|
||||
|
||||
if (radius > 0.0f) {
|
||||
D = normalize(D + disk_light_sample(D, rand) * radius);
|
||||
}
|
||||
|
||||
ls->P = D;
|
||||
ls->Ng = D;
|
||||
ls->D = -D;
|
||||
ls->P = ls->Ng;
|
||||
ls->D = -ls->Ng;
|
||||
ls->t = FLT_MAX;
|
||||
|
||||
float costheta = dot(lightD, D);
|
||||
ls->pdf = invarea / (costheta * costheta * costheta);
|
||||
ls->eval_fac = ls->pdf;
|
||||
ls->eval_fac = klight->distant.eval_fac;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -50,15 +41,11 @@ ccl_device bool distant_light_intersect(const ccl_global KernelLight *klight,
|
||||
{
|
||||
kernel_assert(klight->type == LIGHT_DISTANT);
|
||||
|
||||
if (klight->distant.radius == 0.0f) {
|
||||
if (klight->distant.angle == 0.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const float3 lightD = klight->co;
|
||||
const float costheta = dot(-lightD, ray->D);
|
||||
const float cosangle = klight->distant.cosangle;
|
||||
|
||||
if (costheta < cosangle) {
|
||||
if (vector_angle(-klight->co, ray->D) > klight->distant.angle) {
|
||||
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);
|
||||
const int shader = klight->shader_id;
|
||||
const float radius = klight->distant.radius;
|
||||
const LightType type = (LightType)klight->type;
|
||||
|
||||
if (type != LIGHT_DISTANT) {
|
||||
@ -85,37 +71,19 @@ ccl_device bool distant_light_sample_from_intersection(KernelGlobals kg,
|
||||
if (!(shader & SHADER_USE_MIS)) {
|
||||
return false;
|
||||
}
|
||||
if (radius == 0.0f) {
|
||||
if (klight->distant.angle == 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)
|
||||
if (vector_angle(-klight->co, ray_D) > klight->distant.angle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ls->type = type;
|
||||
#ifndef __HIP__
|
||||
@ -133,10 +101,8 @@ ccl_device bool distant_light_sample_from_intersection(KernelGlobals kg,
|
||||
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 = klight->distant.pdf;
|
||||
ls->eval_fac = klight->distant.eval_fac;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -133,14 +133,35 @@ ccl_device_inline void sample_uniform_cone_concentric(const float3 N,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
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);
|
||||
const float r2 = len_squared(xy);
|
||||
|
||||
/* Equivalent to `mix(cos_angle, 1.0f, 1.0f - r2)` */
|
||||
*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));
|
||||
|
||||
float3 T, B;
|
||||
|
@ -1375,10 +1375,10 @@ typedef struct KernelAreaLight {
|
||||
} KernelAreaLight;
|
||||
|
||||
typedef struct KernelDistantLight {
|
||||
float radius;
|
||||
float cosangle;
|
||||
float invarea;
|
||||
float pad;
|
||||
float angle;
|
||||
float one_minus_cosangle;
|
||||
float pdf;
|
||||
float eval_fac;
|
||||
} KernelDistantLight;
|
||||
|
||||
typedef struct KernelLight {
|
||||
|
@ -1230,22 +1230,23 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
|
||||
else if (light->light_type == LIGHT_DISTANT) {
|
||||
shader_id &= ~SHADER_AREA_LIGHT;
|
||||
|
||||
float3 dir = safe_normalize(light->dir);
|
||||
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 && area > 0.0f)
|
||||
if (light->use_mis && angle > 0.0f) {
|
||||
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].distant.invarea = invarea;
|
||||
klights[light_index].distant.radius = radius;
|
||||
klights[light_index].distant.cosangle = cosangle;
|
||||
klights[light_index].distant.angle = angle;
|
||||
klights[light_index].distant.one_minus_cosangle = one_minus_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) {
|
||||
uint visibility = scene->background->get_visibility();
|
||||
|
@ -336,6 +336,12 @@ ccl_device float fast_atan2f(float y, float x)
|
||||
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:
|
||||
*
|
||||
* https://github.com/LiraNuna/glsl-sse2/blob/master/source/vec4.h
|
||||
|
Loading…
Reference in New Issue
Block a user