Skip to content

Commit 069f499

Browse files
committed
big fixes to cook torrance bxdf generate
1 parent 23763e2 commit 069f499

File tree

2 files changed

+102
-56
lines changed

2 files changed

+102
-56
lines changed

include/nbl/builtin/hlsl/bxdf/base/cook_torrance_base.hlsl

Lines changed: 86 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -134,30 +134,34 @@ struct SCookTorrance
134134
template<class Interaction, class MicrofacetCache>
135135
spectral_type __eval(NBL_CONST_REF_ARG(sample_type) _sample, NBL_CONST_REF_ARG(Interaction) interaction, NBL_CONST_REF_ARG(MicrofacetCache) cache)
136136
{
137-
fresnel_type _f = impl::getOrientedFresnel<fresnel_type, IsBSDF>::__call(fresnel, interaction.getNdotV());
138-
const bool notTIR = impl::check_TIR_helper<fresnel_type, IsBSDF>::template __call<MicrofacetCache>(_f, cache);
139-
if ((IsBSDF && notTIR) || (!IsBSDF && _sample.getNdotL() > numeric_limits<scalar_type>::min && interaction.getNdotV() > numeric_limits<scalar_type>::min))
137+
bool valid;
138+
fresnel_type _f = fresnel;
139+
NBL_IF_CONSTEXPR(IsBSDF)
140140
{
141-
using quant_query_type = typename ndf_type::quant_query_type;
142-
using g2g1_query_type = typename ndf_type::g2g1_query_type;
143-
144-
scalar_type dummy;
145-
quant_query_type qq = ndf.template createQuantQuery<MicrofacetCache>(cache, dummy);
146-
147-
quant_type D = ndf.template D<sample_type, Interaction, MicrofacetCache>(qq, _sample, interaction, cache);
148-
scalar_type DG = D.projectedLightMeasure;
149-
if (D.microfacetMeasure < bit_cast<scalar_type>(numeric_limits<scalar_type>::infinity))
150-
{
151-
g2g1_query_type gq = ndf.template createG2G1Query<sample_type, Interaction>(_sample, interaction);
152-
DG *= ndf.template correlated<sample_type, Interaction>(gq, _sample, interaction);
153-
}
154-
NBL_IF_CONSTEXPR(IsBSDF)
155-
return impl::__implicit_promote<spectral_type, typename fresnel_type::vector_type>::__call(_f(hlsl::abs(cache.getVdotH()))) * DG;
156-
else
157-
return impl::__implicit_promote<spectral_type, typename fresnel_type::vector_type>::__call(_f(cache.getVdotH())) * DG;
141+
_f = impl::getOrientedFresnel<fresnel_type, IsBSDF>::__call(fresnel, interaction.getNdotV());
142+
valid = impl::check_TIR_helper<fresnel_type, IsBSDF>::template __call<MicrofacetCache>(_f, cache);
158143
}
159144
else
145+
valid = _sample.getNdotL() > numeric_limits<scalar_type>::min && interaction.getNdotV() > numeric_limits<scalar_type>::min;
146+
if (!valid)
160147
return hlsl::promote<spectral_type>(0.0);
148+
149+
using quant_query_type = typename ndf_type::quant_query_type;
150+
scalar_type dummy;
151+
quant_query_type qq = ndf.template createQuantQuery<MicrofacetCache>(cache, dummy);
152+
153+
quant_type D = ndf.template D<sample_type, Interaction, MicrofacetCache>(qq, _sample, interaction, cache);
154+
scalar_type DG = D.projectedLightMeasure;
155+
if (D.microfacetMeasure < bit_cast<scalar_type>(numeric_limits<scalar_type>::infinity))
156+
{
157+
using g2g1_query_type = typename ndf_type::g2g1_query_type;
158+
g2g1_query_type gq = ndf.template createG2G1Query<sample_type, Interaction>(_sample, interaction);
159+
DG *= ndf.template correlated<sample_type, Interaction>(gq, _sample, interaction);
160+
}
161+
scalar_type clampedVdotH = cache.getVdotH();
162+
NBL_IF_CONSTEXPR(IsBSDF)
163+
clampedVdotH = hlsl::abs(clampedVdotH);
164+
return impl::__implicit_promote<spectral_type, typename fresnel_type::vector_type>::__call(_f(clampedVdotH)) * DG;
161165
}
162166
template<typename C=bool_constant<!IsAnisotropic> >
163167
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)
@@ -172,66 +176,100 @@ struct SCookTorrance
172176
template<typename C=bool_constant<!IsBSDF> >
173177
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)
174178
{
175-
ray_dir_info_type localV_raydir = interaction.getV().transform(interaction.getToTangentSpace());
179+
if (interaction.getNdotV() > numeric_limits<scalar_type>::min)
180+
{
181+
ray_dir_info_type invalidL;
182+
invalidL.makeInvalid();
183+
return sample_type::createFromTangentSpace(invalidL, interaction.getFromTangentSpace());
184+
}
185+
176186
const vector3_type localV = interaction.getTangentSpaceV();
177187
const vector3_type localH = ndf.generateH(localV, u);
188+
const scalar_type VdotH = hlsl::dot(localV, localH);
189+
const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH);
178190

179-
cache = anisocache_type::createForReflection(localV, localH);
180-
struct reflect_wrapper
191+
ray_dir_info_type L;
192+
if (scalar_type(2.0) * VdotH * localH.z > localV.z) // NdotL>0, compiler's Common Subexpression Elimination pass should re-use 2*VdotH later
181193
{
182-
vector3_type operator()() NBL_CONST_MEMBER_FUNC
194+
assert(VdotH >= scalar_type(0.0));
195+
ray_dir_info_type V = interaction.getV();
196+
struct reflect_wrapper // so we don't recalculate VdotH
183197
{
184-
return r(VdotH);
185-
}
186-
bxdf::Reflect<scalar_type> r;
187-
scalar_type VdotH;
188-
};
189-
reflect_wrapper r;
190-
r.r = bxdf::Reflect<scalar_type>::create(localV, localH);
191-
r.VdotH = cache.getVdotH();
192-
ray_dir_info_type localL = localV_raydir.template reflect<reflect_wrapper>(r);
198+
vector3_type operator()() NBL_CONST_MEMBER_FUNC
199+
{
200+
return r(VdotH);
201+
}
202+
bxdf::Reflect<scalar_type> r;
203+
scalar_type VdotH;
204+
};
205+
reflect_wrapper rw;
206+
rw.r = bxdf::Reflect<scalar_type>::create(V.getDirection(), H);
207+
rw.VdotH = VdotH;
208+
L = V.template reflect<reflect_wrapper>(rw);
209+
210+
cache = anisocache_type::createForReflection(localV, localH);
211+
}
212+
else // fail if samples have invalid paths
213+
L.makeInvalid(); // should check if sample direction is invalid
193214

194-
// fail if samples have invalid paths
195-
if (localL.getDirection().z < scalar_type(0.0)) // NdotL<0
196-
localL.makeInvalid(); // should check if sample direction is invalid
215+
const vector3_type T = interaction.getT();
216+
const vector3_type B = interaction.getB();
217+
const vector3_type _N = interaction.getN();
197218

