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 +97 to +122
#ifndef __HLSL_VERSION
namespace std
{
template<typename T, uint16_t N>
struct hash<nbl::hlsl::vector<T,N> >
{
size_t operator()(const nbl::hlsl::vector<T,N>& v) const noexcept
{
size_t seed = 0;
NBL_UNROLL for (uint16_t i = 0; i < N; i++)
nbl::core::hash_combine(seed, v[i]);
return seed;
}
};

template<typename T>
struct hash<nbl::hlsl::vector<T,1> >
{
size_t operator()(const nbl::hlsl::vector<T,1>& v) const noexcept
{
std::hash<T> hasher;
return hasher(v.x);
}
};
}
#endif
Copy link
Member Author

Choose a reason for hiding this comment

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

are you sure having this specialization globally is a good idea ?

What do you use it for ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also used in the bucket bxdf test, for hash map buckets

Copy link
Member Author

Choose a reason for hiding this comment

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

then define it in that example, nowhere else

Comment on lines 167 to 174
N ndf;
F fresnel;
};

template<class Config, class N, class F>
NBL_PARTIAL_REQ_TOP(config_concepts::MicrofacetConfiguration<Config> && ndf::NDF<N> && fresnel::Fresnel<F>)
struct SCookTorrance<Config, N, F, true NBL_PARTIAL_REQ_BOT(config_concepts::MicrofacetConfiguration<Config> && ndf::NDF<N> && fresnel::Fresnel<F>) >
{
Copy link
Member Author

Choose a reason for hiding this comment

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

any chance at inheriting from a common base for both BRDF and BSDF, so that we don't have so much duplicate code (BRDF and BSDF only differ in how they factor the reflectance into the generation and pdf)

Comment on lines -78 to 82

template<class Query NBL_FUNC_REQUIRES(ggx_concepts::DG1BrdfQuery<Query>)
scalar_type DG1(NBL_CONST_REF_ARG(Query) query)
scalar_type DG1(NBL_CONST_REF_ARG(dg1_query_type) query)
{
return scalar_type(0.5) * query.getNdf() * query.getG1over2NdotV();
}

template<class Query, class MicrofacetCache NBL_FUNC_REQUIRES(ggx_concepts::DG1BsdfQuery<Query> && ReadableIsotropicMicrofacetCache<MicrofacetCache>)
scalar_type DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
scalar_type NG = query.getNdf() * query.getG1over2NdotV();
scalar_type factor = scalar_type(0.5);
if (cache.isTransmission())
{
const scalar_type VdotH_etaLdotH = (cache.getVdotH() + query.getOrientedEta() * cache.getLdotH());
// VdotHLdotH is negative under transmission, so this factor is negative
factor *= -scalar_type(2.0) * cache.getVdotHLdotH() / (VdotH_etaLdotH * VdotH_etaLdotH);
}
return NG * factor;
return query.getNdf() * query.getG1();
}
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 seems silly to keep around

Copy link
Member Author

Choose a reason for hiding this comment

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

your query its just multiplying its two members

static scalar_type G1_wo_numerator_devsh_part(scalar_type absNdotX, scalar_type devsh_part)
{
// numerator is 2 * NdotX
return scalar_type(1.0) / (absNdotX + devsh_part);
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 are the two methods needed?

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'd think you only need G1_wo_numerator_devsh_part(scalar_type absNdotX, scalar_type devsh_part)

Comment on lines +39 to +40
scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; }
scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; }
Copy link
Member Author

Choose a reason for hiding this comment

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

its fine to have a query like this but we need a concept and template everything on it

}

vector<scalar_type, 2> A;
vector<scalar_type, 2> A; // TODO: remove?
Copy link
Member Author

Choose a reason for hiding this comment

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

yes!

Comment on lines 109 to 119
scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache)
{
using quant_query_type = typename N::quant_query_type;
using dg1_query_type = typename N::dg1_query_type;

scalar_type dummy;
quant_query_type qq = ndf.template createQuantQuery<isocache_type>(cache, dummy);
dg1_query_type dq = ndf.template createDG1Query<isotropic_interaction_type, isocache_type>(interaction, cache);
quant_type DG1 = ndf.template DG1<sample_type, isotropic_interaction_type>(dq, qq, _sample, interaction);
return DG1.projectedLightMeasure;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

the isotropic methods need enable_if because you need to check if the NDF used is isotropic or not

Comment on lines +113 to +122
template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction>)
scalar_type correlated(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction)
{
BxDFClampMode _clamp = query.getClampMode();
assert(_clamp != BxDFClampMode::BCM_NONE);
return scalar_type(4.0) * interaction.getNdotV(_clamp) * _sample.getNdotL(_clamp) * correlated_wo_numerator<LS, Interaction>(query, _sample, interaction);
}

template<class LS, class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction> && ReadableIsotropicMicrofacetCache<MicrofacetCache>)
scalar_type G2_over_G1(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _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.

the cook_torrance_base::Eval calling D and correlated separately misses an optimization opportunity fo GGX to not put a 4 NdotV NdotL factor in the lightProjectedMeasure of the return value

We need to figure out how to leverage it on

Comment on lines 124 to 125
BxDFClampMode _clamp = query.getClampMode();
assert(_clamp != BxDFClampMode::BCM_NONE);
Copy link
Member Author

Choose a reason for hiding this comment

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

its weird for the query to tell us about clamping dynamically, I'd possibly prefer constexpr

Comment on lines 177 to 178
return w2 * w2 * atab * numbers::inv_pi<scalar_type>;
}
Copy link
Member Author

@devshgraphicsprogramming devshgraphicsprogramming Sep 19, 2025

Choose a reason for hiding this comment

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

kill the burley method, I have some ideas to make GGX spin by employing a covariance matrix of sorts(don't implement yet, just throw the Desmos link in the comment)

https://www.desmos.com/3d/weq2ginq9o

Comment on lines +214 to 224
template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Anisotropic<Interaction>)
scalar_type correlated(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction)
{
BxDFClampMode _clamp = query.getClampMode();
assert(_clamp != BxDFClampMode::BCM_NONE);
return scalar_type(4.0) * interaction.getNdotV(_clamp) * _sample.getNdotL(_clamp) * correlated_wo_numerator<LS, Interaction>(query, _sample, interaction);
}

template<class LS, class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Anisotropic<Interaction> && AnisotropicMicrofacetCache<MicrofacetCache>)
scalar_type G2_over_G1(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _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.

same problem as burley, these are identical for isotropic and anisotropic once you have the query that provides you with the anisotropic terms.

Even correlated_Wo_numerator is identical

Comment on lines +278 to +281
//reprojection onto hemisphere
//TODO try it wothout the max(), not sure if -t1*t1-t2*t2>-1.0
vector3_type H = t1*T1 + t2*T2 + sqrt<scalar_type>(max<scalar_type>(0.0, 1.0-t1*t1-t2*t2))*V;
//unstretch
Copy link
Member Author

Choose a reason for hiding this comment

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

find out, you might have to be careful about catastropic cancellation making small values near 0 negative

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 it was in the original code, thats probably why it was there

Comment on lines 287 to 591
template<typename T, bool IsAnisotropic, MicrofacetTransformTypes reflect_refract NBL_STRUCT_CONSTRAINABLE>
struct GGX;

// partial spec for brdf
template<typename T>
NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar<T>)
struct GGX<T,false,MTT_REFLECT NBL_PARTIAL_REQ_BOT(concepts::FloatingPointScalar<T>) >
{
using scalar_type = T;
using base_type = impl::GGXCommon<T,false>;
using quant_type = SDualMeasureQuant<scalar_type>;
using vector2_type = vector<T, 2>;
using vector3_type = vector<T, 3>;

using dg1_query_type = typename base_type::dg1_query_type;
using g2g1_query_type = typename base_type::g2g1_query_type;
using quant_query_type = impl::SGGXQuantQuery<scalar_type>;

template<class MicrofacetCache NBL_FUNC_REQUIRES(ReadableIsotropicMicrofacetCache<MicrofacetCache>)
quant_query_type createQuantQuery(NBL_CONST_REF_ARG(MicrofacetCache) cache, scalar_type orientedEta)
{
quant_query_type dummy; // brdfs don't make use of this
return dummy;
}
template<class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(surface_interactions::Isotropic<Interaction> && ReadableIsotropicMicrofacetCache<MicrofacetCache>)
dg1_query_type createDG1Query(NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
dg1_query_type dg1_query;
dg1_query.ndf = __base.template D<MicrofacetCache>(cache);
scalar_type clampedNdotV = interaction.getNdotV(BxDFClampMode::BCM_MAX);
dg1_query.G1 = scalar_type(2.0) * clampedNdotV * __base.G1_wo_numerator(clampedNdotV, interaction.getNdotV2());
return dg1_query;
}
template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction>)
g2g1_query_type createG2G1Query(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction)
{
g2g1_query_type g2_query;
g2_query.devsh_l = __base.devsh_part(_sample.getNdotL2());
g2_query.devsh_v = __base.devsh_part(interaction.getNdotV2());
g2_query._clamp = BxDFClampMode::BCM_MAX;
return g2_query;
}

