66#define _NBL_BUILTIN_HLSL_IES_SAMPLER_INCLUDED_
77
88#include "nbl/builtin/hlsl/cpp_compat.hlsl"
9+ #include "nbl/builtin/hlsl/limits.hlsl"
10+ #include "nbl/builtin/hlsl/bit.hlsl"
11+ #include "nbl/builtin/hlsl/algorithm.hlsl"
912#include "nbl/builtin/hlsl/math/polar.hlsl"
1013#include "nbl/builtin/hlsl/math/octahedral.hlsl"
1114#include "nbl/builtin/hlsl/concepts.hlsl"
1215#include "nbl/builtin/hlsl/ies/profile.hlsl"
1316
14- namespace nbl
17+ namespace nbl
1518{
16- namespace hlsl
19+ namespace hlsl
1720{
18- namespace ies
21+ namespace ies
1922{
2023namespace concepts
2124{
@@ -27,26 +30,26 @@ NBL_CONCEPT_BEGIN(1)
2730#define accessor NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0
2831#define req_key_t uint32_t
2932#define req_key_t2 uint32_t2
30- #define req_value_t float32_t
33+ #define req_angle_t float32_t
34+ #define req_candela_t float32_t
3135NBL_CONCEPT_END (
32- ((NBL_CONCEPT_REQ_TYPE)(accessor_t::key_t))
33- ((NBL_CONCEPT_REQ_TYPE)(accessor_t::key_t2))
34- ((NBL_CONCEPT_REQ_TYPE)(accessor_t::value_t))
35- ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((req_key_t (0 )), is_same_v, typename accessor_t::key_t))
36- ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((req_key_t2 (0 , 0 )), is_same_v, typename accessor_t::key_t2))
37- ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((req_value_t (0 )), is_same_v, typename accessor_t::value_t))
36+ ((NBL_CONCEPT_REQ_TYPE)(accessor_t::angle_t))
37+ ((NBL_CONCEPT_REQ_TYPE)(accessor_t::candela_t))
38+ ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((req_angle_t (0 )), is_same_v, typename accessor_t::angle_t))
39+ ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((req_candela_t (0 )), is_same_v, typename accessor_t::candela_t))
3840
41+ ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.getProperties ()), is_same_v, ProfileProperties))
42+ ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.value (req_key_t2 (0 , 0 ))), is_same_v, typename accessor_t::candela_t))
3943 ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.vAnglesCount ()), is_same_v, req_key_t))
4044 ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.hAnglesCount ()), is_same_v, req_key_t))
41- ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.getProperties ()), is_same_v, ProfileProperties))
42- ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.template vAngle<req_key_t>((req_key_t)0 )), is_same_v, req_value_t))
43- ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.template hAngle<req_key_t>((req_key_t)0 )), is_same_v, req_value_t))
44- ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.template value<req_key_t2>((req_key_t2)0 )), is_same_v, req_value_t))
45+ ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.vAngle (req_key_t (0 ))), is_same_v, typename accessor_t::angle_t))
46+ ((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((accessor.hAngle (req_key_t (0 ))), is_same_v, typename accessor_t::angle_t))
4547);
4648#undef accessor
4749#undef req_key_t
4850#undef req_key_t2
49- #undef req_value_t
51+ #undef req_angle_t
52+ #undef req_candela_t
5053#include <nbl/builtin/hlsl/concepts/__end.hlsl>
5154
5255template<typename accessor_t>
@@ -57,109 +60,106 @@ template<typename Accessor NBL_FUNC_REQUIRES(concepts::IsIESAccessor<Accessor>)
5760struct CandelaSampler
5861{
5962 using accessor_t = Accessor;
60- using value_t = typename accessor_t::value_t;
63+ using angle_t = typename accessor_t::angle_t;
64+ using candela_t = typename accessor_t::candela_t;
6165 using symmetry_t = ProfileProperties::LuminairePlanesSymmetry;
62- using polar_t = math::Polar<float32_t>;
63- using octahedral_t = math::OctahedralTransform<float32_t>;
66+ using polar_t = math::Polar<float32_t>;
67+ using octahedral_t = math::OctahedralTransform<float32_t>;
68+ using vector2_type = float32_t2;
69+
70+ vector2_type halfMinusHalfPixel;
6471
65- static value_t sample (NBL_CONST_REF_ARG (accessor_t) accessor, NBL_CONST_REF_ARG (math::Polar<float32_t>) polar )
72+ static inline CandelaSampler create (NBL_CONST_REF_ARG (vector2_type) lastTexelRcp )
6673 {
67- // TODO: DXC seems to have a bug and cannot use symmetry_t directly with == operator https://godbolt.devsh.eu/z/P9Kc5x
68- const ProfileProperties::LuminairePlanesSymmetry symmetry = accessor. getProperties (). getSymmetry ( );
69- const float32_t vAngle = degrees (polar.theta) ;
70- const float32_t hAngle = degrees ( wrapPhi (polar.phi, symmetry));
74+ CandelaSampler retval;
75+ retval.halfMinusHalfPixel = vector2_type ( 0.5f , 0.5f ) / ( vector2_type ( 1.f , 1.f ) + lastTexelRcp );
76+ return retval ;
77+ }
7178
72- const float32_t vABack = accessor.vAngle (accessor.vAnglesCount () - 1u);
73- if (vAngle > vABack)
74- return 0.f ;
79+ inline candela_t operator ()(NBL_CONST_REF_ARG (accessor_t) accessor, NBL_CONST_REF_ARG (polar_t) polar) NBL_CONST_MEMBER_FUNC
80+ {
81+ assert (polar.theta >= float32_t (0.0 ) && polar.theta <= numbers::pi<float32_t>);
82+ assert (hlsl::abs (polar.phi) <= numbers::pi<float32_t> * float32_t (2.0 ));
7583
76- const uint32_t j0 = getVLB (accessor, vAngle);
77- const uint32_t j1 = getVUB (accessor, vAngle);
78- const uint32_t i0 = (symmetry == ProfileProperties::LuminairePlanesSymmetry::ISOTROPIC) ? 0u : getHLB (accessor, hAngle);
79- const uint32_t i1 = (symmetry == ProfileProperties::LuminairePlanesSymmetry::ISOTROPIC) ? 0u : getHUB (accessor, hAngle);
84+ const symmetry_t symmetry = accessor.getProperties ().getSymmetry ();
85+ const angle_t vAngle = degrees (polar.theta);
86+ const angle_t hAngle = degrees (__wrapPhi (polar.phi, symmetry));
8087
81- const float32_t uReciprocal = ((i1 == i0) ? 1.f : 1.f / (accessor.hAngle (i1) - accessor.hAngle (i0)));
82- const float32_t vReciprocal = ((j1 == j0) ? 1.f : 1.f / (accessor.vAngle (j1) - accessor.vAngle (j0)));
88+ #define NBL_IES_DEF_ANGLE_ACC (T, EXPR) struct T { using value_type = angle_t; accessor_t acc; value_type operator[](uint32_t idx) NBL_CONST_MEMBER_FUNC { return EXPR; } };
8389
84- const float32_t u = ((hAngle - accessor. hAngle (i0)) * uReciprocal);
85- const float32_t v = ((vAngle - accessor. vAngle (j0)) * vReciprocal);
90+ NBL_IES_DEF_ANGLE_ACC (VAcc, acc. vAngle (idx))
91+ NBL_IES_DEF_ANGLE_ACC (HAcc, acc. hAngle (idx))
8692
87- const float32_t s0 = (accessor.value (uint32_t2 (i0, j0)) * (1.f - v) + accessor.value (uint32_t2 (i0, j1)) * v);
88- const float32_t s1 = (accessor.value (uint32_t2 (i1, j0)) * (1.f - v) + accessor.value (uint32_t2 (i1, j1)) * v);
93+ VAcc vAcc; vAcc.acc = accessor; HAcc hAcc; hAcc.acc = accessor;
8994
90- return s0 * (1.f - u) + s1 * u;
91- }
95+ #undef NBL_IES_DEF_ANGLE_ACC
9296
93- static value_t sample (NBL_CONST_REF_ARG (accessor_t) accessor, NBL_CONST_REF_ARG (float32_t2) uv)
94- {
95- const float32_t3 dir = octahedral_t::uvToDir (uv);
96- const polar_t polar = polar_t::createFromCartesian (dir);
97- return sample (accessor, polar);
98- }
97+ const uint32_t vCount = accessor.vAnglesCount ();
98+ const uint32_t hCount = accessor.hAnglesCount ();
99+ const angle_t vABack = vAcc[vCount - 1u];
100+ if (vAngle > vABack)
101+ return candela_t (0 );
99102
100- static float32_t wrapPhi (const float32_t phi, const symmetry_t symmetry)
101- {
102- switch (symmetry)
103- {
104- case symmetry_t::ISOTROPIC: //! axial symmetry
105- return 0.0f ;
106- case symmetry_t::QUAD_SYMETRIC: //! phi MIRROR_REPEAT wrap onto [0, 90] degrees range
107- {
108- NBL_CONSTEXPR float32_t M_HALF_PI = numbers::pi<float32_t> * 0.5f ;
109- float32_t wrapPhi = abs (phi); //! first MIRROR
110- if (wrapPhi > M_HALF_PI) //! then REPEAT
111- wrapPhi = hlsl::clamp (M_HALF_PI - (wrapPhi - M_HALF_PI), 0.f , M_HALF_PI);
112- return wrapPhi; //! eg. maps (in degrees) 91,269,271 -> 89 and 179,181,359 -> 1
113- }
114- case symmetry_t::HALF_SYMETRIC: //! phi MIRROR wrap onto [0, 180] degrees range
115- case symmetry_t::OTHER_HALF_SYMMETRIC: //! eg. maps (in degress) 181 -> 179 or 359 -> 1
116- return abs (phi);
117- case symmetry_t::NO_LATERAL_SYMMET: //! plot onto whole (in degress) [0, 360] range
118- {
119- NBL_CONSTEXPR float32_t M_TWICE_PI = numbers::pi<float32_t> *2.f ;
120- return (phi < 0.f ) ? (phi + M_TWICE_PI) : phi;
121- }
122- }
123- return 69.f ;
124- }
103+ const uint32_t vUbRaw = __upperBound (vAcc, vCount, vAngle);
104+ const uint32_t vLb = __lowerFromUpper (vUbRaw);
105+ const uint32_t vUb = __clampUpper (vUbRaw, vCount);
125106
126- struct impl_t
127- {
128- static uint32_t getVUB (NBL_CONST_REF_ARG (accessor_t) accessor, const float32_t angle)
129- {
130- for (uint32_t i = 0u; i < accessor.vAnglesCount (); ++i)
131- if (accessor.vAngle (i) > angle)
132- return i;
133- return accessor.vAnglesCount ();
134- }
107+ const bool isotropic = (symmetry == symmetry_t::ISOTROPIC);
108+ const uint32_t hUbRaw = isotropic ? 0u : __upperBound (hAcc, hCount, hAngle);
109+ const uint32_t hLb = isotropic ? 0u : __lowerFromUpper (hUbRaw);
110+ const uint32_t hUb = isotropic ? 0u : __clampUpper (hUbRaw, hCount);
135111
136- static uint32_t getHUB (NBL_CONST_REF_ARG (accessor_t) accessor, const float32_t angle)
137- {
138- for (uint32_t i = 0u; i < accessor.hAnglesCount (); ++i)
139- if (accessor.hAngle (i) > angle)
140- return i;
141- return accessor.hAnglesCount ();
142- }
143- };
112+ const angle_t uReciprocal = (hUb == hLb) ? angle_t (1 ) : angle_t (1 ) / (hAcc[hUb] - hAcc[hLb]);
113+ const angle_t vReciprocal = (vUb == vLb) ? angle_t (1 ) : angle_t (1 ) / (vAcc[vUb] - vAcc[vLb]);
144114
145- static uint32_t getVLB (NBL_CONST_REF_ARG (accessor_t) accessor, const float32_t angle)
146- {
147- return (uint32_t)hlsl::max ((int64_t)impl_t::getVUB (accessor, angle) - 1ll, 0ll);
148- }
115+ const angle_t u = (hAngle - hAcc[hLb]) * uReciprocal;
116+ const angle_t v = (vAngle - vAcc[vLb]) * vReciprocal;
149117
150- static uint32_t getHLB (NBL_CONST_REF_ARG (accessor_t) accessor, const float32_t angle)
151- {
152- return (uint32_t)hlsl::max ((int64_t)impl_t::getHUB (accessor, angle) - 1ll, 0ll);
118+ const candela_t s0 = accessor.value (uint32_t2 (hLb, vLb)) * (angle_t (1 ) - v) + accessor.value (uint32_t2 (hLb, vUb)) * v;
119+ const candela_t s1 = accessor.value (uint32_t2 (hUb, vLb)) * (angle_t (1 ) - v) + accessor.value (uint32_t2 (hUb, vUb)) * v;
120+
121+ return s0 * (angle_t (1 ) - u) + s1 * u;
153122 }
154123
155- static uint32_t getVUB ( NBL_CONST_REF_ARG (accessor_t) accessor, const float32_t angle)
124+ inline candela_t operator ()( NBL_CONST_REF_ARG (accessor_t) accessor, NBL_CONST_REF_ARG (float32_t2) uv) NBL_CONST_MEMBER_FUNC
156125 {
157- return (uint32_t)hlsl::min ((int64_t)impl_t::getVUB (accessor, angle), (int64_t)(accessor.vAnglesCount () - 1u));
126+ const float32_t3 dir = octahedral_t::uvToDir (uv, halfMinusHalfPixel);
127+ const polar_t polar = polar_t::createFromCartesian (dir);
128+ return operator ()(accessor, polar);
158129 }
159130
160- static uint32_t getHUB (NBL_CONST_REF_ARG (accessor_t) accessor, const float32_t angle)
131+ template<typename View>
132+ static inline uint32_t __upperBound (NBL_REF_ARG (View) view, const uint32_t count, const angle_t angle) { return nbl::hlsl::upper_bound (view, 0u, count, angle); }
133+
134+ static inline uint32_t __lowerFromUpper (const uint32_t ubRaw) { return ubRaw > 0u ? (ubRaw - 1u) : 0u; }
135+
136+ static inline uint32_t __clampUpper (const uint32_t ubRaw, const uint32_t count) { return ubRaw < count ? ubRaw : (count - 1u); }
137+
138+ static inline angle_t __wrapPhi (const angle_t phi, const symmetry_t symmetry)
161139 {
162- return (uint32_t)hlsl::min ((int64_t)impl_t::getHUB (accessor, angle), (int64_t)(accessor.hAnglesCount () - 1u));
140+ switch (symmetry)
141+ {
142+ case symmetry_t::ISOTROPIC: //! axial symmetry
143+ return angle_t (0.0 );
144+ case symmetry_t::QUAD_SYMETRIC: //! phi MIRROR_REPEAT wrap onto [0, 90] degrees range
145+ {
146+ const angle_t HalfPI = numbers::pi<angle_t> * angle_t (0.5 );
147+ angle_t wrapPhi = hlsl::abs (phi); //! first MIRROR
148+ if (wrapPhi > HalfPI) //! then REPEAT
149+ wrapPhi = hlsl::clamp (HalfPI - (wrapPhi - HalfPI), angle_t (0 ), HalfPI);
150+ return wrapPhi; //! eg. maps (in degrees) 91,269,271 -> 89 and 179,181,359 -> 1
151+ }
152+ case symmetry_t::HALF_SYMETRIC: //! phi MIRROR wrap onto [0, 180] degrees range
153+ case symmetry_t::OTHER_HALF_SYMMETRIC: //! eg. maps (in degress) 181 -> 179 or 359 -> 1
154+ return hlsl::abs (phi);
155+ case symmetry_t::NO_LATERAL_SYMMET: //! plot onto whole (in degress) [0, 360] range
156+ {
157+ const angle_t TwicePI = numbers::pi<angle_t> * angle_t (2.0 );
158+ return (phi < angle_t (0 )) ? (phi + TwicePI) : phi;
159+ }
160+ }
161+
162+ return bit_cast<angle_t>(numeric_limits<float32_t>::quiet_NaN);
163163 }
164164};
165165
0 commit comments