@@ -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);
0 commit comments