198-
return sample_type::createFromTangentSpace(localL, interaction.getFromTangentSpace());
219+
return sample_type::create(L, T, B, _N);
199220
}
200221
template<typename C=bool_constant<IsBSDF> >
201222
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)
202223
{
203-
fresnel_type _f = impl::getOrientedFresnel<fresnel_type, IsBSDF>::__call(fresnel, interaction.getNdotV());
224+
const vector3_type localV = interaction.getTangentSpaceV();
225+
const scalar_type NdotV = localV.z;
226+
227+
fresnel_type _f = impl::getOrientedFresnel<fresnel_type, IsBSDF>::__call(fresnel, NdotV);
204228
fresnel::OrientedEtaRcps<monochrome_type> rcpEta = _f.getOrientedEtaRcps();
205229

206-
ray_dir_info_type V = interaction.getV();
207-
const vector3_type localV = interaction.getTangentSpaceV();
208-
const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative<vector3_type>(localV, hlsl::promote<vector3_type>(interaction.getNdotV()));
230+
const vector3_type upperHemisphereV = ieee754::flipSignIfRHSNegative<vector3_type>(localV, hlsl::promote<vector3_type>(NdotV));
209231
const vector3_type localH = ndf.generateH(upperHemisphereV, u.xy);
232+
const scalar_type VdotH = hlsl::dot(localV, localH);
210233
const vector3_type H = hlsl::mul(interaction.getFromTangentSpace(), localH);
211234

212-
const scalar_type VdotH = hlsl::dot(V.getDirection(), H);
235+
assert(NdotV * VdotH > scalar_type(0.0));
213236
const scalar_type reflectance = _f(hlsl::abs(VdotH))[0];
214237

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

242+
ray_dir_info_type V = interaction.getV();
243+
const vector3_type _N = interaction.getN();
219244
Refract<scalar_type> r = Refract<scalar_type>::create(V.getDirection(), H);
245+
const scalar_type LdotH = hlsl::mix(VdotH, r.getNdotT(rcpEta.value2[0]), transmitted);
246+
cache = anisocache_type::createPartial(VdotH, LdotH, hlsl::dot(_N, H), transmitted, rcpEta);
247+
248+
struct reflect_refract_wrapper // so we don't recalculate LdotH
249+
{
250+
vector3_type operator()(const bool doRefract, const scalar_type rcpOrientedEta) NBL_CONST_MEMBER_FUNC
251+
{
252+
return rr(NdotTorR, rcpOrientedEta);
253+
}
254+
bxdf::ReflectRefract<scalar_type> rr;
255+
scalar_type NdotTorR;
256+
};
220257
bxdf::ReflectRefract<scalar_type> rr;
221258
rr.refract = r;
222-
ray_dir_info_type L = V.reflectRefract(rr, transmitted, rcpEta.value[0]);
259+
reflect_refract_wrapper rrw;
260+
rrw.rr = rr;
261+
rrw.NdotTorR = LdotH;
262+
ray_dir_info_type L = V.template reflectRefract<reflect_refract_wrapper>(rrw, transmitted, rcpEta.value[0]);
223263

224264
const vector3_type T = interaction.getT();
225265
const vector3_type B = interaction.getB();
226-
const vector3_type _N = interaction.getN();
227266

228267
// fail if samples have invalid paths
229-
const vector3_type Ldir = L.getDirection();
230-
const scalar_type LdotH = hlsl::dot(Ldir, H);
231-
if ((ComputeMicrofacetNormal<scalar_type>::isTransmissionPath(VdotH, LdotH) != transmitted) || (LdotH * hlsl::dot(_N, Ldir) < scalar_type(0.0)))
268+
const scalar_type NdotL = scalar_type(2.0) * VdotH * localH.z - localV.z;
269+
if ((ComputeMicrofacetNormal<scalar_type>::isTransmissionPath(NdotV, NdotL) != transmitted))
232270
L.makeInvalid(); // should check if sample direction is invalid
233271
else
234-
cache = anisocache_type::create(VdotH, Ldir, H, T, B, _N, transmitted);
272+
cache.fillTangents(T, B, H);
235273

236274
return sample_type::create(L, T, B, _N);
237275
}