vector<T, 3> generateH(const vector3_type localV, const vector2_type u)
{
return impl::GGXGenerateH<scalar_type>::__call(__base.A, localV, u);
}

template<class LS, class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction> && ReadableIsotropicMicrofacetCache<MicrofacetCache>)
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 = __base.template D<MicrofacetCache>(cache);
return createDualMeasureQuantity<T>(d, interaction.getNdotV(BxDFClampMode::BCM_MAX), _sample.getNdotL(BxDFClampMode::BCM_MAX));
}

template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction>)
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.DG1(query);
return createDualMeasureQuantity<T>(dg1, interaction.getNdotV(BxDFClampMode::BCM_MAX), _sample.getNdotL(BxDFClampMode::BCM_MAX));
}

template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction>)
quant_type correlated(NBL_CONST_REF_ARG(g2g1_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 g = __base.template correlated<LS, Interaction>(query, _sample, interaction);
return createDualMeasureQuantity<T>(g, interaction.getNdotV(BxDFClampMode::BCM_MAX), _sample.getNdotL(BxDFClampMode::BCM_MAX));
}

template<class LS, class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction> && ReadableIsotropicMicrofacetCache<MicrofacetCache>)
scalar_type G2_over_G1(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
return __base.template G2_over_G1<LS, Interaction, MicrofacetCache>(query, _sample, interaction, cache);
}

base_type __base;
};

template<typename T>
NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar<T>)
struct GGX<T,true,MTT_REFLECT NBL_PARTIAL_REQ_BOT(concepts::FloatingPointScalar<T>) >
{
using scalar_type = T;
using base_type = impl::GGXCommon<T,true>;
using quant_type = SDualMeasureQuant<scalar_type>;
using vector2_type = vector<T, 2>;
using vector3_type = vector<T, 3>;

using dg1_query_type = typename base_type::dg1_query_type;
using g2g1_query_type = typename base_type::g2g1_query_type;
using quant_query_type = impl::SGGXQuantQuery<scalar_type>;

template<class MicrofacetCache NBL_FUNC_REQUIRES(AnisotropicMicrofacetCache<MicrofacetCache>)
quant_query_type createQuantQuery(NBL_CONST_REF_ARG(MicrofacetCache) cache, scalar_type orientedEta)
{
quant_query_type dummy; // brdfs don't make use of this
return dummy;
}
template<class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(surface_interactions::Anisotropic<Interaction> && AnisotropicMicrofacetCache<MicrofacetCache>)
dg1_query_type createDG1Query(NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
dg1_query_type dg1_query;
dg1_query.ndf = __base.template D<MicrofacetCache>(cache);
scalar_type clampedNdotV = interaction.getNdotV(BxDFClampMode::BCM_MAX);
dg1_query.G1 = scalar_type(2.0) * clampedNdotV * __base.G1_wo_numerator(clampedNdotV, interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2());
return dg1_query;
}
template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Anisotropic<Interaction>)
g2g1_query_type createG2G1Query(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction)
{
g2g1_query_type g2_query;
g2_query.devsh_l = __base.devsh_part(_sample.getTdotL2(), _sample.getBdotL2(), _sample.getNdotL2());
g2_query.devsh_v = __base.devsh_part(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2());
g2_query._clamp = BxDFClampMode::BCM_MAX;
return g2_query;
}

