-
Notifications
You must be signed in to change notification settings - Fork 15
Complete bxdf tests #236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Complete bxdf tests #236
Conversation
…ng/Nabla-Examples-and-Tests into bxdf_unit_tests
…ng/Nabla-Examples-and-Tests into bxdf_unit_tests
| if NBL_CONSTEXPR_FUNC (aniso) | ||
| NBL_IF_CONSTEXPR(aniso) | ||
| { | ||
| pdf = base_t::bxdf.quotient_and_pdf(s, base_t::anisointer, cache); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pdf is a really bad variable name for this, it leads to unreadability pathologies in code like pdf.pdf or worse pdf.quotient
| if (checkZero<float32_t3>(bsdf, 1e-5) || checkZero<float32_t3>(pdf.quotient, 1e-5)) | ||
| return BET_NONE; // produces an "impossible" sample | ||
| return BTR_NONE; // likely to be that a bad sample was produced, unless it's a mixture/delta BxDF |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve the comment
before we've checked if:
1. PDF is positive (you return no error if its not)
2. qutient is positive and (1) already checked
So if we must have `bsdf == quotient*pdf` , then bsdf must also be positive.
However for mixture of, or singular delta BxDF the bsdf can be less due to removal of Dirac-Delta lobes from the eval method, which is why allow `BTR_NONE` in this case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GH UI shows outdated, but still needs to be done
| if (hlsl::isnan(pdf.pdf)) | ||
| return BTR_NONE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pdf should never return NAN (because it will get used in MIS weights), that should be a separate error on its own!
| // infinite PDF and zero jacobian are both valid behaviors | ||
| if (!checkZero<float>(det, 1e-3) && !checkZero<float>(det * pdf.pdf / s.getNdotL(), 1e-4)) | ||
| return BTR_ERROR_JACOBIAN_TEST_FAIL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just check if PDF is inf AND jacobian is 0 and return BTR_NONE
because right now you check Jacobian!=0 AND Jacobian*pdf/NdotL != 0 for the error which is a completely wrong test
actually why are you not failing here all the time!? Literally every BXDF would fail this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
basically
if (isinf(pdf))
{
// if pdf is infinite then density is infinite and no differential area inbetween samples
if (det!=0)
return BTR_ERROR_JACOBIAN_TEST_FAIL;
// valid behaviour, but obviously can't check bsdf = quotient*pdf
return BTR_NONE;
}
else if (det==0)
return BTR_ERROR_JACOBIAN_TEST_FAIL;
if (!checkEq(pdf.value,bsdf))
{
... the usual stuff ...
return BTR_ERROR_PDF_EVAL_DIFF;
}
return BTR_NONE;| template<typename T> | ||
| bool checkEq(T a, T b, float32_t eps) | ||
| { | ||
| T _a = hlsl::max(hlsl::abs(a), hlsl::promote<T>(1e-5)); | ||
| T _b = hlsl::max(hlsl::abs(b), hlsl::promote<T>(1e-5)); | ||
| return nbl::hlsl::all<hlsl::vector<bool, vector_traits<T>::Dimension> >(nbl::hlsl::max<T>(_a / _b, _b / _a) <= hlsl::promote<T>(1 + eps)); | ||
| } | ||
|
|
||
| template<> | ||
| bool checkEq<float32_t>(float32_t a, float32_t b, float32_t eps) | ||
| { | ||
| float32_t _a = hlsl::max(hlsl::abs(a), 1e-5f); // prevent divide by 0 | ||
| float32_t _b = hlsl::max(hlsl::abs(b), 1e-5f); | ||
| return nbl::hlsl::max<float32_t>(_a / _b, _b / _a) <= float32_t(1 + eps); | ||
| return testing::relativeApproxCompare<T>(a, b, eps); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just remove checkEq and replace all usages with testing::relativeApproxCompare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GH UI shows outdated, but still needs to be done
| if (pdf.pdf < bit_cast<float>(numeric_limits<float>::min)) // there's exceptional cases where pdf=0, so we check here to avoid adding all edge-cases, but quotient must be positive afterwards | ||
| return BTR_NONE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you should check pdf.pdf being non-negative before returning BTR_NONE for a 0 pdf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you need to rename BTR_ERROR_GENERATED_SAMPLE_NON_POSITIVE_PDF to BTR_ERROR_GENERATED_SAMPLE_NEGATIVE_PDF or just remove it as an enum and just use BTR_ERROR_NEGATIVE_VAL
| float32_t3 ux = base_t::rc.u + float32_t3(base_t::rc.eps,0,0); | ||
| float32_t3 uy = base_t::rc.u + float32_t3(0,base_t::rc.eps,0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok I think we have a problem here. The domain where forward finite differences are
well-defined is the interior
| retval.u.x = hlsl::clamp(retval.u.x, retval.eps, 1.f-retval.eps); |
Tho the input generation and clamp allows the boundary value u.x = 1 − ε to occur.
| return ret_t(x) / hlsl::promote<ret_t>(numeric_limits<uint32_t>::max); |
Since the Jacobian test uses forward finite differences ux = u + (ε, 0)
| float32_t3 ux = base_t::rc.u + float32_t3(base_t::rc.eps,0,0); |
the boundary case leads to
and for a partial derivative at
At the boundary
which means the Jacobian here is evaluated outside the differentiability domain and we rely on whatever generate() does (clamp/wrap/invalid value? does all of them know what to do in such situation?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and we rely on whatever generate()
even if generate() handles such inputs in a defined way, the finite difference is evaluated outside the differentiability domain. In that case we either differentiate a different function or rely on implementation specific behavior, so then the Jacobian test is mathematically invalid regardless
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eg beckmann
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the solution is to not generate [0,1]^2 inputs, but [0,1-epsilon]^2 inputs
Or better, geneerate [0,1] but when generated number is >1-epsilon we don't do a "positive difference" but "negative instead" (so we take our s_x sample by subtracting epsilon, then correct the sign diff throughout)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
u.xy is in [epsilon,1-epsilon]^2, the problem is what goes on FD when u.x or u.y is 1-eps, then ux or uy is 1 which is outside input domain. Next it enters each generate(). The base sample is in-domain, but the forward-FD sample can step out of the declared domain.
If the domain were [0,1]^2 the same issue would still exist at the boundary u.x=1 (or u.y) because 1+eps is outside
| float32_t3 ux = base_t::rc.u + float32_t3(base_t::rc.eps,0,0); |
Jacobian requires differentiability at point, even if a one sided derivative exists at 1 the function is not differentiable at this point (my previous arg about open neighborhood around the point), which discards the Jacobian test at this point
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a choice of how to handle when we get too close to 1.0 on the point:
- don't do the test (discard/exit with positive non-zero code
- switch from Forward to Backward
differentiationdifferences
either is valid, the jacobian test is only approximate
| return BTR_NONE; | ||
| } | ||
| else | ||
| else if (checkZero<float>(det, numeric_limits<float>::min)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is incorrect, det here is det(m) which comes from forward differences so it scales with the step eps. A constant threshold like numeric_limits<float>::min is in the wrong units. You need to normalize by eps^2 (or scale your tolerance by eps^2). It should be more or less
constexpr auto tol = numeric_limits<float>::min(); // example (not best probably)
checkZero(det, tol * eps * eps);To recap:
first-order Taylor expansion is
so
Counterexample why it matters https://godbolt.org/z/MnfMzYcE7
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sense I guess
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also seems done
| float32_t3 u = hlsl::min(base_t::rc.u, hlsl::promote<float32_t3>(1.0-2.0*eps)); | ||
| float32_t3 ux = u + float32_t3(eps,0,0); | ||
| float32_t3 uy = u + float32_t3(0,eps,0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I found a bug. FD can cross the Cook‑Torrance reflect/refract branch which invalidates Jacobian test for certain points.
Its because the reflect/refract choice is a hard threshold depending on u.xy, so FD perturbations can flip the branch and then sx-s is a jump (~2/eps), its not a derivative and the Jacobian test cannot be valid there. The mapping is not differentiable
Counterexample https://godbolt.org/z/E8KM8xE3b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lambertian and Oren‑Nayar have similar issue and can hit FD at points where the mapping is not differentiable. Both go through ProjectedHemisphere, then ConcentricMapping which is piecewise with a hard branch on
if (std::abs(ux) > std::abs(uy)) { ... } else { ... }FD step in u.x or u.y can cross that branch near the diagonal, then it measures a jump between two branches, its not a derivative and the Jacobian test is invalid at those samples.
There is plenty of those points in our domain
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it can cross the Reflect/Refract branch, because the choice of reflect vs refract is done on the Z coordinate of the random variable
scalar_type z = u.z;
...
bool transmitted = partitionRandVariable(z, rcpChoiceProb);Me and sorakrit thought about it when designing the tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because the choice of reflect vs refract is done on the Z coordinate of the random variable
but the threshold is not and that's why we can have the branch flip with fixed u.z. FD changes in u.xy
Nabla-Examples-and-Tests/66_HLSLBxDFTests/app_resources/test_components.hlsl
Lines 18 to 20 in 7017217
| float32_t3 u = hlsl::min(base_t::rc.u, hlsl::promote<float32_t3>(1.0-2.0*eps)); | |
| float32_t3 ux = u + float32_t3(eps,0,0); | |
| float32_t3 uy = u + float32_t3(0,eps,0); |
u.xy is plugged into
const vector3_type localH = ndf.generateH(upperHemisphereV, u.xy);then
VdotH = dot(localV, localH)and
const scalar_type reflectance = __getScaledReflectance(_f, interaction, hlsl::abs(VdotH), false, dummy);
// ..
sampling::PartitionRandVariable<scalar_type> partitionRandVariable;
partitionRandVariable.leftProb = reflectance; // !!then we finally have
note that leftProb depends on u.xy, henceforth constant u.z does not save our situation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First thing we need to change
float32_t3 u = hlsl::min(base_t::rc.u, float32_t3(1.0-2.0*eps,1.0-2.0*eps,1.0)); There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok I see exactly what you're getting at now....
This means we need to run the test with either u.z=0 or u.z=1 and no value in between
| rec_cache.iso_cache.VdotH = cache.iso_cache.getLdotH(); | ||
| rec_cache.iso_cache.LdotH = cache.iso_cache.getVdotH(); | ||
| rec_isocache = isocache; | ||
| rec_isocache.VdotH = isocache.getLdotH(); | ||
| rec_isocache.LdotH = isocache.getVdotH(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This breaks anisotropic reciprocity. rec_cache is copied from forward and only VdotH/LdotH are swapped but TdotH/BdotH stay from the forward H. Anisotropic NDFs GGX and Beckmann use getTdotH2/getBdotH2 so reciprocal eval is fed a forward half‑vector, making reciprocity test invalid for anisotropic microfacets.
Counterexample https://godbolt.org/z/EnrnTxevG
| ); | ||
| float det = nbl::hlsl::determinant<float32_t2x2>(m) / (eps * eps); | ||
|
|
||
| float jacobi_dg1_ndoth = det * dg1 / hlsl::abs(s.getNdotL()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s.getNdotL() can be 0 or near, the sample is still valid since s.isValid only checks L!=0 so this division can produce inf/NaN and trigger a false Jacobian fail
note we already guard NdotV earlier but not NdotL here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm good catch
| bool32_t3 diff_test = nbl::hlsl::max<float32_t3>(pdf.value() / bsdf, bsdf / pdf.value()) <= (float32_t3)(1 + base_t::rc.eps); | ||
| if (!all<bool32_t3>(diff_test)) | ||
| return PDF_EVAL_DIFF; | ||
| float32_t3 a = Li / hlsl::abs(s.getNdotL()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same NdotL issue as in TestNDF, 0 or near -> false fail #236 (comment)
| if (verbose) | ||
| base_t::errMsg += std::format("VdotH={}, NdotV={}, LdotH={}, NdotL={}, NdotH={}, eta={}, alpha=[{},{}] Jacobian={}, DG1={}, Jacobian*DG1={}", | ||
| aniso ? cache.getVdotH() : isocache.getVdotH(), aniso ? base_t::anisointer.getNdotV() : base_t::isointer.getNdotV(), | ||
| aniso ? cache.getLdotH() : isocache.getLdotH(), s.getNdotL(), NdotH, base_t::rc.eta.x, base_t::rc.alpha.x, base_t::rc.alpha.y, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NdotH is not assigned value, are we printing garbage?
| retval.alpha.x = ConvertToFloat01<uint32_t>::__call(retval.rng()); | ||
| retval.alpha.y = ConvertToFloat01<uint32_t>::__call(retval.rng()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no clamp on RNG -> alpha can be 0. initBxDF feeds that into NDF (ax/ay/a2) and Beckmann/GGX's D divides by ax2/ay2/a2 -> inf/NaN.
| base_t::bxdf.ndf = base_t::bxdf_t::ndf_type::create(_rc.alpha.x); |
| base_t::bxdf.ndf = base_t::bxdf_t::ndf_type::create(_rc.alpha.x, _rc.alpha.y); |
| base_t::bxdf.ndf = base_t::bxdf_t::ndf_type::create(_rc.alpha.x); |
| base_t::bxdf.ndf = base_t::bxdf_t::ndf_type::create(_rc.alpha.x, _rc.alpha.y); |
TestNDF handles isInfinity with skip, ok
Nabla-Examples-and-Tests/66_HLSLBxDFTests/app_resources/test_components.hlsl
Lines 94 to 95 in 7017217
| if (isNdfInfinity) | |
| return BTR_INVALID_TEST_CONFIG; |
however BxDF tests call quotient_and_pdf
| sampledLi = base_t::bxdf.quotient_and_pdf(s, base_t::isointer); |
In cook_torrance_base, pdf() masks isInfinity -> 0 but quotient_and_pdf() does not
https://github.com/Devsh-Graphics-Programming/Nabla/blob/c26719ac178c943362c12b25aa53644653df6954/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl#L365
https://github.com/Devsh-Graphics-Programming/Nabla/blob/c26719ac178c943362c12b25aa53644653df6954/include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl#L373
So pdf can stay inf and the test rejects it unless det≈0:
| if (hlsl::isinf(sampledLi.pdf)) |
| const float NdotL = s.getNdotL(); | ||
| if (NdotV * NdotL < 0.f) | ||
| tmpeta = NdotV < 0.f ? 1.f / eta : eta; | ||
| float32_t3 H = hlsl::normalize(V.getDirection() + L.getDirection() * tmpeta); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this builds H manually and our sign logic diverges from ComputeMicrofacetNormal for transmission (esp NdotV<0), hence chi2 compares against a different pdf and the test is invalid
Nabla-Examples-and-Tests/66_HLSLBxDFTests/tests.h
Lines 158 to 185 in 7017217
| float tmpeta = 1.f; | |
| NBL_IF_CONSTEXPR(traits_t::IsMicrofacet) | |
| { | |
| const float NdotV = anisointer.getNdotV(); | |
| NBL_IF_CONSTEXPR(traits_t::type == bxdf::BT_BRDF) | |
| if (NdotV < 0.f) return 0.f; | |
| const float NdotL = s.getNdotL(); | |
| if (NdotV * NdotL < 0.f) | |
| tmpeta = NdotV < 0.f ? 1.f / eta : eta; | |
| float32_t3 H = hlsl::normalize(V.getDirection() + L.getDirection() * tmpeta); | |
| float VdotH = hlsl::dot(V.getDirection(), H); | |
| if (NdotV * VdotH < 0.f) | |
| { | |
| H = -H; | |
| VdotH = -VdotH; | |
| } | |
| cache.iso_cache.VdotH = VdotH; | |
| cache.iso_cache.LdotH = hlsl::dot(L.getDirection(), H); | |
| cache.iso_cache.VdotL = hlsl::dot(V.getDirection(), L.getDirection()); | |
| cache.iso_cache.absNdotH = hlsl::abs(hlsl::dot(N, H)); | |
| cache.iso_cache.NdotH2 = cache.iso_cache.absNdotH * cache.iso_cache.absNdotH; | |
| if (!cache.isValid(bxdf::fresnel::OrientedEtas<hlsl::vector<float, 1> >::create(1.f, hlsl::promote<hlsl::vector<float, 1> >(tmpeta)))) | |
| return 0.f; | |
| cache.fillTangents(T, B, H); |
https://github.com/Devsh-Graphics-Programming/Nabla/blob/3ce0d89aef69ff7bb52b3b784b6b58228040a629/include/nbl/builtin/hlsl/bxdf/fresnel.hlsl#L101-L166
counterexample: https://godbolt.org/z/5q6oe59ra
| pdfSinTheta.isointer = base_t::isointer; | ||
| pdfSinTheta.anisointer = base_t::anisointer; | ||
| pdfSinTheta.eta = base_t::rc.eta.x; | ||
| integrateFreq[intidx++] = numSamples * math::quadrature::AdaptiveSimpson2D<CalculatePdfSinTheta<BxDF, aniso>, float>::__call( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expected frequencies are scaled by numSamples even though invalid samples are skipped above. Observed and expected therefore correspond to different underlying distributions which biases chi2 and makes the test invalid
Nabla-Examples-and-Tests/66_HLSLBxDFTests/tests.h
Lines 329 to 330 in 7017217
| integrateFreq[intidx++] = numSamples * math::quadrature::AdaptiveSimpson2D<CalculatePdfSinTheta<BxDF, aniso>, float>::__call( | |
| pdfSinTheta, float32_t2(i * thetaFactor, j * phiFactor), float32_t2((i + 1) * thetaFactor, (j + 1) * phiFactor)); |
counterexample: https://godbolt.org/z/4YY5889rf
even if we wanted to “test with rejection” right now observed comes from
| if (hlsl::isinf(sampledLi.pdf)) | ||
| accumulatedQuotient += sampledLi.quotient; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this accumulates only when pdf==inf, so for all continuous BxDFs with finite pdf the test never adds anything and becomes a false pass
we do run this test on continuous BxDFs (Lambert/Oren/GGX/Beckmann/etc) hence it will silently pass for all of them
Nabla-Examples-and-Tests/66_HLSLBxDFTests/main.cpp
Lines 287 to 301 in 7017217
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::reflection::SLambertian<iso_config_t>>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::reflection::SOrenNayar<iso_config_t>>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::reflection::SBeckmannIsotropic<iso_microfacet_config_t>, false>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::reflection::SBeckmannAnisotropic<aniso_microfacet_config_t>, true>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::reflection::SGGXIsotropic<iso_microfacet_config_t>, false>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::reflection::SGGXAnisotropic<aniso_microfacet_config_t>, true>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::reflection::SIridescent<iso_microfacet_config_t>, false>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::transmission::SLambertian<iso_config_t>>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::transmission::SOrenNayar<iso_config_t>>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::transmission::SBeckmannDielectricIsotropic<iso_microfacet_config_t>, false>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::transmission::SBeckmannDielectricAnisotropic<aniso_microfacet_config_t>, true>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::transmission::SGGXDielectricIsotropic<iso_microfacet_config_t>, false>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::transmission::SGGXDielectricAnisotropic<aniso_microfacet_config_t>, true>), initparams); | |
| RUN_TEST_OF_TYPE((TestModifiedWhiteFurnace<bxdf::transmission::SIridescent<iso_microfacet_config_t>, false>), initparams); |
| ErrorType test() | ||
| { | ||
| compute(); | ||
| transmitted = aniso ? cache.isTransmission() : isocache.isTransmission(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cache/isocache are uninitialized for non‑microfacet paths but we read them here (transmitted)
Nabla-Examples-and-Tests/66_HLSLBxDFTests/app_resources/tests.hlsl
Lines 220 to 238 in 7017217
| NBL_IF_CONSTEXPR(traits_t::type == bxdf::BT_BRDF && !traits_t::IsMicrofacet) | |
| { | |
| s = base_t::bxdf.generate(anisointer, base_t::rc.u.xy); | |
| } | |
| NBL_IF_CONSTEXPR(traits_t::type == bxdf::BT_BRDF && traits_t::IsMicrofacet) | |
| { | |
| NBL_IF_CONSTEXPR(aniso) | |
| { | |
| s = base_t::bxdf.generate(anisointer, base_t::rc.u.xy, cache); | |
| } | |
| else | |
| { | |
| s = base_t::bxdf.generate(isointer, base_t::rc.u.xy, isocache); | |
| } | |
| } | |
| NBL_IF_CONSTEXPR(traits_t::type == bxdf::BT_BSDF && !traits_t::IsMicrofacet) | |
| { | |
| s = base_t::bxdf.generate(anisointer, base_t::rc.u); | |
| } |
we need a guard with traits_t::IsMicrofacet or init the caches
No description provided.