Skip to content

Commit 082e744

Browse files
committed
Spread angle parameter for area lights
1 parent 5561f39 commit 082e744

File tree

58 files changed

+414
-198
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+414
-198
lines changed

SceneBase.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ struct spot_light_desc_t {
233233
struct rect_light_desc_t {
234234
float color[3] = {1.0f, 1.0f, 1.0f};
235235
float width = 1.0f, height = 1.0f;
236+
float spread_angle = 180.0f;
236237
bool doublesided = false;
237238
bool sky_portal = false;
238239
bool multiple_importance = true; ///< Use combination of explicit and implicit light sampling
@@ -246,6 +247,7 @@ struct rect_light_desc_t {
246247
struct disk_light_desc_t {
247248
float color[3] = {1.0f, 1.0f, 1.0f};
248249
float size_x = 1.0f, size_y = 1.0f;
250+
float spread_angle = 180.0f;
249251
bool doublesided = false;
250252
bool sky_portal = false;
251253
bool multiple_importance = true; ///< Use combination of explicit and implicit light sampling

internal/Core.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,13 @@ struct light_t {
208208
} sph;
209209
struct {
210210
float pos[3], area;
211-
float u[3], _unused0;
212-
float v[3], _unused1;
211+
float u[3], tan_half_spread;
212+
float v[3], spread_normalization;
213213
} rect;
214214
struct {
215215
float pos[3], area;
216-
float u[3], _unused0;
217-
float v[3], _unused1;
216+
float u[3], tan_half_spread;
217+
float v[3], spread_normalization;
218218
} disk;
219219
struct {
220220
float pos[3], area;

internal/CoreRef.cpp

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,6 +1279,14 @@ force_inline float angle_between(const fvec4 &v1, const fvec4 &v2) {
12791279
}
12801280
}
12811281

1282+
force_inline float tan_angle(const fvec4 &a, const fvec4 &b) { return safe_div(length(cross(a, b)), dot(a, b)); }
1283+
1284+
float spread_attenuation(const fvec4 &D, const fvec4 &light_fwd, const float tan_half_spread,
1285+
const float spread_normalization) {
1286+
const float tan_a = tan_angle(-D, light_fwd);
1287+
return fmaxf((tan_half_spread - tan_a) * spread_normalization, 0.0f);
1288+
}
1289+
12821290
} // namespace Ref
12831291
} // namespace Ray
12841292

@@ -3422,6 +3430,9 @@ void Ray::Ref::SampleLightSource(const fvec4 &P, const fvec4 &T, const fvec4 &B,
34223430
ls.col *= env_col;
34233431
ls.from_env = 1;
34243432
}
3433+
if (l.rect.spread_normalization > 0.0f) {
3434+
ls.col *= spread_attenuation(ls.L, light_forward, l.rect.tan_half_spread, l.rect.spread_normalization);
3435+
}
34253436
}
34263437
} else if (l.type == LIGHT_TYPE_DISK) {
34273438
const fvec4 light_pos = make_fvec3(l.disk.pos);
@@ -3473,6 +3484,9 @@ void Ray::Ref::SampleLightSource(const fvec4 &P, const fvec4 &T, const fvec4 &B,
34733484
ls.col *= env_col;
34743485
ls.from_env = 1;
34753486
}
3487+
if (l.disk.spread_normalization > 0.0f) {
3488+
ls.col *= spread_attenuation(ls.L, light_forward, l.disk.tan_half_spread, l.disk.spread_normalization);
3489+
}
34763490
} else if (l.type == LIGHT_TYPE_LINE) {
34773491
const fvec4 light_pos = make_fvec3(l.line.pos);
34783492
const fvec4 light_dir = make_fvec3(l.line.v);
@@ -3622,9 +3636,7 @@ void Ray::Ref::IntersectAreaLights(Span<const ray_data_t> rays, Span<const light
36223636
const ray_data_t &ray = rays[_i];
36233637
hit_data_t &inout_inter = inout_inters[_i];
36243638

3625-
const fvec4 ro = make_fvec3(ray.o);
3626-
const fvec4 rd = make_fvec3(ray.d);
3627-
3639+
const fvec4 ro = make_fvec3(ray.o), rd = make_fvec3(ray.d);
36283640
const uint32_t ray_flags = (1u << get_ray_type(ray.depth));
36293641

36303642
float inv_d[3];
@@ -3793,10 +3805,12 @@ void Ray::Ref::IntersectAreaLights(Span<const ray_data_t> rays, Span<const light
37933805
if (a1 >= -0.5f && a1 <= 0.5f) {
37943806
const float a2 = dot(light_v, vi);
37953807
if (a2 >= -0.5f && a2 <= 0.5f) {
3796-
inout_inter.v = 0.0f;
3797-
inout_inter.obj_index = -int(light_index) - 1;
3798-
inout_inter.t = t;
3799-
inout_inter.u = cur.factor;
3808+
if (tan_angle(-rd, light_forward) < l.rect.tan_half_spread) {
3809+
inout_inter.v = 0.0f;
3810+
inout_inter.obj_index = -int(light_index) - 1;
3811+
inout_inter.t = t;
3812+
inout_inter.u = cur.factor;
3813+
}
38003814
}
38013815
}
38023816
}
@@ -3819,7 +3833,8 @@ void Ray::Ref::IntersectAreaLights(Span<const ray_data_t> rays, Span<const light
38193833
const float a1 = dot(light_u, vi);
38203834
const float a2 = dot(light_v, vi);
38213835

3822-
if (sqrtf(a1 * a1 + a2 * a2) <= 0.5f) {
3836+
if (sqrtf(a1 * a1 + a2 * a2) <= 0.5f &&
3837+
tan_angle(-rd, light_forward) < l.disk.tan_half_spread) {
38233838
inout_inter.v = 0.0f;
38243839
inout_inter.obj_index = -int(light_index) - 1;
38253840
inout_inter.t = t;
@@ -3868,9 +3883,7 @@ void Ray::Ref::IntersectAreaLights(Span<const ray_data_t> rays, Span<const light
38683883
const ray_data_t &ray = rays[_i];
38693884
hit_data_t &inout_inter = inout_inters[_i];
38703885

3871-
const fvec4 ro = make_fvec3(ray.o);
3872-
const fvec4 rd = make_fvec3(ray.d);
3873-
3886+
const fvec4 ro = make_fvec3(ray.o), rd = make_fvec3(ray.d);
38743887
const uint32_t ray_flags = (1u << get_ray_type(ray.depth));
38753888

38763889
float inv_d[3];
@@ -4114,9 +4127,7 @@ void Ray::Ref::IntersectAreaLights(Span<const ray_data_t> rays, Span<const light
41144127
const ray_data_t &ray = rays[i];
41154128
hit_data_t &inout_inter = inout_inters[i];
41164129

4117-
const fvec4 ro = make_fvec3(ray.o);
4118-
const fvec4 rd = make_fvec3(ray.d);
4119-
4130+
const fvec4 ro = make_fvec3(ray.o), rd = make_fvec3(ray.d);
41204131
const uint32_t ray_flags = (1u << get_ray_type(ray.depth));
41214132

41224133
float inv_d[3];
@@ -4311,9 +4322,7 @@ void Ray::Ref::IntersectAreaLights(Span<const ray_data_t> rays, Span<const light
43114322
float Ray::Ref::IntersectAreaLights(const shadow_ray_t &ray, Span<const light_t> lights,
43124323
Span<const light_wbvh_node_t> nodes) {
43134324
const float rdist = fabsf(ray.dist);
4314-
4315-
const fvec4 ro = make_fvec3(ray.o);
4316-
const fvec4 rd = make_fvec3(ray.d);
4325+
const fvec4 ro = make_fvec3(ray.o), rd = make_fvec3(ray.d);
43174326

43184327
float inv_d[3];
43194328
safe_invert(value_ptr(rd), inv_d);
@@ -4454,9 +4463,7 @@ float Ray::Ref::IntersectAreaLights(const shadow_ray_t &ray, Span<const light_t>
44544463
float Ray::Ref::IntersectAreaLights(const shadow_ray_t &ray, Span<const light_t> lights,
44554464
Span<const light_cwbvh_node_t> nodes) {
44564465
const float rdist = fabsf(ray.dist);
4457-
4458-
const fvec4 ro = make_fvec3(ray.o);
4459-
const fvec4 rd = make_fvec3(ray.d);
4466+
const fvec4 ro = make_fvec3(ray.o), rd = make_fvec3(ray.d);
44604467

44614468
float inv_d[3];
44624469
safe_invert(value_ptr(rd), inv_d);

internal/CoreSIMD.h

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2851,6 +2851,34 @@ template <int S> uvec<S> calc_grid_level(const fvec<S> p[3], const cache_grid_pa
28512851
return uvec<S>(ret);
28522852
}
28532853

2854+
template <int S> fvec<S> tan_angle(const fvec<S> a[3], const fvec<S> b[3]) {
2855+
fvec<S> temp[3];
2856+
cross(a, b, temp);
2857+
return safe_div(length(temp), dot3(a, b));
2858+
}
2859+
2860+
template <int S> fvec<S> tan_angle(const fvec<S> a[3], const float b[3]) {
2861+
fvec<S> temp[3];
2862+
cross(a, b, temp);
2863+
return safe_div(length(temp), dot3(a, b));
2864+
}
2865+
2866+
template <int S>
2867+
fvec<S> spread_attenuation(const fvec<S> D[3], const fvec<S> light_fwd[3], const float tan_half_spread,
2868+
const float spread_normalization) {
2869+
const fvec<S> temp[3] = {-D[0], -D[1], -D[2]};
2870+
const fvec<S> tan_a = tan_angle(temp, light_fwd);
2871+
return max((tan_half_spread - tan_a) * spread_normalization, 0.0f);
2872+
}
2873+
2874+
template <int S>
2875+
fvec<S> spread_attenuation(const fvec<S> D[3], const float light_fwd[3], const float tan_half_spread,
2876+
const float spread_normalization) {
2877+
const fvec<S> temp[3] = {-D[0], -D[1], -D[2]};
2878+
const fvec<S> tan_a = tan_angle(temp, light_fwd);
2879+
return max((tan_half_spread - tan_a) * spread_normalization, 0.0f);
2880+
}
2881+
28542882
} // namespace NS
28552883
} // namespace Ray
28562884

@@ -5683,6 +5711,11 @@ void Ray::NS::SampleLightSource(const fvec<S> P[3], const fvec<S> T[3], const fv
56835711
UNROLLED_FOR(i, 3, { where(ray_queue[index], ls.col[i]) *= sc.env.env_col[i]; })
56845712
where(ray_queue[index], ls.from_env) = -1;
56855713
}
5714+
if (l.rect.spread_normalization > 0.0f) {
5715+
const fvec<S> att =
5716+
spread_attenuation(ls.L, _light_forward, l.rect.tan_half_spread, l.rect.spread_normalization);
5717+
UNROLLED_FOR(i, 3, { where(ray_queue[index], ls.col[i]) *= att; })
5718+
}
56865719
} else if (l.type == LIGHT_TYPE_DISK) {
56875720
fvec<S> offset[2] = {2.0f * rand_light_uv[0] - 1.0f, 2.0f * rand_light_uv[1] - 1.0f};
56885721
const ivec<S> mask = simd_cast(offset[0] != 0.0f & offset[1] != 0.0f);
@@ -5740,6 +5773,11 @@ void Ray::NS::SampleLightSource(const fvec<S> P[3], const fvec<S> T[3], const fv
57405773
UNROLLED_FOR(i, 3, { where(ray_queue[index], ls.col[i]) *= sc.env.env_col[i]; })
57415774
where(ray_queue[index], ls.from_env) = -1;
57425775
}
5776+
if (l.disk.spread_normalization > 0.0f) {
5777+
const fvec<S> att =
5778+
spread_attenuation(ls.L, _light_forward, l.disk.tan_half_spread, l.disk.spread_normalization);
5779+
UNROLLED_FOR(i, 3, { where(ray_queue[index], ls.col[i]) *= att; })
5780+
}
57435781
} else if (l.type == LIGHT_TYPE_LINE) {
57445782
fvec<S> center_to_surface[3];
57455783
UNROLLED_FOR(i, 3, { center_to_surface[i] = P[i] - l.line.pos[i]; })
@@ -6158,8 +6196,11 @@ void Ray::NS::IntersectAreaLights(const ray_data_t<S> &r, Span<const light_t> li
61586196
const fvec<S> a1 = dot3(l.rect.u, vi) / dot_u;
61596197
const fvec<S> a2 = dot3(l.rect.v, vi) / dot_v;
61606198

6161-
const fvec<S> final_mask =
6162-
(a1 >= -0.5f & a1 <= 0.5f) & (a2 >= -0.5f & a2 <= 0.5f) & simd_cast(imask);
6199+
const fvec<S> temp[3] = {-r.d[0], -r.d[1], -r.d[2]};
6200+
const fvec<S> tan_angl = tan_angle(temp, light_fwd);
6201+
6202+
const fvec<S> final_mask = (a1 >= -0.5f & a1 <= 0.5f) & (a2 >= -0.5f & a2 <= 0.5f) &
6203+
(tan_angl < l.rect.tan_half_spread) & simd_cast(imask);
61636204

61646205
where(final_mask, inout_inter.v) = 0.0f;
61656206
where(final_mask, inout_inter.obj_index) = -ivec<S>(light_index) - 1;
@@ -6190,7 +6231,11 @@ void Ray::NS::IntersectAreaLights(const ray_data_t<S> &r, Span<const light_t> li
61906231
const fvec<S> a1 = dot3(l.disk.u, vi) / dot_u;
61916232
const fvec<S> a2 = dot3(l.disk.v, vi) / dot_v;
61926233

6193-
const fvec<S> final_mask = (sqrt(a1 * a1 + a2 * a2) <= 0.5f) & simd_cast(imask);
6234+
const fvec<S> temp[3] = {-r.d[0], -r.d[1], -r.d[2]};
6235+
const fvec<S> tan_angl = tan_angle(temp, light_fwd);
6236+
6237+
const fvec<S> final_mask =
6238+
(sqrt(a1 * a1 + a2 * a2) <= 0.5f) & (tan_angl < l.disk.tan_half_spread) & simd_cast(imask);
61946239

61956240
where(final_mask, inout_inter.v) = 0.0f;
61966241
where(final_mask, inout_inter.obj_index) = -ivec<S>(light_index) - 1;
@@ -6385,8 +6430,11 @@ Ray::NS::fvec<S> Ray::NS::IntersectAreaLights(const shadow_ray_t<S> &r, Span<con
63856430
const fvec<S> a1 = dot3(l.rect.u, vi) / dot_u;
63866431
const fvec<S> a2 = dot3(l.rect.v, vi) / dot_v;
63876432

6388-
const fvec<S> final_mask =
6389-
(a1 >= -0.5f & a1 <= 0.5f) & (a2 >= -0.5f & a2 <= 0.5f) & simd_cast(imask);
6433+
const fvec<S> temp[3] = {-r.d[0], -r.d[1], -r.d[2]};
6434+
const fvec<S> tan_angl = tan_angle(temp, light_fwd);
6435+
6436+
const fvec<S> final_mask = (a1 >= -0.5f & a1 <= 0.5f) & (a2 >= -0.5f & a2 <= 0.5f) &
6437+
(tan_angl < l.rect.tan_half_spread) & simd_cast(imask);
63906438

63916439
ray_mask &= ~simd_cast(final_mask);
63926440
where(final_mask, ret) = 0.0f;
@@ -6415,7 +6463,11 @@ Ray::NS::fvec<S> Ray::NS::IntersectAreaLights(const shadow_ray_t<S> &r, Span<con
64156463
const fvec<S> a1 = dot3(l.disk.u, vi) / dot_u;
64166464
const fvec<S> a2 = dot3(l.disk.v, vi) / dot_v;
64176465

6418-
const fvec<S> final_mask = (sqrt(a1 * a1 + a2 * a2) <= 0.5f) & simd_cast(imask);
6466+
const fvec<S> temp[3] = {-r.d[0], -r.d[1], -r.d[2]};
6467+
const fvec<S> tan_angl = tan_angle(temp, light_fwd);
6468+
6469+
const fvec<S> final_mask =
6470+
(sqrt(a1 * a1 + a2 * a2) <= 0.5f) & (tan_angl < l.disk.tan_half_spread) & simd_cast(imask);
64196471

64206472
ray_mask &= ~simd_cast(final_mask);
64216473
where(final_mask, ret) = 0.0f;
@@ -6654,6 +6706,12 @@ void Ray::NS::Evaluate_LightColor(const fvec<S> P[3], const ray_data_t<S> &ray,
66546706
cross(l.rect.u, l.rect.v, light_fwd);
66556707
normalize(light_fwd);
66566708

6709+
if (l.rect.spread_normalization > 0.0f) {
6710+
const fvec<S> att =
6711+
spread_attenuation(ray.d, light_fwd, l.rect.tan_half_spread, l.rect.spread_normalization);
6712+
UNROLLED_FOR(i, 3, { lcol[i] *= att; })
6713+
}
6714+
66576715
const fvec<S> cos_theta = dot3(ray.d, light_fwd);
66586716

66596717
fvec<S> light_pdf = 0.0f;
@@ -6673,6 +6731,12 @@ void Ray::NS::Evaluate_LightColor(const fvec<S> P[3], const ray_data_t<S> &ray,
66736731
cross(l.disk.u, l.disk.v, light_fwd);
66746732
normalize(light_fwd);
66756733

6734+
if (l.disk.spread_normalization > 0.0f) {
6735+
const fvec<S> att =
6736+
spread_attenuation(ray.d, light_fwd, l.disk.tan_half_spread, l.disk.spread_normalization);
6737+
UNROLLED_FOR(i, 3, { lcol[i] *= att; })
6738+
}
6739+
66766740
const fvec<S> cos_theta = dot3(ray.d, light_fwd);
66776741

66786742
const fvec<S> light_pdf = safe_div(inter.t * inter.t, l.disk.area * cos_theta * pdf_factor);

internal/SceneCPU.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,16 @@ Ray::LightHandle Ray::Cpu::Scene::AddLight(const rect_light_desc_t &_l, const fl
691691
memcpy(l.rect.u, value_ptr(uvec), 3 * sizeof(float));
692692
memcpy(l.rect.v, value_ptr(vvec), 3 * sizeof(float));
693693

694+
l.rect.tan_half_spread = FLT_MAX;
695+
l.rect.spread_normalization = 0.0f;
696+
if (!_l.sky_portal && _l.spread_angle < 180.0f) {
697+
const float half_spread = fmaxf(0.5f * _l.spread_angle * PI / 180.0f, 0.1f);
698+
l.rect.tan_half_spread = tanf(half_spread);
699+
700+
// Integrate cos(x) * (1 - tan(x) / tan(half_spread)) * sin(x) from x = 0 to half_spread
701+
l.rect.spread_normalization = 1.0f / (l.rect.tan_half_spread - half_spread);
702+
}
703+
694704
std::unique_lock<std::shared_timed_mutex> lock(mtx_);
695705

696706
const std::pair<uint32_t, uint32_t> light_index = lights_.push(l);
@@ -726,6 +736,16 @@ Ray::LightHandle Ray::Cpu::Scene::AddLight(const disk_light_desc_t &_l, const fl
726736
memcpy(l.disk.u, value_ptr(uvec), 3 * sizeof(float));
727737
memcpy(l.disk.v, value_ptr(vvec), 3 * sizeof(float));
728738

739+
l.disk.tan_half_spread = FLT_MAX;
740+
l.disk.spread_normalization = 0.0f;
741+
if (!_l.sky_portal && _l.spread_angle < 180.0f) {
742+
const float half_spread = fmaxf(0.5f * _l.spread_angle * PI / 180.0f, 0.1f);
743+
l.disk.tan_half_spread = tanf(half_spread);
744+
745+
// Integrate cos(x) * (1 - tan(x) / tan(half_spread)) * sin(x) from x = 0 to half_spread
746+
l.disk.spread_normalization = 1.0f / (l.disk.tan_half_spread - half_spread);
747+
}
748+
729749
std::unique_lock<std::shared_timed_mutex> lock(mtx_);
730750

731751
const std::pair<uint32_t, uint32_t> light_index = lights_.push(l);

internal/SceneGPU.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,16 @@ inline Ray::LightHandle Ray::NS::Scene::AddLight(const rect_light_desc_t &_l, co
12851285
memcpy(l.rect.u, value_ptr(uvec), 3 * sizeof(float));
12861286
memcpy(l.rect.v, value_ptr(vvec), 3 * sizeof(float));
12871287

1288+
l.rect.tan_half_spread = FLT_MAX;
1289+
l.rect.spread_normalization = 0.0f;
1290+
if (!_l.sky_portal && _l.spread_angle < 180.0f) {
1291+
const float half_spread = fmaxf(0.5f * _l.spread_angle * PI / 180.0f, 0.1f);
1292+
l.rect.tan_half_spread = tanf(half_spread);
1293+
1294+
// Integrate cos(x) * (1 - tan(x) / tan(half_spread)) * sin(x) from x = 0 to half_spread
1295+
l.rect.spread_normalization = 1.0f / (l.rect.tan_half_spread - half_spread);
1296+
}
1297+
12881298
std::unique_lock<std::shared_timed_mutex> lock(mtx_);
12891299

12901300
const std::pair<uint32_t, uint32_t> light_index = lights_.push(l);
@@ -1320,6 +1330,16 @@ inline Ray::LightHandle Ray::NS::Scene::AddLight(const disk_light_desc_t &_l, co
13201330
memcpy(l.disk.u, value_ptr(uvec), 3 * sizeof(float));
13211331
memcpy(l.disk.v, value_ptr(vvec), 3 * sizeof(float));
13221332

1333+
l.disk.tan_half_spread = FLT_MAX;
1334+
l.disk.spread_normalization = 0.0f;
1335+
if (!_l.sky_portal && _l.spread_angle < 180.0f) {
1336+
const float half_spread = fmaxf(0.5f * _l.spread_angle * PI / 180.0f, 0.1f);
1337+
l.disk.tan_half_spread = tanf(half_spread);
1338+
1339+
// Integrate cos(x) * (1 - tan(x) / tan(half_spread)) * sin(x) from x = 0 to half_spread
1340+
l.disk.spread_normalization = 1.0f / (l.disk.tan_half_spread - half_spread);
1341+
}
1342+
13231343
std::unique_lock<std::shared_timed_mutex> lock(mtx_);
13241344

13251345
const std::pair<uint32_t, uint32_t> light_index = lights_.push(l);

0 commit comments

Comments
 (0)