Fermat
ggx.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010-2016, 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 
34 #pragma once
35 
36 #include <cugar/linalg/vector.h>
37 #include <cugar/basic/numbers.h>
38 #include <cugar/bsdf/differential_geometry.h>
39 #include <cugar/bsdf/ggx_common.h>
40 
41 
42 namespace cugar {
43 
48 struct GGXVCavityMicrofacetDistribution
55 {
56  CUGAR_FORCEINLINE CUGAR_HOST_DEVICE
57  GGXVCavityMicrofacetDistribution(const float _roughness) :
58  roughness(_roughness),
59  inv_roughness(1.0f / _roughness) {}
60 
63  CUGAR_FORCEINLINE CUGAR_HOST_DEVICE
64  float p(const DifferentialGeometry& geometry, const Vector3f V, const Vector3f H) const
65  {
66  const float2 inv_alpha = make_float2(inv_roughness, inv_roughness);
67 
68  const Vector3f N = geometry.normal_s;
69 
70  const float VoH = fabsf( dot(V, H) );
71  const float NoV = fabsf( dot(N, V) );
72  const float NoH = fabsf( dot(N, H) );
73 
74  const float G1 = fminf(1, 2 * NoH * NoV / VoH);
75 
76  const float D = hvd_ggx_eval(inv_alpha, NoH, dot(geometry.tangent, H), dot(geometry.binormal, H));
77  float p = D * G1 * VoH / NoV;
78  // NOTE: if reflection of V against H was used to generate a ray, the Jacobian 1 / (4*VoH) would elide
79  // the VoH at the numerator, leaving: D * G1 / (4 * NoV) as a solid angle probability, or D * G1 / (4 * NoV * NoL)
80  // as a projected solid angle probability
81 
82  return (!isfinite(p) || isnan(p)) ? 1.0e8f : p;
83  }
84 
87  CUGAR_FORCEINLINE CUGAR_HOST_DEVICE
88  void sample(
89  const Vector3f u,
90  const DifferentialGeometry& geometry,
91  const Vector3f V,
92  Vector3f& H,
93  float& p) const
94  {
95  //const float2 inv_alpha = make_float2(inv_roughness, inv_roughness);
96  const float inv_alpha = inv_roughness;
97 
98  const Vector3f N = geometry.normal_s;
99 
100  H = hvd_ggx_sample( make_float2(u.x,u.y), inv_alpha );
101 
102  H =
103  H[0] * geometry.tangent +
104  H[1] * geometry.binormal +
105  H[2] * geometry.normal_s;
106 
107  const Vector3f H_prime = reflect(H, N);
108 
109  float VoH = dot(V, H);
110  float VoH_prime = dot(V, H_prime);
111 
112  if (u[2] < VoH_prime / (VoH + VoH_prime))
113  {
114  H = H_prime;
115  VoH = VoH_prime;
116  }
117 
118  p = this->p(geometry, V, H);
119  }
120 
121 public:
122  float roughness;
123  float inv_roughness;
124 };
125 
132 struct GGXBsdf
133 {
134  CUGAR_FORCEINLINE CUGAR_HOST_DEVICE
135  GGXBsdf(const float _roughness) :
136  roughness(_roughness),
137  inv_roughness(1.0f / _roughness) {}
138 
141  CUGAR_FORCEINLINE CUGAR_HOST_DEVICE
142  Vector3f f(const DifferentialGeometry& geometry, const Vector3f V, const Vector3f L) const
143  {
144  const float2 inv_alpha = make_float2(inv_roughness, inv_roughness);
145 
146  const Vector3f H = cugar::normalize(V + L);
147  const Vector3f N = geometry.normal_s;
148 
149  if (dot(N,L)*dot(N,V) <= 0.0f)
150  return cugar::Vector3f(0.0f);
151 
152  const float VoH = dot(V, H);
153  const float NoL = dot(N, L);
154  const float NoV = dot(N, V);
155  const float NoH = dot(N, H);
156 
157  if (NoL * NoV <= 0.0f || NoH == 0.0f)
158  return 0.0f;
159 
160  const float D = hvd_ggx_eval(inv_alpha, NoH, dot(geometry.tangent, H), dot(geometry.binormal, H));
161  const float G2 = fminf(1, fminf(2 * NoH * NoV / VoH, 2 * NoH * NoL / VoH));
162  const float denom = (4 * NoV * NoL );
163  const float p = D * G2 / denom;
164 
165  return (!isfinite(p) || isnan(p)) ? 1.0e8f : p;
166  }
167 
170  CUGAR_FORCEINLINE CUGAR_HOST_DEVICE
171  Vector3f f_over_p(const DifferentialGeometry& geometry, const Vector3f V, const Vector3f L) const
172  {
173  const Vector3f H = cugar::normalize(V + L);
174  const Vector3f N = geometry.normal_s;
175 
176  if (dot(N, L)*dot(N, V) <= 0.0f)
177  return cugar::Vector3f(0.0f);
178 
179  const float VoH = dot(V, H);
180  const float NoL = dot(N, L);
181  const float NoV = dot(N, V);
182  const float NoH = dot(N, H);
183 
184  const float G2 = fminf(1, fminf(2 * NoH * NoV / VoH, 2 * NoH * NoL / VoH));
185  const float G1 = fminf(1, 2 * NoH * NoV / VoH);
186 
187  return cugar::Vector3f(G2 / G1);
188  }
189 
192  CUGAR_FORCEINLINE CUGAR_HOST_DEVICE
193  void f_and_p(const DifferentialGeometry& geometry, const Vector3f V, const Vector3f L, Vector3f& f, float& p, const SphericalMeasure measure = kProjectedSolidAngle) const
194  {
195  const float2 inv_alpha = make_float2(inv_roughness, inv_roughness);
196 
197  const Vector3f H = cugar::normalize(V + L);
198  const Vector3f N = geometry.normal_s;
199 
200  const float VoH = dot(V, H);
201  const float NoL = dot(N, L);
202  const float NoV = dot(N, V);
203  const float NoH = dot(N, H);
204 
205  const float D = hvd_ggx_eval(inv_alpha, NoH, dot(geometry.tangent, H), dot(geometry.binormal, H));
206  const float G2 = fminf(1, fminf(2 * NoH * NoV / VoH, 2 * NoH * NoL / VoH));
207  const float G1 = fminf(1, 2 * NoH * NoV / VoH);
208  const float denom = (4 * NoV * NoL );
209 
210  p = D * G1 / denom;
211  p = (!isfinite(p) || isnan(p)) ? 1.0e8f : p;
212 
213  float f_s = D * G2 / denom;
214  f_s = (!isfinite(f_s) || isnan(f_s)) ? 1.0e8f : f_s;
215 
216  f = (NoL * NoV <= 0.0f || NoH == 0.0f) ? Vector3f(0.0f) : Vector3f(f_s);
217 
218  if (measure == kSolidAngle)
219  p *= fabsf( NoL );
220  }
221 
224  CUGAR_FORCEINLINE CUGAR_HOST_DEVICE
225  float p(const DifferentialGeometry& geometry, const Vector3f V, const Vector3f L, const SphericalMeasure measure = kProjectedSolidAngle) const
226  {
227  const float2 inv_alpha = make_float2(inv_roughness, inv_roughness);
228 
229  const Vector3f H = cugar::normalize(V + L);
230  const Vector3f N = geometry.normal_s;
231 
232  const float VoH = dot(V, H);
233  const float NoL = dot(N, L);
234  const float NoV = dot(N, V);
235  const float NoH = dot(N, H);
236 
237  const float D = hvd_ggx_eval(inv_alpha, NoH, dot(geometry.tangent, H), dot(geometry.binormal, H));
238  const float G1 = fminf(1, 2 * NoH * NoV / VoH);
239  const float denom = (4 * NoV * NoL );
240  float p = D * G1 / denom;
241 
242  if (measure == kSolidAngle)
243  p *= fabsf(NoL);
244 
245  return (!isfinite(p) || isnan(p)) ? 1.0e8f : p;
246  }
247 
250  CUGAR_FORCEINLINE CUGAR_HOST_DEVICE
251  void sample(
252  const Vector3f u,
253  const DifferentialGeometry& geometry,
254  const Vector3f V,
255  Vector3f& L,
256  Vector3f& g,
257  float& p,
258  float& p_proj) const
259  {
260  //const float2 inv_alpha = make_float2(inv_roughness, inv_roughness);
261  const float inv_alpha = inv_roughness;
262 
263  const Vector3f N = geometry.normal_s;
264 
265  Vector3f H = hvd_ggx_sample( make_float2(u.x,u.y), inv_alpha );
266 
267  H =
268  H[0] * geometry.tangent +
269  H[1] * geometry.binormal +
270  H[2] * geometry.normal_s;
271 
272  const Vector3f H_prime = reflect(H, N);
273 
274  float VoH = dot(V, H);
275  float VoH_prime = dot(V, H_prime);
276 
277  if (u[2] < VoH_prime / (VoH + VoH_prime))
278  {
279  H = H_prime;
280  VoH = VoH_prime;
281  }
282 
283  L = 2 * dot(V, H) * H - V;
284 
285  const float NoL = dot(N, L);
286  const float NoV = dot(N, V);
287  const float NoH = dot(N, H);
288 
289  const float D = hvd_ggx_eval(make_float2(inv_alpha,inv_alpha), NoH, dot(geometry.tangent, H), dot(geometry.binormal, H));
290  const float G2 = fminf(1, fminf(2 * NoH * NoV / VoH, 2 * NoH * NoL / VoH));
291  const float G1 = fminf(1, 2 * NoH * NoV / VoH);
292  const float denom = (4 * NoV * NoL );
293 
294  g = cugar::Vector3f(1.0f) * (dot(L,N)*dot(V,N) > 0.0f ? (G2 / G1) : 0.0f);
295  p_proj = D * G1 / denom;
296  p = p_proj * fabsf(dot(N, L));
297 
298  if (!isfinite(p) || isnan(p)) p = 1.0e8f;
299  if (!isfinite(p_proj) || isnan(p_proj)) p_proj = 1.0e8f;
300  }
301 
304  template <typename RandomGeneratorT>
305  CUGAR_FORCEINLINE CUGAR_HOST_DEVICE
306  bool invert(
307  const DifferentialGeometry& geometry,
308  const Vector3f V,
309  const Vector3f L,
310  RandomGeneratorT& random,
311  Vector3f& z,
312  float& p,
313  float& p_proj) const
314  {
315  const float2 inv_alpha = make_float2(inv_roughness, inv_roughness);
316 
317  const Vector3f N = geometry.normal_s;
318 
319  //if (dot(N, L)*dot(N, V) <= 0.0f)
320  // return false;
321 
322  const Vector3f H = cugar::normalize(V + L);
323  const Vector3f H_alt = reflect(H, N);
324  //printf("\n H: %f, %f, %f\n", H.x, H.y, H.z);
325  //printf(" H_alt: %f, %f, %f\n", H_alt.x, H_alt.y, H_alt.z);
326 
327  // Now, there's two possibilities. Either:
328  // 1) H was originally sampled, and L directly generated from it, or
329  // 2) H_alt was originally sampled, and H was obtained as H = H_alt' = reflect(H_alt,N);
330  //
331  // 1) can only happen if H.N > 0, whereas 2) happens otherwise.
332  //
333  // If the case had been 1, the probability of staying with H was V.H / (V.H + V.H_alt)
334  // If the case had been 2, the probability of switching from H_alt to H was, again, V.H / (V.H + V.H_alt)
335  // These are needed to compute the random number that makes sure the eventual switch happens appropriately.
336 
337  float VoH = dot(V, H);
338  float VoH_alt = dot(V, H_alt);
339 
340  //printf(" rel probs: %f, %f : (%f, %f)\n", VoH / (VoH_alt + VoH), VoH_alt / (VoH_alt + VoH), VoH, VoH_alt);
341 
342  const float NoH = dot(H, N); // If NoH < 0.0f, the only possibility is that H_alt must have been sampled
343  //printf(" NoH: %f\n", NoH);
344 
345  const cugar::Vector3f H_orig = NoH > 0.0f ? H : H_alt;
346  const float2 H_prob = NoH > 0.0f ?
347  make_float2( fmaxf( 0.0f, fminf( 1.0f, VoH_alt / (VoH_alt + VoH) ) ), 1.0f ) :
348  make_float2( 0.0f, fmaxf( 0.0f, fminf( 1.0f, VoH / (VoH_alt + VoH) ) ) );
349 
350  const Vector3f local_H(
351  dot(H_orig, geometry.tangent),
352  dot(H_orig, geometry.binormal),
353  dot(H_orig, geometry.normal_s));
354  //printf(" local_H: %f, %f, %f - H_prob: [%f,%f]\n", local_H.x, local_H.y, local_H.z, H_prob.x, H_prob.y);
355 
356  const float2 u = hvd_ggx_invert( local_H, inv_alpha );
357  //const Vector3f local_H2 = hvd_ggx_sample(u, inv_alpha);
358  //printf(" local_H2: %f, %f, %f - u(%f,%f)\n", local_H2.x, local_H2.y, local_H2.z, u.x, u.y);
359 
360  p_proj = 1.0f / cugar::max(this->p(geometry, V, L, kProjectedSolidAngle), 1.0e-8f);
361  p = p_proj / cugar::max(fabsf(dot(L, N)), 1.0e-8f);
362 
363  z = Vector3f( u.x, u.y, H_prob.x + random.next() * (H_prob.y - H_prob.x) );
364  return H_prob.y - H_prob.x > 0.0f ? true : false; // NOTE: inversion actually works, but the resulting pdf contains a Dirac delta
365  }
366 
369  CUGAR_FORCEINLINE CUGAR_HOST_DEVICE
371  const DifferentialGeometry& geometry,
372  const Vector3f V,
373  const Vector3f L,
374  const Vector3f u,
375  float& p,
376  float& p_proj) const
377  {
378  const Vector3f N = geometry.normal_s;
379 
380  p_proj = 1.0f / cugar::max(this->p(geometry, V, L, kProjectedSolidAngle), 1.0e-8f);
381  p = p_proj / cugar::max(fabsf(dot(L, N)), 1.0e-8f);
382  }
383 
384 public:
385  float roughness;
386  float inv_roughness;
387 };
388 
392 } // namespace cugar
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float3 hvd_ggx_sample(const float2 &samples, const float inv_alpha)
Definition: ggx_common.h:115
SphericalMeasure
Definition: differential_geometry.h:50
Defines the GGX BSDF.
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, const SphericalMeasure measure=kProjectedSolidAngle) const
Definition: ggx.h:225
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float hvd_ggx_eval(const float2 &inv_alpha, const float nh, const float ht, const float hb)
Definition: ggx_common.h:92
Definition: ggx.h:132
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE void sample(const Vector3f u, const DifferentialGeometry &geometry, const Vector3f V, Vector3f &H, float &p) const
Definition: ggx.h:88
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE void inverse_pdf(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, const Vector3f u, float &p, float &p_proj) const
Definition: ggx.h:370
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f H) const
Definition: ggx.h:64
Definition: differential_geometry.h:59
float random()
Definition: tiled_sampling.h:44
Define a vector_view POD type and plain_view() for std::vector.
Definition: diff.h:38
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE void sample(const Vector3f u, const DifferentialGeometry &geometry, const Vector3f V, Vector3f &L, Vector3f &g, float &p, float &p_proj) const
Definition: ggx.h:251
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE Vector3f f_over_p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L) const
Definition: ggx.h:171
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE void f_and_p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, Vector3f &f, float &p, const SphericalMeasure measure=kProjectedSolidAngle) const
Definition: ggx.h:193
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE bool invert(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, RandomGeneratorT &random, Vector3f &z, float &p, float &p_proj) const
Definition: ggx.h:306
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float2 hvd_ggx_invert(const float3 H, const float2 inv_alpha)
Definition: ggx_common.h:238
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE Vector3f f(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L) const
Definition: ggx.h:142