Skip to content

Conversation

devshgraphicsprogramming
Copy link
Member

@devshgraphicsprogramming devshgraphicsprogramming commented Sep 16, 2025

Description

Continues #899 , #916 and #919

Testing

TODO list:

Comment on lines 286 to 287
template<typename C=bool_constant<!IsAnisotropic>, typename T=conditional_t<IsBSDF, vector3_type, vector2_type> >
enable_if_t<C::value && !IsAnisotropic, sample_type> generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const T u, NBL_REF_ARG(isocache_type) cache)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use conditional_t directly in a function signature, there's no need to add another template parameter T

P.S. also try to use NBL_FUNC_REQUIRES instead of the enable_if_t

Comment on lines 320 to 322
fresnel_type _f = fresnel;
NBL_IF_CONSTEXPR(IsBSDF)
_f = impl::getOrientedFresnel<fresnel_type, IsBSDF>::__call(fresnel, interaction.getNdotV());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

either don't passIsBSDF to impl::getOrientedFresnel or skip the NBL_IF_CONSTEXPR

spectral_type quo = hlsl::promote<spectral_type>(0.0);
const bool notTIR = impl::check_TIR_helper<fresnel_type, IsBSDF>::template __call<MicrofacetCache>(_f, cache);
if (notTIR)
assert(notTIR);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a comment why

Comment on lines 409 to 418
dmq.microfacetMeasure = dg1;
dmq.projectedLightMeasure = dg1;// TODO: figure this out * _sample.getNdotL(BxDFClampMode::BCM_MAX);
return dmq;
}
template<class LS, class Interaction, typename C=bool_constant<IsBSDF> NBL_FUNC_REQUIRES(LightSample<LS> && RequiredInteraction<Interaction>)
enable_if_t<C::value && IsBSDF, quant_type> DG1(NBL_CONST_REF_ARG(dg1_query_type) query, NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction)
{
scalar_type dg1 = base_type::template DG1<dg1_query_type>(query);
quant_type dmq;
dmq.microfacetMeasure = dg1; // note: microfacetMeasure/2NdotV
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if your base DG1 returns something else than actual DG1 like DG1_over_4NdotV, then it should be called DG1_over_4NdotV

then you assign straight to projectedLightMeasure in the reflective case and properly multiply the 4*NdotV back in for the microfacetMeasure

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally you should really just return DG1_over_2NdotV cause extra 0.5 mul is kinda pointless inside

Comment on lines 420 to 426
const scalar_type VdotHLdotH = quant_query.getVdotHLdotH();
const scalar_type VdotH_etaLdotH = quant_query.getVdotH_etaLdotH();
const bool transmitted = reflect_refract==MTT_REFRACT || (reflect_refract!=MTT_REFLECT && VdotHLdotH < scalar_type(0.0));
scalar_type NdotL_over_denominator = _sample.getNdotL(BxDFClampMode::BCM_ABS);
if (transmitted)
NdotL_over_denominator *= -scalar_type(4.0) * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH);
dmq.projectedLightMeasure = dg1;// TODO: figure this out * NdotL_over_denominator;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the projectedLightMeasure transmittance you'd just do mix(0.5,2.0,transmitted)*DG1_over_2NdotV and then

if (transmitted) 
   dmq.projectedLightMeasure *= VdotHLdotH*neg_rcp2_VdotH_etaLdotH;
dmq.microfacetMeasure *= 2.0*clampedNdotV;

Comment on lines 378 to 428
template<class LS, class Interaction, class MicrofacetCache, typename C=bool_constant<!IsBSDF> NBL_FUNC_REQUIRES(LightSample<LS> && RequiredInteraction<Interaction> && RequiredMicrofacetCache<MicrofacetCache>)
enable_if_t<C::value && !IsBSDF, quant_type> D(NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
scalar_type d = __ndf_base.template D<MicrofacetCache>(cache);
quant_type dmq;
dmq.microfacetMeasure = d;
dmq.projectedLightMeasure = d * _sample.getNdotL(BxDFClampMode::BCM_MAX);
return dmq;
}
template<class LS, class Interaction, class MicrofacetCache, typename C=bool_constant<IsBSDF> NBL_FUNC_REQUIRES(LightSample<LS> && RequiredInteraction<Interaction> && RequiredMicrofacetCache<MicrofacetCache>)
enable_if_t<C::value && IsBSDF, quant_type> D(NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
scalar_type d = __ndf_base.template D<MicrofacetCache>(cache);
quant_type dmq;
dmq.microfacetMeasure = d; // note: microfacetMeasure/2NdotV

const scalar_type VdotHLdotH = quant_query.getVdotHLdotH();
const scalar_type VdotH_etaLdotH = quant_query.getVdotH_etaLdotH();
const bool transmitted = reflect_refract==MTT_REFRACT || (reflect_refract!=MTT_REFLECT && VdotHLdotH < scalar_type(0.0));
scalar_type NdotL_over_denominator = _sample.getNdotL(BxDFClampMode::BCM_ABS);
if (transmitted)
NdotL_over_denominator *= -scalar_type(4.0) * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH);
dmq.projectedLightMeasure = d * NdotL_over_denominator;
return dmq;
}

