-
Notifications
You must be signed in to change notification settings - Fork 65
Bxdf fixes cook torrance #930
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?
Conversation
} | ||
scalar_type getNdotL2() NBL_CONST_MEMBER_FUNC { return NdotL2; } | ||
|
||
bool isValid() NBL_CONST_MEMBER_FUNC { return !hlsl::all<vector<bool, 3> >(hlsl::glsl::equal(L.getDirection(), hlsl::promote<vector3_type>(0.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.
why not use glsl::notEqual
and not-unary-not it? I mean SPIR-V OpFOrdNotEqual
exists so that intrinsic should probably get made
template<typename T> | ||
vector<T, 2> cartesianToPolar(vector<T, 3> coords) | ||
{ | ||
return vector<T, 2>(hlsl::acos(clamp<float>(coords.z, -1, 1)), hlsl::atan2(coords.y, coords.x)); |
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.
should be asin
for the coord.z
cause you get it from a sin in polarToCartesian
.
Also there should be no need to clamp, assert that input is already clamped
btw since we're in HLSL land, we should make a struct polar<T> {T theta; T phi};
with specializations for _static_cast_helper
and a header for it for semantic clarity and not being suspect to accidental casts betweeen cartesian and polar.
P.S. what uses it so far ?
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.
Used to bucket generated sample angles in the bxdf test
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.
in GLSL they were in the sampling
folder
vec2 nbl_glsl_sampling_envmap_uvCoordFromDirection(vec3 v) |
#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 |
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.
are you sure having this specialization globally is a good idea ?
What do you use it for ?
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 used in the bucket bxdf test, for hash map buckets
T eta; | ||
T etak; | ||
T etak2; | ||
T ior; | ||
T iork; | ||
T iork2; |
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.
it seems the thing you really want to precompute would be
etaLen2
and store eta
so 2 not 3 variables
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 only changed the naming, why ior and not eta ?
was it because of this ? #916 (comment)
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>) > | ||
{ |
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.
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)
using dg1_query_type = SBeckmannDG1Query<scalar_type>; | ||
using g2g1_query_type = SBeckmannG2overG1Query<scalar_type>; | ||
|
||
template<class MicrofacetCache NBL_FUNC_REQUIRES(ReadableIsotropicMicrofacetCache<MicrofacetCache>) | ||
scalar_type D(NBL_CONST_REF_ARG(MicrofacetCache) cache) | ||
{ | ||
scalar_type nom = exp2<scalar_type>((cache.getNdotH2() - scalar_type(1.0)) / (log<scalar_type>(2.0) * a2 * cache.getNdotH2())); | ||
scalar_type denom = a2 * cache.getNdotH2() * cache.getNdotH2(); | ||
scalar_type NdotH2 = cache.getNdotH2(); | ||
scalar_type nom = exp2<scalar_type>((NdotH2 - scalar_type(1.0)) / (log<scalar_type>(2.0) * a2 * NdotH2)); | ||
scalar_type denom = a2 * NdotH2 * NdotH2; | ||
return numbers::inv_pi<scalar_type> * nom / denom; | ||
} | ||
|
||
// brdf | ||
template<class Query NBL_FUNC_REQUIRES(beckmann_concepts::DG1Query<Query>) | ||
scalar_type DG1(NBL_CONST_REF_ARG(Query) query) | ||
scalar_type DG1(NBL_CONST_REF_ARG(dg1_query_type) query) |
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.
there should be concepts for the queries, I'm not against some defined structs to use, but if you don't template the queries I love the polymorphism which is why we got rid of the overloads and complex function set ups
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.
alos it seems that your DG1
doesn't depend on member variables so can be static #916 (comment)
return 2.0 / a2 - 2.0; | ||
} | ||
|
||
scalar_type C2(scalar_type NdotX2) |
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.
clampedNdotX2
also control other places #916 (comment)
You could slap an assert(clampedNdotX2>=scalar_type(0));
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::SBeckmannQuantQuery<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; | ||
} |
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.
does anyone make use of 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.
or you mean that bsdfs do ?
vector<T, 3> generateH(const vector3_type localV, const vector2_type u) | ||
{ | ||
return impl::BeckmannGenerateH<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; |
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.
it kinda looks like this part of the Isotropic and Anisotropic code doesn't need to be specialized, you just need to make a concept alias to use in your requires
clauses
template<class Interaction>
NBL_CONSTEXPR_STATIC_INLINE bool RequiredInteraction = IsAnisotropic ? ...
Example
https://godbolt.devsh.eu/z/zKjKa1
its only the query creation which needs specialization
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); | ||
dg1_query.lambda_V = __base.LambdaC2(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.lambda_L = __base.LambdaC2(_sample.getNdotL2()); | ||
g2_query.lambda_V = __base.LambdaC2(interaction.getNdotV2()); | ||
return g2_query; | ||
} | ||
|
||
vector<T, 3> generateH(const vector3_type localV, const vector2_type u) | ||
{ | ||
return impl::BeckmannGenerateH<scalar_type>::__call(__base.A, localV, u); | ||
} |
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 part seems identical to anisotropic BRDF
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); | ||
} |
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.
tbf you could skip the specialization and use NBL_IF_CONSTEXPR
to choose which createDualMeasureQuantity
is needed
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()); | ||
} |
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.
yeah I think that correlated
should be measureless, because we'd expec to get DG2
when you multiply D
with G2
(correlated) by yourself, and if the code does Dual Measure transform on bothm, then you end up with the differential reflection/refraction factors squared.
template<typename T> | ||
struct SGGXDG1Query | ||
{ | ||
using scalar_type = T; | ||
|
||
scalar_type getNdf() NBL_CONST_MEMBER_FUNC { return ndf; } | ||
scalar_type getG1() NBL_CONST_MEMBER_FUNC { return G1; } | ||
|
||
scalar_type ndf; | ||
scalar_type G1; | ||
}; |
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.
could be defined in ndf
header, its common to all
BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; } | ||
|
||
scalar_type devsh_v; | ||
scalar_type devsh_l; | ||
BxDFClampMode _clamp; |
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 clamp mode is not something that can be just set willy nilly if you're specializing your NDFs based on things like MicrofacetTransformTypes
Either deduce it somewhere else, or make this an NBL_CONSTEXPR_STATIC_INLINE
template<typename T> | ||
struct SGGXQuantQuery | ||
{ | ||
using scalar_type = T; | ||
|
||
scalar_type getVdotHLdotH() NBL_CONST_MEMBER_FUNC { return VdotHLdotH; } | ||
scalar_type getVdotH_etaLdotH() NBL_CONST_MEMBER_FUNC { return VdotH_etaLdotH; } | ||
|
||
scalar_type VdotHLdotH; | ||
scalar_type VdotH_etaLdotH; | ||
}; |
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 also looks quite common to both NDFs
|
||
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(); | ||
} |
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 seems silly to keep around
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.
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); |
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 are the two methods needed?
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'd think you only need G1_wo_numerator_devsh_part(scalar_type absNdotX, scalar_type devsh_part)
scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; } | ||
scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; } |
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.
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? |
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.
yes!
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; | ||
} |
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 isotropic methods need enable_if
because you need to check if the NDF used is isotropic or not
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) |
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 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
BxDFClampMode _clamp = query.getClampMode(); | ||
assert(_clamp != BxDFClampMode::BCM_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.
its weird for the query to tell us about clamping dynamically, I'd possibly prefer constexpr
return w2 * w2 * atab * numbers::inv_pi<scalar_type>; | ||
} |
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.
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)
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) | ||
{ |
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 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
//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 |
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.
find out, you might have to be careful about catastropic cancellation making small values near 0 negative
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.
if it was in the original code, thats probably why it was there
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; | ||
}; | ||
|
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 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
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)); |
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 should somehow use our knowledge of factors in GGX that cancel out to make the dual measure quantity faster.
Description
Continues #899 , #916 and #919
Testing
TODO list: