Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
5218545
changes to dual measure quant, old stuff commented out for now
keptsecret Sep 4, 2025
f7525af
create query structs in beckmann ndf, removed query concepts, funcs r…
keptsecret Sep 5, 2025
c50db68
did the same for ggx ndf, some fixes to beckmann
keptsecret Sep 5, 2025
c49bedb
Merge branch 'master' into bxdf_fixes_cook_torrance
keptsecret Sep 8, 2025
319c954
fixes to ndf concept
keptsecret Sep 8, 2025
4eeacf1
moved eval, quotient/pdf into cook torrance base
keptsecret Sep 9, 2025
e7bc784
moved generate H into ndfs, generate impl in cook torrance base
keptsecret Sep 9, 2025
72226bb
use new cook torrance base in microfacet bxdfs
keptsecret Sep 9, 2025
f34b348
Merge branch 'master' into bxdf_fixes_cook_torrance
keptsecret Sep 10, 2025
ad13044
numerous typo bug fixes
keptsecret Sep 10, 2025
d8d2116
fixes to ggx ndf
keptsecret Sep 10, 2025
238b08e
fixed conductor fresnel naming ior as eta
keptsecret Sep 10, 2025
8bc707f
minor fixes to beckmann ndf, removed obsolete comments
keptsecret Sep 10, 2025
9fb28cf
added luminosity contribution hint to cook torrance bsdfs
keptsecret Sep 11, 2025
4cb8a0a
added aniso overloads to isotropic bxdf methods
keptsecret Sep 11, 2025
392dc31
checks for invalid generate sample
keptsecret Sep 11, 2025
02de86e
removed obsolete commented out stuff
keptsecret Sep 12, 2025
4a7f532
added cartesian-polar conversions from unit tests
keptsecret Sep 12, 2025
80c4f67
Merge branch 'fix_rotation_mat' into bxdf_fixes_cook_torrance
keptsecret Sep 15, 2025
6233bd1
Merge branch 'master' into bxdf_fixes_cook_torrance
keptsecret Sep 15, 2025
ce5fbac
vector hashes
keptsecret Sep 15, 2025
47e814b
Merge branch 'fix_rotation_mat' into bxdf_fixes_cook_torrance
keptsecret Sep 15, 2025
c983975
Merge branch 'master' into bxdf_fixes_cook_torrance
keptsecret Sep 16, 2025
0f2ee0b
fix fresnel usage in bxdf pdf
keptsecret Sep 17, 2025
b5f02e6
fix F calc in generate
keptsecret Sep 17, 2025
a111415
merge master, fix conflicts
keptsecret Sep 22, 2025
9655049
minor fixes to non cook torrance bxdf generate
keptsecret Sep 22, 2025
a1743d2
split out bxdf concept typdefs
keptsecret Sep 22, 2025
e6d663b
removed redundant thin_smooth_dielectric create
keptsecret Sep 22, 2025
340cee3
added and use notEqual spirv intrinsic
keptsecret Sep 22, 2025
a3733b1
moved polar coord stuff into its own file
keptsecret Sep 22, 2025
3e3589b
made smith functions return measureless
keptsecret Sep 22, 2025
4bdf199
use type alias macro from config
keptsecret Sep 22, 2025
4faecc3
adjust ndf concept, change fresnel conductor ior to eta
keptsecret Sep 23, 2025
407da2f
combine brdf/bsdf cook torrance into same struct, old stuff commented…
keptsecret Sep 23, 2025
638b8b5
moved duplicate code in eval, pdf, quotient_pdf into templated intera…
keptsecret Sep 23, 2025
c587820
some changes to fresnel, cook torrance base
keptsecret Sep 24, 2025
d438360
reverted ggx ndf to use optimizations
keptsecret Sep 24, 2025
5f49f11
pdf function checks for backfacing V
keptsecret Sep 25, 2025
627074b
reduced beckmann, ggx ndfs to single struct with enable_ifs
keptsecret Sep 25, 2025
3896231
put concepts back in for ndf impl
keptsecret Sep 26, 2025
91b39d5
ggx ndf determine clamp with template bool
keptsecret Sep 26, 2025
2a3cda3
moved A out of ndf base into generate base
keptsecret Sep 26, 2025
c9f9366
added create methods to ndfs, cook torrance base; slight changes to c…
keptsecret Sep 26, 2025
fd128e6
make beckmann + ggx bxdfs typedefs of cooktorrance base, functionalit…
keptsecret Sep 26, 2025
882375e
added flipSign func that copies sign of rhs
keptsecret Sep 29, 2025
b22d570
remove unused var
keptsecret Sep 29, 2025
c0586f5
moved cook torrance base into base folder, removed commented out in b…
keptsecret Sep 29, 2025
9eeb248
make lambertian + oren nayar bxdfs typedefs of corresponding base, fu…
keptsecret Sep 29, 2025
26c76e4
fix microfacet bxdf concept
keptsecret Sep 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
300 changes: 300 additions & 0 deletions include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
// Copyright (C) 2018-2025 - DevSH Graphics Programming Sp. z O.O.
// This file is part of the "Nabla Engine".
// For conditions of distribution and use, see copyright notice in nabla.h
#ifndef _NBL_BUILTIN_HLSL_BXDF_COOK_TORRANCE_INCLUDED_
#define _NBL_BUILTIN_HLSL_BXDF_COOK_TORRANCE_INCLUDED_

#include "nbl/builtin/hlsl/bxdf/common.hlsl"
#include "nbl/builtin/hlsl/bxdf/config.hlsl"
#include "nbl/builtin/hlsl/bxdf/ndf.hlsl"
#include "nbl/builtin/hlsl/bxdf/fresnel.hlsl"
#include "nbl/builtin/hlsl/bxdf/ndf/microfacet_to_light_transform.hlsl"

namespace nbl
{
namespace hlsl
{
namespace bxdf
{

namespace impl
{
template<typename T, typename U>
struct __implicit_promote;

template<typename T>
struct __implicit_promote<T,T>
{
static T __call(const T v)
{
return v;
}
};

template<typename T>
struct __implicit_promote<T,vector<typename vector_traits<T>::scalar_type, 1> >
{
static T __call(const vector<typename vector_traits<T>::scalar_type, 1> v)
{
return hlsl::promote<T>(v[0]);
}
};

template<class N, class F, bool IsBSDF>
struct quant_query_helper;

template<class N, class F>
struct quant_query_helper<N, F, true>
{
using quant_query_type = typename N::quant_query_type;

template<class C>
static quant_query_type __call(NBL_REF_ARG(N) ndf, NBL_CONST_REF_ARG(F) fresnel, NBL_CONST_REF_ARG(C) cache)
{
return ndf.template createQuantQuery<C>(cache, fresnel.orientedEta.value[0]);
}
};

template<class N, class F>
struct quant_query_helper<N, F, false>
{
using quant_query_type = typename N::quant_query_type;

template<class C>
static quant_query_type __call(NBL_REF_ARG(N) ndf, NBL_CONST_REF_ARG(F) fresnel, NBL_CONST_REF_ARG(C) cache)
{
typename N::scalar_type dummy;
return ndf.template createQuantQuery<C>(cache, dummy);
}
};

template<class F, typename Spectral, bool IsAnisotropic>
struct CookTorranceParams;

template<class F, typename Spectral>
struct CookTorranceParams<F,Spectral,false>
{
using scalar_type = typename F::scalar_type;
using spectral_type = Spectral;

scalar_type A;
F fresnel;
};

template<class F, typename Spectral>
struct CookTorranceParams<F,Spectral,true>
{
using scalar_type = typename F::scalar_type;
using spectral_type = Spectral;

scalar_type ax;
scalar_type ay;
F fresnel;
};
}

// N (NDF), F (fresnel)
template<class Config, class N, class F NBL_PRIMARY_REQUIRES(config_concepts::MicrofacetConfiguration<Config> && ndf::NDF<N> && fresnel::Fresnel<F>)
struct SCookTorrance
{
MICROFACET_BXDF_CONFIG_TYPE_ALIASES(Config);

using this_t = SCookTorrance<Config, N, F>;
using quant_type = typename N::quant_type;
using ndf_type = N;
using fresnel_type = F;

NBL_CONSTEXPR_STATIC_INLINE bool IsAnisotropic = ndf_type::IsAnisotropic;
NBL_CONSTEXPR_STATIC_INLINE bool IsBSDF = ndf_type::IsBSDF;

using creation_params_type = impl::CookTorranceParams<fresnel_type, spectral_type, IsAnisotropic>;

template<typename C=bool_constant<!IsAnisotropic> >
static enable_if_t<C::value && !IsAnisotropic, this_t> create(NBL_CONST_REF_ARG(creation_params_type) params)
{
this_t retval;
retval.ndf = ndf_type::create(params.A);
retval.fresnel = params.fresnel;
return retval;
}
template<typename C=bool_constant<IsAnisotropic> >
static enable_if_t<C::value && IsAnisotropic, this_t> create(NBL_CONST_REF_ARG(creation_params_type) params)
{
this_t retval;
retval.ndf = ndf_type::create(params.ax, params.ay);
retval.fresnel = params.fresnel;
return retval;
}

template<class Interaction, class MicrofacetCache>
spectral_type __eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
if (IsBSDF || (_sample.getNdotL() > numeric_limits<scalar_type>::min && interaction.getNdotV() > numeric_limits<scalar_type>::min))
{
using quant_query_type = typename ndf_type::quant_query_type;
using g2g1_query_type = typename ndf_type::g2g1_query_type;

scalar_type dummy;
quant_query_type qq = ndf.template createQuantQuery<MicrofacetCache>(cache, dummy);

quant_type D = ndf.template D<sample_type, Interaction, MicrofacetCache>(qq, _sample, interaction, cache);
scalar_type DG = D.projectedLightMeasure;
if (D.microfacetMeasure < numeric_limits<scalar_type>::infinity)
{
g2g1_query_type gq = ndf.template createG2G1Query<sample_type, Interaction>(_sample, interaction);
DG *= ndf.template correlated<sample_type, Interaction>(gq, _sample, interaction);
}
NBL_IF_CONSTEXPR(IsBSDF)
return impl::__implicit_promote<spectral_type, typename fresnel_type::vector_type>::__call(fresnel(hlsl::abs(cache.getVdotH()))) * DG;
else
return impl::__implicit_promote<spectral_type, typename fresnel_type::vector_type>::__call(fresnel(cache.getVdotH())) * DG;
}
else
return hlsl::promote<spectral_type>(0.0);
}
template<typename C=bool_constant<!IsAnisotropic> >
enable_if_t<C::value && !IsAnisotropic, spectral_type> eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache)
{
return __eval<isotropic_interaction_type, isocache_type>(_sample, interaction, cache);
}
spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache)
{
return __eval<anisotropic_interaction_type, anisocache_type>(_sample, interaction, cache);
}

template<typename C=bool_constant<!IsBSDF> >
enable_if_t<C::value && !IsBSDF, sample_type> generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(anisocache_type) cache)
{
const vector3_type localV = interaction.getTangentSpaceV();
const vector3_type localH = ndf.generateH(localV, u);

cache = anisocache_type::createForReflection(localV, localH);
ray_dir_info_type localL;
bxdf::Reflect<scalar_type> r = bxdf::Reflect<scalar_type>::create(localV, localH);
localL = localL.reflect(r);

return sample_type::createFromTangentSpace(localL, interaction.getFromTangentSpace());
}
template<typename C=bool_constant<IsBSDF> >
enable_if_t<C::value && IsBSDF, sample_type> generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(anisocache_type) cache)
{
fresnel::OrientedEtaRcps<monochrome_type> rcpEta = fresnel.getOrientedEtaRcps();

const vector3_type localV = interaction.getTangentSpaceV();
const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative<vector3_type>(localV, hlsl::promote<vector3_type>(interaction.getNdotV())); //hlsl::mix(localV, -localV, interaction.getNdotV() < scalar_type(0.0));
const vector3_type localH = ndf.generateH(upperHemisphereV, u.xy);

const scalar_type VdotH = hlsl::dot(localV, localH);
const scalar_type reflectance = fresnel(hlsl::abs(VdotH))[0];

scalar_type rcpChoiceProb;
scalar_type z = u.z;
bool transmitted = math::partitionRandVariable(reflectance, z, rcpChoiceProb);

Refract<scalar_type> r = Refract<scalar_type>::create(localV, localH);
ray_dir_info_type localL;
bxdf::ReflectRefract<scalar_type> rr;
rr.refract = r;
localL = localL.reflectRefract(rr, transmitted, rcpEta.value[0]);

// fail if samples have invalid paths
// TODO fix this: if (ComputeMicrofacetNormal<scalar_type>::isTransmissionPath(VdotH, hlsl::dot(localL.getDirection(), localH)) != transmitted)
if ((!transmitted && hlsl::sign(localL.getDirection().z) != hlsl::sign(localV.z)) || (transmitted && hlsl::sign(localL.getDirection().z) == hlsl::sign(localV.z)))
{
localL.direction = vector3_type(0,0,0); // should check if sample direction is invalid
}
else
cache = anisocache_type::create(localV, localH, transmitted, rcpEta);

return sample_type::createFromTangentSpace(localL, interaction.getFromTangentSpace());
}
template<typename C=bool_constant<!IsAnisotropic>, typename D=bool_constant<!IsBSDF> >
enable_if_t<C::value && !IsAnisotropic && D::value && !IsBSDF, sample_type> generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u, NBL_REF_ARG(isocache_type) cache)
{
anisocache_type aniso_cache;
sample_type s = generate(anisotropic_interaction_type::create(interaction), u, aniso_cache);
cache = aniso_cache.iso_cache;
return s;
}
template<typename C=bool_constant<!IsAnisotropic>, typename D=bool_constant<IsBSDF> >
enable_if_t<C::value && !IsAnisotropic && D::value && IsBSDF, sample_type> generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector3_type u, NBL_REF_ARG(isocache_type) cache)
{
anisocache_type aniso_cache;
sample_type s = generate(anisotropic_interaction_type::create(interaction), u, aniso_cache);
cache = aniso_cache.iso_cache;
return s;
}

template<class Interaction, class MicrofacetCache>
scalar_type __pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
using quant_query_type = typename ndf_type::quant_query_type;
using dg1_query_type = typename ndf_type::dg1_query_type;

dg1_query_type dq = ndf.template createDG1Query<Interaction, MicrofacetCache>(interaction, cache);

quant_query_type qq = impl::quant_query_helper<ndf_type, fresnel_type, IsBSDF>::template __call<MicrofacetCache>(ndf, fresnel, cache);
quant_type DG1 = ndf.template DG1<sample_type, Interaction>(dq, qq, _sample, interaction);

NBL_IF_CONSTEXPR(IsBSDF)
{
const scalar_type reflectance = fresnel(hlsl::abs(cache.getVdotH()))[0];
return hlsl::mix(reflectance, scalar_type(1.0) - reflectance, cache.isTransmission()) * DG1.projectedLightMeasure;
}
else
{
return DG1.projectedLightMeasure;
}
}
template<typename C=bool_constant<!IsAnisotropic> >
enable_if_t<C::value && !IsAnisotropic, 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)
{
if (IsBSDF || (_sample.getNdotL() > numeric_limits<scalar_type>::min && interaction.getNdotV() > numeric_limits<scalar_type>::min))
return __pdf<isotropic_interaction_type, isocache_type>(_sample, interaction, cache);
else
return scalar_type(0.0);
}
scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache)
{
if (IsBSDF || (_sample.getNdotL() > numeric_limits<scalar_type>::min && interaction.getNdotV() > numeric_limits<scalar_type>::min))
return __pdf<anisotropic_interaction_type, anisocache_type>(_sample, interaction, cache);
else
return scalar_type(0.0);
}

template<class Interaction, class MicrofacetCache>
quotient_pdf_type __quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
{
scalar_type _pdf = __pdf<Interaction, MicrofacetCache>(_sample, interaction, cache);

spectral_type quo = hlsl::promote<spectral_type>(0.0);

using g2g1_query_type = typename N::g2g1_query_type;
g2g1_query_type gq = ndf.template createG2G1Query<sample_type, Interaction>(_sample, interaction);
scalar_type G2_over_G1 = ndf.template G2_over_G1<sample_type, Interaction, MicrofacetCache>(gq, _sample, interaction, cache);
NBL_IF_CONSTEXPR(IsBSDF)
quo = hlsl::promote<spectral_type>(G2_over_G1);
else
quo = fresnel(cache.getVdotH()) * G2_over_G1;

return quotient_pdf_type::create(quo, _pdf);
}
template<typename C=bool_constant<!IsAnisotropic> >
enable_if_t<C::value && !IsAnisotropic, quotient_pdf_type> quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, NBL_CONST_REF_ARG(isocache_type) cache)
{
return __quotient_and_pdf<isotropic_interaction_type, isocache_type>(_sample, interaction, cache);
}
quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, NBL_CONST_REF_ARG(anisocache_type) cache)
{
return __quotient_and_pdf<anisotropic_interaction_type, anisocache_type>(_sample, interaction, cache);
}

ndf_type ndf;
fresnel_type fresnel;
};

}
}
}

#endif
31 changes: 24 additions & 7 deletions include/nbl/builtin/hlsl/bxdf/base/lambertian.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,39 @@ struct SLambertianBase
{
return hlsl::promote<spectral_type>(_sample.getNdotL(_clamp) * numbers::inv_pi<scalar_type> * hlsl::mix(1.0, 0.5, IsBSDF));
}
spectral_type eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction)
{
return eval(_sample, interaction.isotropic);
}

sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u)
template<typename C=bool_constant<!IsBSDF> >
enable_if_t<C::value && !IsBSDF, sample_type> generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector2_type u)
{
// static_assert(!IsBSDF);
ray_dir_info_type L;
L.direction = sampling::ProjectedHemisphere<scalar_type>::generate(u);
return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace());
}

sample_type generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u)
template<typename C=bool_constant<IsBSDF> >
enable_if_t<C::value && IsBSDF, sample_type> generate(NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction, const vector3_type u)
{
// static_assert(IsBSDF);
ray_dir_info_type L;
L.direction = sampling::ProjectedSphere<scalar_type>::generate(u);
return sample_type::createFromTangentSpace(L, interaction.getFromTangentSpace());
}
template<typename C=bool_constant<!IsBSDF> >
enable_if_t<C::value && !IsBSDF, sample_type> generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector2_type u)
{
return generate(anisotropic_interaction_type::create(interaction), u);
}
template<typename C=bool_constant<IsBSDF> >
enable_if_t<C::value && IsBSDF, sample_type> generate(NBL_CONST_REF_ARG(isotropic_interaction_type) interaction, const vector3_type u)
{
return generate(anisotropic_interaction_type::create(interaction), u);
}

scalar_type pdf(NBL_CONST_REF_ARG(sample_type) _sample)
{
if (IsBSDF)
NBL_IF_CONSTEXPR (IsBSDF)
return sampling::ProjectedSphere<scalar_type>::pdf(_sample.getNdotL(_clamp));
else
return sampling::ProjectedHemisphere<scalar_type>::pdf(_sample.getNdotL(_clamp));
Expand All @@ -57,12 +70,16 @@ struct SLambertianBase
quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(isotropic_interaction_type) interaction)
{
sampling::quotient_and_pdf<monochrome_type, scalar_type> qp;
if (IsBSDF)
NBL_IF_CONSTEXPR (IsBSDF)
qp = sampling::ProjectedSphere<scalar_type>::template quotient_and_pdf(_sample.getNdotL(_clamp));
else
qp = sampling::ProjectedHemisphere<scalar_type>::template quotient_and_pdf(_sample.getNdotL(_clamp));
return quotient_pdf_type::create(qp.quotient[0], qp.pdf);
}
quotient_pdf_type quotient_and_pdf(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(anisotropic_interaction_type) interaction)
{
return quotient_and_pdf(_sample, interaction.isotropic);
}
};

}
Expand Down
Loading
Loading