template<class LS, class Interaction, typename C=bool_constant<!IsBSDF> NBL_FUNC_REQUIRES(LightSample<LS> && RequiredInteraction<Interaction>)
enable_if_t<C::value && !IsBSDF, quant_type> DG1(NBL_CONST_REF_ARG(dg1_query_type) query, NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction)
{
scalar_type dg1 = base_type::template DG1<dg1_query_type>(query);
quant_type dmq;
dmq.microfacetMeasure = dg1;
dmq.projectedLightMeasure = dg1;// TODO: figure this out * _sample.getNdotL(BxDFClampMode::BCM_MAX);
return dmq;
}
template<class LS, class Interaction, typename C=bool_constant<IsBSDF> NBL_FUNC_REQUIRES(LightSample<LS> && RequiredInteraction<Interaction>)
enable_if_t<C::value && IsBSDF, quant_type> DG1(NBL_CONST_REF_ARG(dg1_query_type) query, NBL_CONST_REF_ARG(quant_query_type) quant_query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction)
{
scalar_type dg1 = base_type::template DG1<dg1_query_type>(query);
quant_type dmq;
dmq.microfacetMeasure = dg1; // note: microfacetMeasure/2NdotV

const scalar_type VdotHLdotH = quant_query.getVdotHLdotH();
const scalar_type VdotH_etaLdotH = quant_query.getVdotH_etaLdotH();
const bool transmitted = reflect_refract==MTT_REFRACT || (reflect_refract!=MTT_REFLECT && VdotHLdotH < scalar_type(0.0));
scalar_type NdotL_over_denominator = _sample.getNdotL(BxDFClampMode::BCM_ABS);
if (transmitted)
NdotL_over_denominator *= -scalar_type(4.0) * VdotHLdotH / (VdotH_etaLdotH * VdotH_etaLdotH);
dmq.projectedLightMeasure = dg1;// TODO: figure this out * NdotL_over_denominator;
return dmq;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stuff maybe we can rethink, I'd be tempted to allow an NDF "skip" providing D and correlated if it has both the DG1 and Dcorrelated methods (so the cook torrance can leverage that and skip multiplying D with correlated by itself for eval)

NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = ndf_type::NDFSurfaceType != ndf::MTT_REFLECT;

template<class Interaction, class MicrofacetCache>
static bool __checkValid(NBL_CONST_REF_ARG(fresnel_type) f, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why make static and pass the f if its your member?

Copy link
Member Author

@devshgraphicsprogramming devshgraphicsprogramming Oct 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it because the fresnel is already properly oriented? then give f a clear name like reorientedFresnel

and since its static you may as well make the __checkValid into a standalone struct functor with a partial spec on IsBSDF, that way you don't call impl::check_TIR_helper but you can call cache.isValid(reorientedFresnel.getRefractionOrientedEta()) directly

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually if you use impl::checkValid<IsBSDF>::template __call() here

const bool notTIR = impl::check_TIR_helper<fresnel_type, IsBSDF>::template __call<MicrofacetCache>(_f, cache);
assert(notTIR);

then there's no need to have an impl::check_TIR_helper at all

Comment on lines 275 to 276
else
dmq.projectedLightMeasure = d * _sample.getNdotL(BxDFClampMode::BCM_MAX);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw you can always use ABS, because it should be the callee's job to not call you on backfacing BRDF if it chooses to

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just the NDF

Comment on lines 667 to 669
bool isValid(const scalar_type eta) NBL_CONST_MEMBER_FUNC
{
fresnel::OrientedEtas<monochrome_type> orientedEta = fresnel::OrientedEtas<monochrome_type>::create(scalar_type(1.0), hlsl::promote<monochrome_type>(eta));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get what you're doing here

Comment on lines 853 to 855
bool isValid(const scalar_type eta) NBL_CONST_MEMBER_FUNC
{
return iso_cache.isValid(orientedEta);
return iso_cache.isValid(eta);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get whats going on here

Comment on lines +831 to +832
const scalar_type viewShortenFactor = hlsl::mix(scalar_type(1.0), rcpOrientedEta.value[0], transmitted);
retval.iso_cache.VdotL = VdotH * (VdotH * viewShortenFactor + LdotH) - viewShortenFactor;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even though I gave you this, it might be invalid.

refl -> VdotH*(VdotH+LdotH) - 1 = 2*VdotH^2 -1
trans -> VdotH*(VdotH/eta+LdotH) - 1/eta != VdotH^2/eta - VdotH/eta + VdotH*LdotH

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeh its messed up.

Need to bring back the original with one slight opt

retval.iso_cache.VdotL = VdotH * hlsl::mix(
    scalar_type(2.0) * VdotH - scalar_type(1.0),
    VdotH * rcpOrientedEta.value[0] + LdotH - rcpOrientedEta.value[0],
    transmitted
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants