Skip to content

Conversation

devshgraphicsprogramming
Copy link
Member

Description

Continuing #811 due to GH UI messing up diffs again

Testing

TODO list:

Comment on lines 48 to 133
struct SCreationParams
{
scalar_type A;
spectral_type ior0;
spectral_type ior1;
};
using creation_type = SCreationParams;

struct SGGXQuery
{
using scalar_type = scalar_type;

scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; }
scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; }

scalar_type devsh_v;
scalar_type devsh_l;
};
using query_type = SGGXQuery;

static this_t create(scalar_type A, NBL_CONST_REF_ARG(spectral_type) ior0, NBL_CONST_REF_ARG(spectral_type) ior1)
{
this_t retval;
retval.__base.ndf.A = vector2_type(A, A);
retval.__base.ndf.a2 = A*A;
retval.__base.ndf.one_minus_a2 = scalar_type(1.0) - A*A;
retval.__base.fresnel.eta = ior0;
retval.__base.fresnel.etak = ior1;
retval.__base.fresnel.etak2 = ior1*ior1;
return retval;
}
static this_t create(NBL_CONST_REF_ARG(creation_type) params)
{
return create(params.A, params.ior0, params.ior1);
}

query_type createQuery(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction)
{
query_type query;
ndf_type ggx_ndf = __base.ndf;
query.devsh_v = ggx_ndf.devsh_part(interaction.getNdotV2());
query.devsh_l = ggx_ndf.devsh_part(_sample.getNdotL2());
return query;
}

spectral_type eval(NBL_CONST_REF_ARG(query_type) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache)
{
if (_sample.getNdotL() > numeric_limits<scalar_type>::min && interaction.getNdotV() > numeric_limits<scalar_type>::min)
{
struct SGGXG2XQuery
{
using scalar_type = scalar_type;

scalar_type getDevshV() NBL_CONST_MEMBER_FUNC { return devsh_v; }
scalar_type getDevshL() NBL_CONST_MEMBER_FUNC { return devsh_l; }
BxDFClampMode getClampMode() NBL_CONST_MEMBER_FUNC { return _clamp; }

scalar_type devsh_v;
scalar_type devsh_l;
BxDFClampMode _clamp;
};

SGGXG2XQuery g2_query;
g2_query.devsh_v = query.getDevshV();
g2_query.devsh_l = query.getDevshL();
g2_query._clamp = _clamp;

measure_transform_type dualMeasure = __base.template __DG<SGGXG2XQuery>(g2_query, _sample, interaction, cache);
dualMeasure.maxNdotL = _sample.getNdotL(_clamp);
scalar_type DG = dualMeasure.getProjectedLightMeasure();
fresnel_type f = __base.fresnel;
f.clampedCosTheta = cache.getVdotH();
return f() * DG;
}
else
return hlsl::promote<spectral_type>(0.0);
}

sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(isocache_type) cache)
{
SGGXAnisotropic<Config> ggx_aniso = SGGXAnisotropic<Config>::create(__base.ndf.A.x, __base.ndf.A.y, __base.fresnel.eta, __base.fresnel.etak);
anisocache_type anisocache;
sample_type s = ggx_aniso.generate(anisotropic_interaction_type::create(interaction), u, anisocache);
cache = anisocache.iso_cache;
return s;
}
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 comments as for beckmann

Comment on lines +21 to +89
namespace impl
{
struct sincos_accumulator
{
using this_t = sincos_accumulator;

static this_t create()
{
this_t retval;
retval.tmp0 = 0;
retval.tmp1 = 0;
retval.tmp2 = 0;
retval.tmp3 = 0;
retval.tmp4 = 0;
retval.tmp5 = 0;
return retval;
}

static this_t create(float cosA, float cosB, float cosC, float sinA, float sinB, float sinC)
{
this_t retval;
retval.tmp0 = cosA;
retval.tmp1 = cosB;
retval.tmp2 = cosC;
retval.tmp3 = sinA;
retval.tmp4 = sinB;
retval.tmp5 = sinC;
return retval;
}

float getArccosSumofABC_minus_PI()
{
const bool AltminusB = tmp0 < (-tmp1);
const float cosSumAB = tmp0 * tmp1 - tmp3 * tmp4;
const bool ABltminusC = cosSumAB < (-tmp2);
const bool ABltC = cosSumAB < tmp2;
// apply triple angle formula
const float absArccosSumABC = acos<float>(clamp<float>(cosSumAB * tmp2 - (tmp0 * tmp4 + tmp3 * tmp1) * tmp5, -1.f, 1.f));
return ((AltminusB ? ABltC : ABltminusC) ? (-absArccosSumABC) : absArccosSumABC) + ((AltminusB || ABltminusC) ? numbers::pi<float> : (-numbers::pi<float>));
}

static void combineCosForSumOfAcos(float cosA, float cosB, float biasA, float biasB, NBL_REF_ARG(float) out0, NBL_REF_ARG(float) out1)
{
const float bias = biasA + biasB;
const float a = cosA;
const float b = cosB;
const bool reverse = abs<float>(min<float>(a, b)) > max<float>(a, b);
const float c = a * b - sqrt<float>((1.0f - a * a) * (1.0f - b * b));

if (reverse)
{
out0 = -c;
out1 = bias + numbers::pi<float>;
}
else
{
out0 = c;
out1 = bias;
}
}

float tmp0;
float tmp1;
float tmp2;
float tmp3;
float tmp4;
float tmp5;
};
}
Copy link
Member Author

Choose a reason for hiding this comment

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

See #899 (comment)

Also no need for this to stay in impl

Comment on lines +365 to +367
ray_dir_info_type localL;
bxdf::Reflect<scalar_type> r = bxdf::Reflect<scalar_type>::create(localV, H);
localL.direction = r(cache.iso_cache.getVdotH());
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 use interaction.getV().reflect(r) to build localL otherwise in the future you'll loose ray-differentials or covariance matrices

Comment on lines +37 to +52
struct SCreationParams
{
scalar_type eta;
};
using creation_type = SCreationParams;

static this_t create(scalar_type eta)
{
this_t retval;
retval.eta = eta;
return retval;
}
static this_t create(NBL_CONST_REF_ARG(creation_type) params)
{
return create(params.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.

make the eta a full fresnel::Dielectric member, get rid of create methods

Comment on lines +118 to +123
struct SCreationParams
{
spectral_type eta2;
spectral_type luminosityContributionHint;
};
using creation_type = SCreationParams;
Copy link
Member Author

Choose a reason for hiding this comment

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

remove creation params and its associated create

return quotient_and_pdf(_sample, interaction.isotropic);
}

spectral_type eta2;
Copy link
Member Author

Choose a reason for hiding this comment

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

hold fresnel::Dielectric as a member instead

Comment on lines +132 to +137
static this_t create(NBL_CONST_REF_ARG(spectral_type) eta2)
{
static_assert(vector_traits<spectral_type>::Dimension == 3);
const spectral_type rec709 = spectral_type(0.2126, 0.7152, 0.0722);
return create(eta2, rec709);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

useful don't touch

Comment on lines +155 to +172
// `remainderMetadata` is a variable which the generator function returns byproducts of sample generation that would otherwise have to be redundantly calculated `quotient_and_pdf`
sample_type __generate_wo_clamps(NBL_CONST_REF_ARG(ray_dir_info_type) V, const vector3_type T, const vector3_type B, const vector3_type N, scalar_type NdotV, scalar_type absNdotV, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(spectral_type) eta2, NBL_CONST_REF_ARG(spectral_type) luminosityContributionHint, NBL_REF_ARG(spectral_type) remainderMetadata)
{
// we will only ever intersect from the outside
const spectral_type reflectance = fresnel::thinDielectricInfiniteScatter<spectral_type>(fresnel::Dielectric<spectral_type>::__call(eta2,absNdotV));

// we are only allowed one choice for the entire ray, so make the probability a weighted sum
const scalar_type reflectionProb = nbl::hlsl::dot<spectral_type>(reflectance, luminosityContributionHint);

scalar_type rcpChoiceProb;
const bool transmitted = math::partitionRandVariable(reflectionProb, u.z, rcpChoiceProb);
remainderMetadata = hlsl::mix(reflectance, hlsl::promote<spectral_type>(1.0) - reflectance, transmitted) * rcpChoiceProb;

Reflect<scalar_type> r = Reflect<scalar_type>::create(V.getDirection(), N);
ray_dir_info_type L;
L.direction = hlsl::mix(V.reflect(r).getDirection(), V.transmit().getDirection(), transmitted);
return sample_type::create(L, T, B, N);
}
Copy link
Member Author

@devshgraphicsprogramming devshgraphicsprogramming Aug 29, 2025

Choose a reason for hiding this comment

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

change the __generate_wo_clamps into generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(spectral_type) remainderMetadata)

Comment on lines +174 to +181
sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u)
{
vector3_type dummy;
return __generate_wo_clamps(interaction.getV(), interaction.getT(), interaction.getB(), interaction.getN(), interaction.getNdotV(), interaction.getNdotV(_clamp), u, eta2, luminosityContributionHint, dummy);
}
sample_type generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_REF_ARG(vector3_type) u)
{
return generate(anisotropic_interaction_type::create(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.

u shall be taken by copy not reference #899 (comment)

Comment on lines +168 to +171
Reflect<scalar_type> r = Reflect<scalar_type>::create(V.getDirection(), N);
ray_dir_info_type L;
L.direction = hlsl::mix(V.reflect(r).getDirection(), V.transmit().getDirection(), transmitted);
return sample_type::create(L, T, B, N);
Copy link
Member Author

@devshgraphicsprogramming devshgraphicsprogramming Aug 29, 2025

Choose a reason for hiding this comment

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

you can't build a reflected L this way, you need to use V.reflect(r) or V.refract(r,rcpOrientedEta)

btw it seems that the RayDirInfo needs a reflectRefract method #899 (comment)

For ideas see #899 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

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

Comment on lines +57 to +64
static vector_t3 generate(vector_t3 _sample)
{
vector_t3 retval = hemisphere_t::generate(_sample.xy);
const bool chooseLower = _sample.z > T(0.5);
retval.z = chooseLower ? (-retval.z) : retval.z;
if (chooseLower)
_sample.z -= T(0.5);
_sample.z *= T(2.0);
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 this to work the _sample needs to be taken as NBL_REF_ARG

((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.transmit()), ::nbl::hlsl::is_same_v, T))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.reflect(rfl)), ::nbl::hlsl::is_same_v, T))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.refract(rfr, rcpEta)), ::nbl::hlsl::is_same_v, T))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((rdirinfo.reflectRefract(rr, t, rcpEta)), ::nbl::hlsl::is_same_v, T))
Copy link
Member Author

Choose a reason for hiding this comment

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

oh we already have reflectRefract ! Then use it in all the BSDF generate

Comment on lines +218 to +222
((NBL_CONCEPT_REQ_TYPE)(T::ray_dir_info_type))
((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type))
((NBL_CONCEPT_REQ_TYPE)(T::scalar_type))
((NBL_CONCEPT_REQ_TYPE)(T::vector3_type))
((NBL_CONCEPT_REQ_TYPE)(T::matrix3x3_type))
Copy link
Member Author

Choose a reason for hiding this comment

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

maybe check that T matches Isotropic first, and then you can only check for things not in that concept ?

Copy link
Member Author

Choose a reason for hiding this comment

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

also can you add a check for decltype<T>().isotropic existing ?

Comment on lines +509 to +513
retval.VdotL = VdotL;
retval.VdotH = LplusV_rcpLen * VdotL + LplusV_rcpLen;
retval.LdotH = retval.VdotH;
retval.NdotH = (NdotL + NdotV) * LplusV_rcpLen;
retval.NdotH2 = retval.NdotH * retval.NdotH;
Copy link
Member Author

Choose a reason for hiding this comment

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

NdotH member needs to be called absNdotH and needs to be always be positive, see #899 (comment) for why

Problem is that NdotL+NdotV can be less than 0, and in that case all dot products with H need their signs flipped.

