Fermat
sh_inline.h
1 /*
2  * Copyright (c) 2010-2018, NVIDIA Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of NVIDIA Corporation nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 namespace cugar {
29 
30 // rotate a zonal harmonics to an arbitrary direction vector
31 //
32 // \param L number of bands
33 // \param zh_coeff input Zonal Harmonics coefficients
34 // \param d input vector
35 // \param sh_coeff output Spherical Harmonics coefficients
36 template <typename ZHVector, typename SHVector, typename Vector3>
37 CUGAR_HOST_DEVICE void rotate_ZH(const int32 L, const ZHVector& zh_coeff, const Vector3& d, SHVector& sh_coeff)
38 {
39  for (int32 l = 0; l < L; ++l)
40  for (int32 m = -l; m <= l; ++m)
41  sh_coeff[ l*l + m+l ] = sqrtf( 4.0f*M_PIf / float(2*l + 1) ) * zh_coeff[l] * sh( l, m, d );
42 }
43 
44 // return the (l,m) spherical harmonics coefficient of a zonal harmonics
45 // function rotated to match a given axis.
46 //
47 // \param zh_l l-band zonal harmonics coefficient
48 // \param d input vector
49 template <int32 l, int32 m, typename Vector3>
50 CUGAR_HOST_DEVICE float rotate_ZH(const float zh_l, const Vector3& d)
51 {
52  return sqrtf( 4.0f*M_PIf / float(2*l + 1) ) * zh_l * sh( l, m, d );
53 }
54 
55 template <int32 l>
57 {
58  template <int32 m>
59  struct Apply
60  {
61  template <typename ZHVector, typename SHVector, typename Vector3>
62  CUGAR_HOST_DEVICE static void eval(const ZHVector& zh_coeff, const Vector3& d, SHVector& sh_coeff)
63  {
64  sh_coeff[ l*l + m+l ] = sqrtf( 4.0f*M_PIf / float(2*l + 1) ) * zh_coeff[l] * sh<l,m>( d );
65  Apply<m+1>::eval( zh_coeff, d, sh_coeff );
66  }
67  };
68  template <>
69  struct Apply<l>
70  {
71  template <typename ZHVector, typename SHVector, typename Vector3>
72  CUGAR_HOST_DEVICE static void eval(const ZHVector& zh_coeff, const Vector3& d, SHVector& sh_coeff)
73  {
74  sh_coeff[ l*l + l+l ] = sqrtf( 4.0f*M_PIf / float(2*l + 1) ) * zh_coeff[l] * sh<l,l>( d );
75  }
76  };
77 
78  template <typename ZHVector, typename SHVector, typename Vector3>
79  CUGAR_HOST_DEVICE static void eval(const ZHVector& zh_coeff, const Vector3& d, SHVector& sh_coeff)
80  {
81  Apply<-l>::eval( zh_coeff, d, sh_coeff );
82  if (l > 0)
83  ZH_rotation<l-1>::eval( zh_coeff, d, sh_coeff );
84  }
85 };
86 template <>
87 struct ZH_rotation<0>
88 {
89  template <typename ZHVector, typename SHVector, typename Vector3>
90  CUGAR_HOST_DEVICE static void eval(const ZHVector& zh_coeff, const Vector3& d, SHVector& sh_coeff)
91  {
92  sh_coeff[0] = sqrtf( 4.0f*M_PIf ) * zh_coeff[0] * sh<0,0>( d );
93  }
94 };
95 
96 // rotate a zonal harmonics to an arbitrary direction vector, with
97 // the number of bands specified at compile-time.
98 //
99 // \param zh_coeff input Zonal Harmonics coefficients
100 // \param d input vector
101 // \param sh_coeff output Spherical Harmonics coefficients
102 template <int32 L, typename ZHVector, typename SHVector, typename Vector3>
103 CUGAR_HOST_DEVICE void rotate_ZH(const ZHVector& zh_coeff, const Vector3& d, SHVector& sh_coeff)
104 {
105  ZH_rotation<L-1>::eval( zh_coeff, d, sh_coeff );
106 }
107 
108 // evaluate the (l,m)-th basis function on a given vector
109 //
110 // \param l band index
111 // \param m subband index
112 // \param v input vector
113 template <typename Vector3>
114 CUGAR_HOST_DEVICE float sh(const int32 l, const int32 m, const Vector3& v)
115 {
116 #if 0
117  if (l == 0)
118  return 0.282095f;
119  else if (l == 1)
120  return 0.488603f * (m == -1 ? v[0] : (m == 0 ? v[2] : v[1]));
121  else if (l == 2)
122  {
123  if (m == 0)
124  return 0.315392f * (3*v[2]*v[2] - 1.0f);
125  else if (m == 2)
126  return 0.546274f * (v[0]*v[0] - v[1]*v[1]);
127  else if (m == -2)
128  return 1.092548f * v[0]*v[2];
129  else if (m == -1)
130  return 1.092548f * v[1]*v[2];
131  else
132  return 1.092548f * v[0]*v[1];
133  }
134 #else
135  const float X = v[0];
136  const float Y = v[1];
137  const float Z = v[2];
138 
139  const float m_15_over_4sqrtPI = 0.54627419f; //sqrtf(15.0f)/(4.0f*sqrtf(M_PIf));
140  const float m_15_over_2sqrtPI = 1.09254837f; //sqrtf(15.0f)/(2.0f*sqrtf(M_PIf));
141  const float m_5_over_4sqrtPI = 0.31539154f; //sqrtf(5.0f)/(4.0f*sqrtf(M_PIf));
142  const float m_sqrt2sqrt35_over_8sqrtPI = 0.59004354f; //sqrtf(2.0f*35.0f)/(8.0f*sqrtf(M_PIf));
143  //const float m_sqrt2sqrt35_over_4sqrtPI = 1.18008709f; //sqrtf(2.0f*35.0f)/(4.0f*sqrtf(M_PIf));
144  const float m_sqrt7_over_4sqrtPI = 0.37317631f; //sqrtf(7.0f)/(4.0f*sqrtf(M_PIf));
145  const float m_sqrt2sqrt21_over_8sqrtPI = 0.45704576f; //sqrtf(2.0f*21.0f)/(8.0f*sqrtf(M_PIf));
146  const float m_sqrt105_over_4sqrtPI = 1.44530571f; //sqrtf(105.0f)/(4.0f*sqrtf(M_PIf));
147  const float m_sqrt105_over_2sqrtPI = 2.89061141f; //sqrtf(105.0f)/(2.0f*sqrtf(M_PIf));
148 
149  if (l == 0)
150  return 0.282095f;
151  else if (l == 1)
152  return 0.488603f * (m == -1 ? -Y : (m == 0 ? Z : -X));
153  else if (l == 2)
154  {
155  if (m == 0)
156  return m_5_over_4sqrtPI * (3*Z*Z - 1.0f);
157  else if (m == 1)
158  return -m_15_over_2sqrtPI * X*Z;
159  else if (m == 2)
160  return m_15_over_4sqrtPI * (X*X - Y*Y);
161  else if (m == -1)
162  return -m_15_over_2sqrtPI * Y*Z;
163  else if (m == -2)
164  return m_15_over_2sqrtPI * X*Y;
165  }
166  else if (l == 3)
167  {
168  if (m == 0)
169  return m_sqrt7_over_4sqrtPI * Z * (5*Z*Z - 3);
170  else if (m == 1)
171  return -m_sqrt2sqrt21_over_8sqrtPI * X * (5*Z*Z - 1);
172  else if (m == 2)
173  return m_sqrt105_over_4sqrtPI * (X*X - Y*Y) * Z;
174  else if (m == 3)
175  return -m_sqrt2sqrt35_over_8sqrtPI * (X*X - 3*Y*Y)*X;
176  else if (m == -1)
177  return -m_sqrt2sqrt21_over_8sqrtPI * Y * (5*Z*Z - 1);
178  else if (m == -2)
179  return m_sqrt105_over_2sqrtPI * X*Y*Z;
180  else if (m == -3)
181  return -m_sqrt2sqrt35_over_8sqrtPI * (3*X*X - Y*Y)*Y;
182  }
183 #endif
184  return 0.0f;
185 }
186 // evaluate the (l,m)-th basis function on a given vector, where
187 // l and m are determined at compile-time.
188 //
189 // \param v input vector
190 template <int32 l, int32 m, typename Vector3>
191 CUGAR_HOST_DEVICE float sh(const Vector3& v)
192 {
193  const float X = v[0];
194  const float Y = v[1];
195  const float Z = v[2];
196 
197  const float m_15_over_4sqrtPI = 0.54627419f; //sqrtf(15.0f)/(4.0f*sqrtf(M_PIf));
198  const float m_15_over_2sqrtPI = 1.09254837f; //sqrtf(15.0f)/(2.0f*sqrtf(M_PIf));
199  const float m_5_over_4sqrtPI = 0.31539154f; //sqrtf(5.0f)/(4.0f*sqrtf(M_PIf));
200  const float m_sqrt2sqrt35_over_8sqrtPI = 0.59004354f; //sqrtf(2.0f*35.0f)/(8.0f*sqrtf(M_PIf));
201  //const float m_sqrt2sqrt35_over_4sqrtPI = 1.18008709f; //sqrtf(2.0f*35.0f)/(4.0f*sqrtf(M_PIf));
202  const float m_sqrt7_over_4sqrtPI = 0.37317631f; //sqrtf(7.0f)/(4.0f*sqrtf(M_PIf));
203  const float m_sqrt2sqrt21_over_8sqrtPI = 0.45704576f; //sqrtf(2.0f*21.0f)/(8.0f*sqrtf(M_PIf));
204  const float m_sqrt105_over_4sqrtPI = 1.44530571f; //sqrtf(105.0f)/(4.0f*sqrtf(M_PIf));
205  const float m_sqrt105_over_2sqrtPI = 2.89061141f; //sqrtf(105.0f)/(2.0f*sqrtf(M_PIf));
206 
207  if (l == 0)
208  return 0.282095f;
209  else if (l == 1)
210  return 0.488603f * (m == -1 ? -Y : (m == 0 ? Z : -X));
211  else if (l == 2)
212  {
213  if (m == 0)
214  return m_5_over_4sqrtPI * (3*Z*Z - 1.0f);
215  else if (m == 1)
216  return -m_15_over_2sqrtPI * X*Z;
217  else if (m == 2)
218  return m_15_over_4sqrtPI * (X*X - Y*Y);
219  else if (m == -1)
220  return -m_15_over_2sqrtPI * Y*Z;
221  else if (m == -2)
222  return m_15_over_2sqrtPI * X*Y;
223  }
224  else if (l == 3)
225  {
226  if (m == 0)
227  return m_sqrt7_over_4sqrtPI * Z * (5*Z*Z - 3);
228  else if (m == 1)
229  return -m_sqrt2sqrt21_over_8sqrtPI * X * (5*Z*Z - 1);
230  else if (m == 2)
231  return m_sqrt105_over_4sqrtPI * (X*X - Y*Y) * Z;
232  else if (m == 3)
233  return -m_sqrt2sqrt35_over_8sqrtPI * (X*X - 3*Y*Y)*X;
234  else if (m == -1)
235  return -m_sqrt2sqrt21_over_8sqrtPI * Y * (5*Z*Z - 1);
236  else if (m == -2)
237  return m_sqrt105_over_2sqrtPI * X*Y*Z;
238  else if (m == -3)
239  return -m_sqrt2sqrt35_over_8sqrtPI * (3*X*X - Y*Y)*Y;
240  }
241  return 0.0f;
242 }
243 
244 // evaluate the (l,m)-th basis function on a given vector, where
245 // l is determined at compile-time.
246 //
247 // \param m subband index
248 // \param v input vector
249 template <int32 l, typename Vector3>
250 CUGAR_HOST_DEVICE float sh(const int32 m, const Vector3& v)
251 {
252  if (l == 0)
253  return sh<0,0>( v );
254  else if (l == 1)
255  {
256  if (m == -1)
257  return sh<1,-1>( v );
258  else if (m == 0)
259  return sh<1,0>( v );
260  else
261  return sh<1,1>( v );
262  }
263  else if (l == 2)
264  {
265  if (m == 0)
266  return sh<2,0>( v );
267  else if (m == 1)
268  return sh<2,1>( v );
269  else if (m == 2)
270  return sh<2,2>( v );
271  else if (m == -1)
272  return sh<2,-1>( v );
273  else if (m == -2)
274  return sh<2,-2>( v );
275  }
276  else if (l == 3)
277  {
278  if (m == 0)
279  return sh<3,0>( v );
280  else if (m == 1)
281  return sh<3,1>( v );
282  else if (m == 2)
283  return sh<3,2>( v );
284  else if (m == 3)
285  return sh<3,3>( v );
286  else if (m == -1)
287  return sh<3,-1>( v );
288  else if (m == -2)
289  return sh<3,-2>( v );
290  else if (m == -3)
291  return sh<3,-3>( v );
292  }
293  return 0.0f;
294 }
295 
296 // evaluate the i-th coefficient at a given point
297 //
298 // \param i coefficient index
299 // \param d direction vector
300 template <int32 L>
301 template <typename Vector3>
302 CUGAR_HOST_DEVICE float SH_basis<L>::eval(const int32 i, const Vector3& d)
303 {
304  if (i == 0)
305  return sh<0>( 0, d );
306  else if (i < 4)
307  return sh<1>( i - 2, d );
308  else if (i < 9)
309  return sh<2>( i - 6, d );
310  else
311  return sh<3>( i - 12, d );
312 }
313 
314 // add a weighted basis expansion of a clamped cosine lobe to a given
315 // set of coefficients
316 //
317 // \param normal input normal
318 // \param w scalar weight
319 // \param coeffs input/output coefficients
320 template <int32 L>
321 CUGAR_HOST_DEVICE void SH_basis<L>::clamped_cosine(const Vector3f& normal, const float w, float* coeffs)
322 {
323  const float zh[4] = {
324  0.886214f,
325  1.023202f,
326  0.495443f,
327  0.013224f };
328 
329  float sh[COEFFS];
330  rotate_ZH<L>( zh, normal, sh );
331 
332  for (uint32 i = 0; i < COEFFS; ++i)
333  coeffs[i] += sh[i] * w;
334 }
335 
336 // return the basis expansion of a constant
337 //
338 // \param k input constant
339 // \param coeffs output coefficients
340 template <int32 L>
341 CUGAR_HOST_DEVICE void SH_basis<L>::constant(float k, float* coeffs)
342 {
343  coeffs[0] = k * 2.0f*sqrtf(M_PIf);
344  for (int32 i = 1; i < COEFFS; ++i)
345  coeffs[i] = 0.0f;
346 }
347 
348 // evaluate the associated Legendre polynomial P_l^m on x = cos(theta), y = sin(theta) = sqrt(1 - x*x)
349 //
350 CUGAR_FORCEINLINE CUGAR_HOST_DEVICE
351 float sh_legendre_polynomial(const uint32 l, const uint32 m, const float x, const float y)
352 {
353  if (l == 0)
354  return 1;
355  else if (l == 1)
356  {
357  if (m == 0)
358  return x;
359  else
360  return -y;
361  }
362  else if (l == 2)
363  {
364  if (m == 0)
365  return 0.5f * (3 * x*x - 1);
366  else if (m == 1)
367  return -3 * x * y;
368  else
369  return 3 * y*y;
370  }
371  else if (l == 3)
372  {
373  if (m == 0)
374  return 0.5f * x * (5 * x*x - 3);
375  else if (m == 1)
376  return -3.0f / 2.0f * (5 * x*x - 1) * y;
377  else if (m == 2)
378  return 15.0f * x * y*y;
379  else
380  return -15.0f * y*y*y;
381  }
382  return 0.0f;
383 }
384 
385 // evaluate the associated Legendre integrals for a basis of order n
386 // see Algorithm 1 in:
387 //
388 // Importance Sampling Spherical Harmonics,
389 // W.Jarosz, N.Carr, H.W.Jensen
390 //
391 template <typename OutputIterator>
392 CUGAR_HOST_DEVICE
393 void sh_legendre_integrals(const uint32 n, const float x, OutputIterator r)
394 {
395  const float y = sqrtf(1.0f - x*x);
396 
397  // Evaluate equation 11:
398  r[0] = x;
399 
400  if (n == 1)
401  return;
402 
403  // Evaluate equation 12 and 13:
404  r[1 * n + 0] = x * 0.5f;
405  r[1 * n + 1] = 0.5f * (x * sqrtf(1 - x*x) + asin(x));
406 
407  for (uint32 l = 2; l < n; ++l)
408  {
409  for (uint32 m = 0; m < l - 1; ++m)
410  {
411  // Evaluate equation 9:
412  const float g_x = (2 * l - 1)*(1 - x*x) * sh_legendre_polynomial(l - 1, m, x, y);
413  r[l * n + m] = ((l - 2)*(l - 1 + m) * r[(l - 2)*n + m] - g_x) / float( (l+1)*(l-m) );
414  }
415 
416  // Evaluate special case of equation 9:
417  r[l * n + l - 1] = -(2*l - 1)/float(l+1) * ((1 - x*x) * sh_legendre_polynomial(l - 1, l - 1, x, y));
418 
419  // Evaluate Equation 10:
420  r[l * n + l] = 1 / float(l + 1) * (l*(2*l - 3)*(2*l - 1) * r[(l - 2)*n + l - 2] + x * sh_legendre_polynomial( 1, 1, x ));
421  }
422 }
423 
424 // evaluate the integral of the polar angle function Phi^m(phi)
425 // see equation 14 in:
426 //
427 // Importance Sampling Spherical Harmonics,
428 // W.Jarosz, N.Carr, H.W.Jensen
429 //
430 // \param m required order
431 // \param phi the point of evaluation, in angular coordinates
432 //
433 CUGAR_HOST_DEVICE
434 float sh_polar_integral(const int32 m, const float phi)
435 {
436  return 1 / float(m) * (m >= 0 ? sin(m*phi) : cos(m*phi));
437 }
438 
439 } // namespace cugar
440 
CUGAR_HOST_DEVICE float sh(const int32 l, const int32 m, const Vector3 &v)
Definition: sh_inline.h:114
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float sh_legendre_polynomial(const uint32 l, const uint32 m, const float x, const float y)
Definition: sh_inline.h:351
static CUGAR_HOST_DEVICE float eval(const int32 i, const Vector3 &d)
Definition: sh_inline.h:302
CUGAR_HOST_DEVICE void rotate_ZH(const int32 L, const ZHVector &zh_coeff, const Vector3 &d, SHVector &sh_coeff)
Definition: sh_inline.h:37
Definition: sh_inline.h:59
CUGAR_HOST_DEVICE float sh_polar_integral(const int32 m, const float phi)
Definition: sh_inline.h:434
Define a vector_view POD type and plain_view() for std::vector.
Definition: diff.h:38
Definition: sh_inline.h:56
static CUGAR_HOST_DEVICE void clamped_cosine(const Vector3f &normal, const float w, float *coeffs)
Definition: sh_inline.h:321
static CUGAR_HOST_DEVICE void constant(float k, float *coeffs)
Definition: sh_inline.h:341
CUGAR_HOST_DEVICE void sh_legendre_integrals(const uint32 n, const float x, OutputIterator r)
Definition: sh_inline.h:393