vector<T, 3> generateH(const vector3_type localV, const vector2_type u)
{
return impl::GGXGenerateH<scalar_type>::__call(__base.A, localV, u);
}

template<class LS, class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Anisotropic<Interaction> && AnisotropicMicrofacetCache<MicrofacetCache>)
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 = __base.template D<MicrofacetCache>(cache);
return createDualMeasureQuantity<T>(d, interaction.getNdotV(BxDFClampMode::BCM_MAX), _sample.getNdotL(BxDFClampMode::BCM_MAX));
}

template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Anisotropic<Interaction>)
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.DG1(query);
return createDualMeasureQuantity<T>(dg1, interaction.getNdotV(BxDFClampMode::BCM_MAX), _sample.getNdotL(BxDFClampMode::BCM_MAX));
}

template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Anisotropic<Interaction>)
quant_type correlated(NBL_CONST_REF_ARG(g2g1_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 g = __base.template correlated<LS, Interaction>(query, _sample, interaction);
return createDualMeasureQuantity<T>(g, interaction.getNdotV(BxDFClampMode::BCM_MAX), _sample.getNdotL(BxDFClampMode::BCM_MAX));
}

template<class LS, class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Anisotropic<Interaction> && AnisotropicMicrofacetCache<MicrofacetCache>)
scalar_type G2_over_G1(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
return __base.template G2_over_G1<LS, Interaction, MicrofacetCache>(query, _sample, interaction, cache);
}

base_type __base;
};

// partial for bsdf
template<typename T, MicrofacetTransformTypes reflect_refract>
NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar<T>)
struct GGX<T,false,reflect_refract NBL_PARTIAL_REQ_BOT(concepts::FloatingPointScalar<T>) >
{
using scalar_type = T;
using base_type = impl::GGXCommon<T,false>;
using quant_type = SDualMeasureQuant<scalar_type>;
using vector2_type = vector<T, 2>;
using vector3_type = vector<T, 3>;

using dg1_query_type = typename base_type::dg1_query_type;
using g2g1_query_type = typename base_type::g2g1_query_type;
using quant_query_type = impl::SGGXQuantQuery<scalar_type>;

template<class MicrofacetCache NBL_FUNC_REQUIRES(ReadableIsotropicMicrofacetCache<MicrofacetCache>)
quant_query_type createQuantQuery(NBL_CONST_REF_ARG(MicrofacetCache) cache, scalar_type orientedEta)
{
quant_query_type quant_query;
quant_query.VdotHLdotH = cache.getVdotHLdotH();
quant_query.VdotH_etaLdotH = cache.getVdotH() + orientedEta * cache.getLdotH();
return quant_query;
}
template<class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(surface_interactions::Isotropic<Interaction> && ReadableIsotropicMicrofacetCache<MicrofacetCache>)
dg1_query_type createDG1Query(NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
dg1_query_type dg1_query;
dg1_query.ndf = __base.template D<MicrofacetCache>(cache);
scalar_type clampedNdotV = interaction.getNdotV(BxDFClampMode::BCM_ABS);
dg1_query.G1 = scalar_type(2.0) * clampedNdotV * __base.G1_wo_numerator(clampedNdotV, interaction.getNdotV2());
return dg1_query;
}
template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction>)
g2g1_query_type createG2G1Query(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction)
{
g2g1_query_type g2_query;
g2_query.devsh_l = __base.devsh_part(_sample.getNdotL2());
g2_query.devsh_v = __base.devsh_part(interaction.getNdotV2());
g2_query._clamp = BxDFClampMode::BCM_ABS;
return g2_query;
}

vector<T, 3> generateH(const vector3_type localV, const vector2_type u)
{
return impl::GGXGenerateH<scalar_type>::__call(__base.A, localV, u);
}

template<class LS, class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction> && ReadableIsotropicMicrofacetCache<MicrofacetCache>)
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 = __base.template D<MicrofacetCache>(cache);
return createDualMeasureQuantity<T, reflect_refract>(d, interaction.getNdotV(BxDFClampMode::BCM_ABS), _sample.getNdotL(BxDFClampMode::BCM_ABS), quant_query.getVdotHLdotH(), quant_query.getVdotH_etaLdotH());
}

template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction>)
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.DG1(query);
return createDualMeasureQuantity<T, reflect_refract>(dg1, interaction.getNdotV(BxDFClampMode::BCM_ABS), _sample.getNdotL(BxDFClampMode::BCM_ABS), quant_query.getVdotHLdotH(), quant_query.getVdotH_etaLdotH());
}

template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction>)
quant_type correlated(NBL_CONST_REF_ARG(g2g1_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 g = __base.template correlated<LS, Interaction>(query, _sample, interaction);
return createDualMeasureQuantity<T, reflect_refract>(g, interaction.getNdotV(BxDFClampMode::BCM_ABS), _sample.getNdotL(BxDFClampMode::BCM_ABS), quant_query.getVdotHLdotH(), quant_query.getVdotH_etaLdotH());
}

template<class LS, class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction> && ReadableIsotropicMicrofacetCache<MicrofacetCache>)
scalar_type G2_over_G1(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
return __base.template G2_over_G1<LS, Interaction, MicrofacetCache>(query, _sample, interaction, cache);
}

base_type __base;
};

template<typename T, MicrofacetTransformTypes reflect_refract>
NBL_PARTIAL_REQ_TOP(concepts::FloatingPointScalar<T>)
struct GGX<T,true,reflect_refract NBL_PARTIAL_REQ_BOT(concepts::FloatingPointScalar<T>) >
{
using scalar_type = T;
using base_type = impl::GGXCommon<T,true>;
using quant_type = SDualMeasureQuant<scalar_type>;
using vector2_type = vector<T, 2>;
using vector3_type = vector<T, 3>;

using dg1_query_type = typename base_type::dg1_query_type;
using g2g1_query_type = typename base_type::g2g1_query_type;
using quant_query_type = impl::SGGXQuantQuery<scalar_type>;

template<class MicrofacetCache NBL_FUNC_REQUIRES(AnisotropicMicrofacetCache<MicrofacetCache>)
quant_query_type createQuantQuery(NBL_CONST_REF_ARG(MicrofacetCache) cache, scalar_type orientedEta)
{
quant_query_type quant_query;
quant_query.VdotHLdotH = cache.getVdotHLdotH();
quant_query.VdotH_etaLdotH = cache.getVdotH() + orientedEta * cache.getLdotH();
return quant_query;
}
template<class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(surface_interactions::Anisotropic<Interaction> && AnisotropicMicrofacetCache<MicrofacetCache>)
dg1_query_type createDG1Query(NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
dg1_query_type dg1_query;
dg1_query.ndf = __base.template D<MicrofacetCache>(cache);
scalar_type clampedNdotV = interaction.getNdotV(BxDFClampMode::BCM_ABS);
dg1_query.G1 = scalar_type(2.0) * clampedNdotV * __base.G1_wo_numerator(clampedNdotV, interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2());
return dg1_query;
}
template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Anisotropic<Interaction>)
g2g1_query_type createG2G1Query(NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction)
{
g2g1_query_type g2_query;
g2_query.devsh_l = __base.devsh_part(_sample.getTdotL2(), _sample.getBdotL2(), _sample.getNdotL2());
g2_query.devsh_v = __base.devsh_part(interaction.getTdotV2(), interaction.getBdotV2(), interaction.getNdotV2());
g2_query._clamp = BxDFClampMode::BCM_ABS;
return g2_query;
}

vector<T, 3> generateH(const vector3_type localV, const vector2_type u)
{
return impl::GGXGenerateH<scalar_type>::__call(__base.A, localV, u);
}

template<class LS, class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Anisotropic<Interaction> && AnisotropicMicrofacetCache<MicrofacetCache>)
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 = __base.template D<MicrofacetCache>(cache);
return createDualMeasureQuantity<T, reflect_refract>(d, interaction.getNdotV(BxDFClampMode::BCM_ABS), _sample.getNdotL(BxDFClampMode::BCM_ABS), quant_query.getVdotHLdotH(), quant_query.getVdotH_etaLdotH());
}

template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Anisotropic<Interaction>)
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.DG1(query);
return createDualMeasureQuantity<T, reflect_refract>(dg1, interaction.getNdotV(BxDFClampMode::BCM_ABS), _sample.getNdotL(BxDFClampMode::BCM_ABS), quant_query.getVdotHLdotH(), quant_query.getVdotH_etaLdotH());
}

template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Anisotropic<Interaction>)
quant_type correlated(NBL_CONST_REF_ARG(g2g1_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 g = __base.template correlated<LS, Interaction>(query, _sample, interaction);
return createDualMeasureQuantity<T, reflect_refract>(g, interaction.getNdotV(BxDFClampMode::BCM_ABS), _sample.getNdotL(BxDFClampMode::BCM_ABS), quant_query.getVdotHLdotH(), quant_query.getVdotH_etaLdotH());
}

template<class LS, class Interaction, class MicrofacetCache NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Anisotropic<Interaction> && AnisotropicMicrofacetCache<MicrofacetCache>)
scalar_type G2_over_G1(NBL_CONST_REF_ARG(g2g1_query_type) query, NBL_CONST_REF_ARG(LS) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
return __base.template G2_over_G1<LS, Interaction, MicrofacetCache>(query, _sample, interaction, cache);
}

base_type __base;
};

Copy link
Member Author

Choose a reason for hiding this comment

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

same as burley, I think all 4 could be rolled into one class, cause IsBSDF is just a matter of a few enable_if and NBL_IF_CONSTEXPR and you can detect anisotropy from the NDF

Comment on lines +339 to +346
return createDualMeasureQuantity<T>(d, interaction.getNdotV(BxDFClampMode::BCM_MAX), _sample.getNdotL(BxDFClampMode::BCM_MAX));
}

template<class LS, class Interaction NBL_FUNC_REQUIRES(LightSample<LS> && surface_interactions::Isotropic<Interaction>)
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.DG1(query);
return createDualMeasureQuantity<T>(dg1, interaction.getNdotV(BxDFClampMode::BCM_MAX), _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.

we should somehow use our knowledge of factors in GGX that cancel out to make the dual measure quantity faster.

Comment on lines 39 to 48
static this_t create(scalar_type A, NBL_CONST_REF_ARG(spectral_type) eta, NBL_CONST_REF_ARG(spectral_type) etak)
{
this_t retval;
retval.__base.ndf.__base.A = vector2_type(A, A);
retval.__base.ndf.__base.a2 = A*A;
retval.__base.fresnel.ior = ior0;
retval.__base.fresnel.iork = ior1;
retval.__base.fresnel.iork2 = ior1*ior1;
retval.__base.fresnel.eta = eta;
retval.__base.fresnel.etak2 = etak * etak;
retval.__base.fresnel.etaLen2 = eta * eta + retval.__base.fresnel.etak2;
return retval;
}
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 are you assuming the fresnel used ?

Comment on lines -45 to -46
spectral_type ior0;
spectral_type ior1;
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 need to pass the fresnel already set up

return create(params.A, params.ior0, params.ior1);
return create(params.A, params.eta, params.etak);
}

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 do all these still exist and aren't just typedefs for Cook Torrance ?

Comment on lines 36 to 43
sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u)
{
return __base.generate(anisotropic_interaction_type::create(interaction), u);
return __base.template generate<vector2_type>(anisotropic_interaction_type::create(interaction), u);
}
sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u)
{
return __base.generate(interaction, u);
return __base.template generate<vector2_type>(interaction, u);
}
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 not just typedef instead of writing pointless passthrough class ?

{
quant_type G2 = ndf.template correlated<sample_type, isotropic_interaction_type>(gq, qq, _sample, interaction);
DG *= G2.microfacetMeasure;
DG *= ndf.template correlated<sample_type, isotropic_interaction_type>(gq, _sample, interaction);
Copy link
Member Author

Choose a reason for hiding this comment

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

lower the gq creation as well

{
quant_type G2 = ndf.template correlated<sample_type, anisotropic_interaction_type>(gq, qq, _sample, interaction);
DG *= G2.microfacetMeasure;
DG *= ndf.template correlated<sample_type, anisotropic_interaction_type>(gq, _sample, interaction);
Copy link
Member Author

Choose a reason for hiding this comment

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

lower the gq creation as well

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