Copy link
Member Author

Choose a reason for hiding this comment

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

Comment on lines +536 to +549
H = computeMicrofacetNormal.normalized(transmitted);
retval.NdotH = hlsl::dot<vector3_type>(N, H);

// not coming from the medium (reflected) OR
// exiting at the macro scale AND ( (not L outside the cone of possible directions given IoR with constraint VdotH*LdotH<0.0) OR (microfacet not facing toward the macrosurface, i.e. non heightfield profile of microsurface) )
const bool valid = ComputeMicrofacetNormal<scalar_type>::isValidMicrofacet(transmitted, VdotL, retval.NdotH, computeMicrofacetNormal.orientedEta);
if (valid)
{
retval.VdotH = hlsl::dot<vector3_type>(computeMicrofacetNormal.V,H);
retval.LdotH = hlsl::dot<vector3_type>(computeMicrofacetNormal.L,H);
retval.NdotH2 = retval.NdotH * retval.NdotH;
}
else
retval.NdotH = bit_cast<scalar_type>(numeric_limits<scalar_type>::quiet_NaN);
Copy link
Member Author

Choose a reason for hiding this comment

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

id NdotH is negative, we need to flip all dot products with H and H` itself, for why see #899 (comment)

return create(interaction,_sample,orientedEtas,dummy);
}

bool isTransmission() NBL_CONST_MEMBER_FUNC { return getVdotHLdotH() < scalar_type(0.0); }
Copy link
Member Author

Choose a reason for hiding this comment

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

lots of documentation needed for this , see #899 (comment)

Comment on lines +687 to +690
retval.iso_cache.VdotH = nbl::hlsl::dot<vector3_type>(tangentSpaceV,tangentSpaceH);
retval.iso_cache.LdotH = retval.iso_cache.getVdotH();
retval.iso_cache.NdotH = tangentSpaceH.z;
retval.iso_cache.NdotH2 = retval.iso_cache.getNdotH() * retval.iso_cache.getNdotH();
Copy link
Member Author

Choose a reason for hiding this comment

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

assert that tangentSpaceH.z>=0.f

Comment on lines +719 to +720
retval.TdotH = (tangentSpaceV.x + tangentSpaceL.x) * LplusV_rcpLen;
retval.BdotH = (tangentSpaceV.y + tangentSpaceL.y) * LplusV_rcpLen;
Copy link
Member Author

Choose a reason for hiding this comment

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

care is needed, LplusV_rcpLen should be made negative if needed by the isocache_type::createForReflection to keep NdotH positive

Comment on lines +810 to +886
((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type))
((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type))
((NBL_CONCEPT_REQ_TYPE)(T::sample_type))
((NBL_CONCEPT_REQ_TYPE)(T::spectral_type))
((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type))
((NBL_CONCEPT_REQ_TYPE)(T::params_isotropic_t))
((NBL_CONCEPT_REQ_TYPE)(T::params_anisotropic_t))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(param_iso)), ::nbl::hlsl::is_same_v, typename T::scalar_type))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(param_aniso)), ::nbl::hlsl::is_same_v, typename T::scalar_type))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u)), ::nbl::hlsl::is_same_v, typename T::sample_type))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(param_iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(param_aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type))
((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type))
((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type))
((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Isotropic, typename T::isotropic_interaction_type))
((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(surface_interactions::Anisotropic, typename T::anisotropic_interaction_type))
);
#undef u
#undef param_aniso
#undef param_iso
#undef aniso
#undef iso
#undef _sample
#undef pdf
#undef spec
#undef bxdf
#include <nbl/builtin/hlsl/concepts/__end.hlsl>

#define NBL_CONCEPT_NAME MicrofacetBxDF
#define NBL_CONCEPT_TPLT_PRM_KINDS (typename)
#define NBL_CONCEPT_TPLT_PRM_NAMES (T)
#define NBL_CONCEPT_PARAM_0 (bxdf, T)
#define NBL_CONCEPT_PARAM_1 (spec, typename T::spectral_type)
#define NBL_CONCEPT_PARAM_2 (pdf, typename T::scalar_type)
#define NBL_CONCEPT_PARAM_3 (_sample, typename T::sample_type)
#define NBL_CONCEPT_PARAM_4 (iso, typename T::isotropic_interaction_type)
#define NBL_CONCEPT_PARAM_5 (aniso, typename T::anisotropic_interaction_type)
#define NBL_CONCEPT_PARAM_6 (isocache, typename T::isocache_type)
#define NBL_CONCEPT_PARAM_7 (anisocache, typename T::anisocache_type)
#define NBL_CONCEPT_PARAM_8 (param_iso, typename T::params_isotropic_t)
#define NBL_CONCEPT_PARAM_9 (param_aniso, typename T::params_anisotropic_t)
#define NBL_CONCEPT_PARAM_10 (u, vector<typename T::scalar_type, 3>)
NBL_CONCEPT_BEGIN(11)
#define bxdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_0
#define spec NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_1
#define pdf NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_2
#define _sample NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_3
#define iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_4
#define aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_5
#define isocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_6
#define anisocache NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_7
#define param_iso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_8
#define param_aniso NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_9
#define u NBL_CONCEPT_PARAM_T NBL_CONCEPT_PARAM_10
NBL_CONCEPT_END(
((NBL_CONCEPT_REQ_TYPE)(T::scalar_type))
((NBL_CONCEPT_REQ_TYPE)(T::isotropic_interaction_type))
((NBL_CONCEPT_REQ_TYPE)(T::anisotropic_interaction_type))
((NBL_CONCEPT_REQ_TYPE)(T::sample_type))
((NBL_CONCEPT_REQ_TYPE)(T::spectral_type))
((NBL_CONCEPT_REQ_TYPE)(T::quotient_pdf_type))
((NBL_CONCEPT_REQ_TYPE)(T::isocache_type))
((NBL_CONCEPT_REQ_TYPE)(T::anisocache_type))
((NBL_CONCEPT_REQ_TYPE)(T::params_isotropic_t))
((NBL_CONCEPT_REQ_TYPE)(T::params_anisotropic_t))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(param_iso)), ::nbl::hlsl::is_same_v, T::spectral_type))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.eval(param_aniso)), ::nbl::hlsl::is_same_v, T::spectral_type))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(iso,u,isocache)), ::nbl::hlsl::is_same_v, typename T::sample_type))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.generate(aniso,u,anisocache)), ::nbl::hlsl::is_same_v, typename T::sample_type))
//((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.template pdf<LS,I>(_sample,iso)), ::nbl::hlsl::is_scalar_v))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(param_iso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type))
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((bxdf.quotient_and_pdf(param_aniso)), ::nbl::hlsl::is_same_v, typename T::quotient_pdf_type))
((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(LightSample, typename T::sample_type))
((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(concepts::FloatingPointLikeVectorial, typename T::spectral_type))
((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type))
((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(AnisotropicMicrofacetCache, typename T::anisocache_type))
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 also make IsotropicBxDF concept, and only check aniso functions and types (at least the signatures) in the BxDF cocnept, and isotropic in the IsotropicBxDF (basically IsotropicBxDF has to have all signatures Anisotropic one would have == has to be compatible with anisotropic interaction and cache types - worst case it will just behave isotropically).

return hlsl::promote<spectral_type>(__base.fresnel(hlsl::abs(cache.getVdotH()))[0]) * DG;
}

sample_type __generate_wo_clamps(const vector3_type localV, const vector3_type H, NBL_CONST_REF_ARG(matrix3x3_type) m, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps<monochrome_type>) rcpEta, NBL_REF_ARG(anisocache_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 need to take whole ray-dir instead of vec3 localV (same comment for all cook torrance)

Comment on lines +305 to +313
const scalar_type VdotH = cache.iso_cache.getVdotH();
Refract<scalar_type> r = Refract<scalar_type>::create(localV, H);
cache.iso_cache.LdotH = hlsl::mix(VdotH, r.getNdotT(rcpEta.value2[0]), transmitted);
ray_dir_info_type localL;
bxdf::ReflectRefract<scalar_type> rr;
rr.refract = r;
localL = localL.reflectRefract(rr, transmitted, rcpEta.value[0]);

return sample_type::createFromTangentSpace(localL, m);
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 when this moves to Cook Torrance, you need to make sure that you somehow "fail" the generation if the following happens:

  1. you choose reflect but resulting localL.z has different sign to localV.z
  2. you choose refract but resulting local.z has same sign as localV.z

This is simply to avoid using samples which have invalid paths.

See #899 (comment)

I think the returned aniso cache can be queried for invalidity, or the sample somehow.

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 to pass tests, in another PR we'll rework the PDF stuf into MIS-weights on BxDFs and we'll be free to do Resampled Importance Sampling

sample_type __generate_wo_clamps(const vector3_type localV, const vector3_type H, NBL_CONST_REF_ARG(matrix3x3_type) m, NBL_REF_ARG(vector3_type) u, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps<monochrome_type>) rcpEta, NBL_REF_ARG(anisocache_type) cache)
{
const scalar_type localVdotH = nbl::hlsl::dot<vector3_type>(localV,H);
const scalar_type reflectance = __base.fresnel(hlsl::abs(cache.getVdotH()))[0];
Copy link
Member Author

Choose a reason for hiding this comment

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

need a luminosity contribution hint same as thindielectric BSDF has

Comment on lines +33 to +89
scalar_type __D(NBL_CONST_REF_ARG(isocache_type) cache)
{
return ndf.template D<isocache_type>(cache);
}
scalar_type __D(NBL_CONST_REF_ARG(anisocache_type) cache)
{
return ndf.template D<anisocache_type>(cache);
}

template<class Query>
MT __DG1(NBL_CONST_REF_ARG(Query) query)
{
MT measure_transform;
measure_transform.pdf = ndf.template DG1<Query>(query);
return measure_transform;
}
template<class Query>
MT __DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(isocache_type) cache)
{
MT measure_transform;
measure_transform.pdf = ndf.template DG1<Query>(query, cache);
measure_transform.transmitted = cache.isTransmission();
measure_transform.VdotH = cache.getVdotH();
measure_transform.LdotH = cache.getLdotH();
measure_transform.VdotHLdotH = cache.getVdotHLdotH();
return measure_transform;
}
template<class Query>
MT __DG1(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(anisocache_type) cache)
{
MT measure_transform;
measure_transform.pdf = ndf.template DG1<Query>(query, cache);
measure_transform.transmitted = cache.isTransmission();
measure_transform.VdotH = cache.getVdotH();
measure_transform.LdotH = cache.getLdotH();
measure_transform.VdotHLdotH = cache.getVdotHLdotH();
return measure_transform;
}

template<class Query>
MT __DG(NBL_CONST_REF_ARG(Query) query, NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache)
{
MT measure_transform;
measure_transform.pdf = ndf.template D<isocache_type>(cache);
NBL_IF_CONSTEXPR(MT::Type == ndf::MicrofacetTransformTypes::MTT_REFLECT_REFRACT)
measure_transform.transmitted = cache.isTransmission();
NBL_IF_CONSTEXPR(MT::Type == ndf::MicrofacetTransformTypes::MTT_REFRACT)
{
measure_transform.VdotH = cache.getVdotH();
measure_transform.LdotH = cache.getLdotH();
measure_transform.VdotHLdotH = cache.getVdotHLdotH();
}
if (any<vector<bool, 2> >(ndf.A > hlsl::promote<vector2_type>(numeric_limits<scalar_type>::min)))
{
measure_transform.pdf *= ndf.template correlated<Query, sample_type, isotropic_interaction_type>(query, _sample, interaction);
}
return measure_transform;
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 I'd really love to slap the BRDF, BTDF and BSDF code all into the same class.

and even moreso, avoid different partial specs for Anisotropic and Isotropic #899 (comment)

@devshgraphicsprogramming devshgraphicsprogramming merged commit b39c937 into master Aug 29, 2025
13 of 14 checks passed
@devshgraphicsprogramming devshgraphicsprogramming deleted the hlsl_bxdfs branch August 29, 2025 13:04
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