@@ -252,44 +252,107 @@ struct SCookTorrance
252252 const vector3_type localH = ndf.generateH (upperHemisphereV, u.xy);
253253 const scalar_type VdotH = hlsl::dot (localV, localH);
254254
255- assert (NdotV * VdotH > scalar_type (0.0 ));
255+ NBL_IF_CONSTEXPR (!ndf_type::GuaranteedVNDF)
256+ {
257+ // allow for rejection sampling, theoretically NdotV=0 or VdotH=0 is valid, but leads to 0 value contribution anyway
258+ if ((IsBSDF ? (NdotV*VdotH) : VdotH) <= scalar_type (0.0 ))
259+ return sample_type::createInvalid ();
260+ assert (!hlsl::isnan (NdotV*VdotH));
261+ }
262+ else
263+ {
264+ assert (NdotV*VdotH >= scalar_type (0.0 ));
265+ }
256266 const scalar_type reflectance = _f (hlsl::abs (VdotH))[0 ];
257267
258268 scalar_type rcpChoiceProb;
259269 scalar_type z = u.z;
260270 bool transmitted = math::partitionRandVariable (reflectance, z, rcpChoiceProb);
261271
262272 ray_dir_info_type V = interaction.getV ();
263- const vector3_type H = hlsl::mul (interaction.getFromTangentSpace (), localH);
264- Refract<scalar_type> r = Refract<scalar_type>::create (V.getDirection (), H);
265- const scalar_type LdotH = hlsl::mix (VdotH, r.getNdotT (rcpEta.value2[0 ]), transmitted);
266-
267- // fail if samples have invalid paths
268- const scalar_type viewShortenFactor = hlsl::mix (scalar_type (1.0 ), rcpEta.value[0 ], transmitted);
269- const scalar_type NdotL = localH.z * (VdotH * viewShortenFactor + LdotH) - NdotV * viewShortenFactor;
270- // VNDF sampling guarantees that `VdotH` has same sign as `NdotV`
271- // and `transmitted` controls the sign of `LdotH` relative to `VdotH` by construction (reflect -> same sign, or refract -> opposite sign)
273+ const vector3_type H = hlsl::normalize (hlsl::mul (interaction.getFromTangentSpace (), localH));
274+
275+ // TODO: UNDER CONSTRUCTION, will uncomment when sure basic stuff passes tests
276+ // Refract<scalar_type> r = Refract<scalar_type>::create(V.getDirection(), H);
277+ // const scalar_type LdotH = hlsl::mix(VdotH, r.getNdotT(rcpEta.value2[0]), transmitted);
278+
279+ // // fail if samples have invalid paths
280+ // const scalar_type viewShortenFactor = hlsl::mix(scalar_type(1.0), rcpEta.value[0], transmitted);
281+ // const scalar_type NdotL = localH.z * (VdotH * viewShortenFactor + hlsl::abs(LdotH)) - NdotV * viewShortenFactor;
282+ // // VNDF sampling guarantees that `VdotH` has same sign as `NdotV`
283+ // // and `transmitted` controls the sign of `LdotH` relative to `VdotH` by construction (reflect -> same sign, or refract -> opposite sign)
284+ // if (ComputeMicrofacetNormal<scalar_type>::isTransmissionPath(NdotV, NdotL) != transmitted)
285+ // return sample_type::createInvalid(); // should check if sample direction is invalid
286+
287+ // cache = anisocache_type::createPartial(VdotH, LdotH, localH.z, transmitted, rcpEta);
288+ // assert(cache.isValid(_f.getRefractionOrientedEta()));
289+
290+ // struct reflect_refract_wrapper // so we don't recalculate LdotH
291+ // {
292+ // vector3_type operator()(const bool doRefract, const scalar_type rcpOrientedEta) NBL_CONST_MEMBER_FUNC
293+ // {
294+ // return rr(NdotTorR, rcpOrientedEta);
295+ // }
296+ // bxdf::ReflectRefract<scalar_type> rr;
297+ // scalar_type NdotTorR;
298+ // };
299+ // bxdf::ReflectRefract<scalar_type> rr; // rr.getNdotTorR() and calls to mix as well as a good part of the computations should CSE with our computation of NdotL above
300+ // rr.refract = r;
301+ // reflect_refract_wrapper rrw;
302+ // rrw.rr = rr;
303+ // rrw.NdotTorR = LdotH;
304+ // ray_dir_info_type L = V.template reflectRefract<reflect_refract_wrapper>(rrw, transmitted, rcpEta.value[0]);
305+
306+ ray_dir_info_type L;
307+ if (transmitted)
308+ {
309+ // scalar_type eta = rcpEta.value[0]; // refraction takes eta as ior_incoming/ior_transmitted due to snell's law
310+ // vector3_type orientedH = ieee754::flipSignIfRHSNegative<vector3_type>(H, hlsl::promote<vector3_type>(NdotV));
311+ // scalar_type cosThetaI = hlsl::dot(V.getDirection(), orientedH);
312+ // scalar_type sin2ThetaI = hlsl::max(scalar_type(0), scalar_type(1) - cosThetaI * cosThetaI);
313+ // scalar_type sin2ThetaT = eta * eta * sin2ThetaI;
314+
315+ // if (sin2ThetaT >= 1) return sample_type::createInvalid();
316+ // scalar_type cosThetaT = hlsl::sqrt(scalar_type(1) - sin2ThetaT);
317+ // L.direction = eta * -V.getDirection() + (eta * cosThetaI - cosThetaT) * orientedH;
318+
319+
320+ Refract<scalar_type> r = Refract<scalar_type>::create (V.getDirection (), H);
321+ bxdf::ReflectRefract<scalar_type> rr;
322+ rr.refract = r;
323+ L = V.reflectRefract (rr, transmitted, rcpEta.value[0 ]);
324+ L.direction = hlsl::normalize (L.direction);
325+ }
326+ else
327+ {
328+ bxdf::Reflect<scalar_type> r = bxdf::Reflect<scalar_type>::create (V.getDirection (), H);
329+ L = V.reflect (r);
330+ }
331+
332+ vector3_type _N = interaction.getN ();
333+ scalar_type NdotL = hlsl::dot (_N, L.getDirection ());
272334 if (ComputeMicrofacetNormal<scalar_type>::isTransmissionPath (NdotV, NdotL) != transmitted)
273335 return sample_type::createInvalid (); // should check if sample direction is invalid
274336
275- cache = anisocache_type::createPartial (VdotH, LdotH, localH.z, transmitted, rcpEta);
337+ cache.iso_cache.VdotH = VdotH;
338+ cache.iso_cache.LdotH = hlsl::dot (L.getDirection (), H);
339+ // cache.iso_cache.VdotL = hlsl::dot(V.getDirection(), L.getDirection());
340+ cache.iso_cache.VdotL = hlsl::mix (scalar_type (2.0 ) * VdotH * VdotH - scalar_type (1.0 ),
341+ VdotH * (VdotH * rcpEta.value[0 ] + cache.iso_cache.LdotH) - rcpEta.value[0 ], transmitted);
342+ // const scalar_type viewShortenFactor = hlsl::mix(scalar_type(1.0), rcpEta.value[0], transmitted);
343+ // scalar_type _VdotL = VdotH * (VdotH * viewShortenFactor + cache.iso_cache.LdotH) - viewShortenFactor;
344+ // scalar_type VdotL = hlsl::dot(V.getDirection(), L.getDirection());
345+ // assert(hlsl::abs(VdotL - cache.iso_cache.VdotL) < 1e-4);
346+ assert (localH.z > scalar_type (0.0 ));
347+ cache.iso_cache.absNdotH = hlsl::abs (localH.z);
348+ cache.iso_cache.NdotH2 = localH.z * localH.z;
349+
276350 assert (cache.isValid (_f.getRefractionOrientedEta ()));
277351
278- struct reflect_refract_wrapper // so we don't recalculate LdotH
279- {
280- vector3_type operator ()(const bool doRefract, const scalar_type rcpOrientedEta) NBL_CONST_MEMBER_FUNC
281- {
282- return rr (NdotTorR, rcpOrientedEta);
283- }
284- bxdf::ReflectRefract<scalar_type> rr;
285- scalar_type NdotTorR;
286- };
287- bxdf::ReflectRefract<scalar_type> rr; // rr.getNdotTorR() and calls to mix as well as a good part of the computations should CSE with our computation of NdotL above
288- rr.refract = r;
289- reflect_refract_wrapper rrw;
290- rrw.rr = rr;
291- rrw.NdotTorR = LdotH;
292- ray_dir_info_type L = V.template reflectRefract<reflect_refract_wrapper>(rrw, transmitted, rcpEta.value[0 ]);
352+ // const scalar_type _viewShortenFactor = hlsl::mix(scalar_type(1.0), rcpEta.value[0], transmitted);
353+ // const scalar_type _NdotL = localH.z * (VdotH * _viewShortenFactor + cache.iso_cache.LdotH) - NdotV * _viewShortenFactor;
354+ scalar_type _NdotL = hlsl::dot (interaction.getN (), L.getDirection ());
355+ assert (hlsl::abs (_NdotL - NdotL) < 1e-4 );
293356
294357 const vector3_type T = interaction.getT ();
295358 const vector3_type B = interaction.getB ();
0 commit comments