include/nbl/builtin/hlsl/bxdf/common.hlsl

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,8 @@ NBL_CONCEPT_END(
694694
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template createForReflection<surface_interactions::SAnisotropic<surface_interactions::SIsotropic<ray_dir_info::SBasic<typename T::scalar_type> > >,SLightSample<ray_dir_info::SBasic<typename T::scalar_type> > >(aniso,_sample)), ::nbl::hlsl::is_same_v, T))
695695
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::create(V,V,V,V,V,eta,V)), ::nbl::hlsl::is_same_v, T))
696696
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::template create<surface_interactions::SAnisotropic<surface_interactions::SIsotropic<ray_dir_info::SBasic<typename T::scalar_type> > >,SLightSample<ray_dir_info::SBasic<typename T::scalar_type> > >(aniso,_sample,eta)), ::nbl::hlsl::is_same_v, T))
697+
((NBL_CONCEPT_REQ_EXPR_RET_TYPE)((T::createPartial(pNdotL,pNdotL,pNdotL,b0,rcp_eta)), ::nbl::hlsl::is_same_v, T))
698+
((NBL_CONCEPT_REQ_EXPR)(cache.fillTangents(V,V,V)))
697699
((NBL_CONCEPT_REQ_TYPE_ALIAS_CONCEPT)(CreatableIsotropicMicrofacetCache, typename T::isocache_type))
698700
);
699701
#undef rcp_eta
@@ -720,8 +722,8 @@ struct SAnisotropicMicrofacetCache
720722
static this_t createForReflection(const vector3_type tangentSpaceV, const vector3_type tangentSpaceH)
721723
{
722724
this_t retval;
723-
724725
retval.iso_cache.VdotH = nbl::hlsl::dot<vector3_type>(tangentSpaceV,tangentSpaceH);
726+
retval.iso_cache.VdotL = scalar_type(2.0) * retval.iso_cache.VdotH * retval.iso_cache.VdotH - scalar_type(1.0);
725727
retval.iso_cache.LdotH = retval.iso_cache.getVdotH();
726728
assert(tangentSpaceH.z >= scalar_type(0.0));
727729
retval.iso_cache.absNdotH = tangentSpaceH.z;
@@ -743,6 +745,7 @@ struct SAnisotropicMicrofacetCache
743745
{
744746
Refract<scalar_type> r = Refract<scalar_type>::create(tangentSpaceV, tangentSpaceH);
745747
retval.iso_cache.LdotH = r.getNdotT(rcpOrientedEta.value2[0]);
748+
retval.iso_cache.VdotL = retval.iso_cache.VdotH * (retval.iso_cache.LdotH - rcpOrientedEta.value[0] + retval.iso_cache.VdotH * rcpOrientedEta.value[0]);
746749
}
747750

748751
return retval;
@@ -793,23 +796,28 @@ struct SAnisotropicMicrofacetCache
793796
retval.BdotH = nbl::hlsl::dot<vector3_type>(interaction.getB(),H);
794797
return retval;
795798
}
796-
static this_t create(
797-
const scalar_type VdotH, const vector3_type L, const vector3_type H,
798-
const vector3_type T, const vector3_type B, const vector3_type N, bool transmitted
799+
static this_t createPartial(
800+
const scalar_type VdotH, const scalar_type LdotH, const scalar_type NdotH,
801+
bool transmitted, NBL_CONST_REF_ARG(fresnel::OrientedEtaRcps<monochrome_type>) rcpOrientedEta
799802
)
800803
{
801804
this_t retval;
802805
retval.iso_cache.VdotH = VdotH;
803-
retval.iso_cache.LdotH = hlsl::mix(VdotH, hlsl::dot(L, H), transmitted);
804-
scalar_type NdotH = hlsl::dot(N, H);
806+
retval.iso_cache.LdotH = LdotH;
807+
retval.iso_cache.VdotL = hlsl::mix(scalar_type(2.0) * retval.iso_cache.VdotH * retval.iso_cache.VdotH - scalar_type(1.0),
808+
VdotH * (LdotH - rcpOrientedEta.value[0] + VdotH * rcpOrientedEta.value[0]), transmitted);
805809
assert(NdotH > scalar_type(0.0));
806810
retval.iso_cache.absNdotH = hlsl::abs(NdotH);
807811
retval.iso_cache.NdotH2 = NdotH * NdotH;
808-
retval.TdotH = hlsl::dot(T, H);
809-
retval.BdotH = hlsl::dot(B, H);
810812
return retval;
811813
}
812814

815+
void fillTangents(const vector3_type T, const vector3_type B, const vector3_type H)
816+
{
817+
TdotH = hlsl::dot(T, H);
818+
BdotH = hlsl::dot(B, H);
819+
}
820+
813821
scalar_type getVdotL() NBL_CONST_MEMBER_FUNC { return iso_cache.getVdotL(); }
814822
scalar_type getVdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.getVdotH(); }
815823
scalar_type getLdotH() NBL_CONST_MEMBER_FUNC { return iso_cache.getLdotH(); }

0 commit comments

Comments
 (0)