diff --git a/include/mitsuba/render/bsdf.h b/include/mitsuba/render/bsdf.h index 0842860852..33ae81219d 100644 --- a/include/mitsuba/render/bsdf.h +++ b/include/mitsuba/render/bsdf.h @@ -129,7 +129,9 @@ MI_DECLARE_ENUM_OPERATORS(BSDFFlags) * The \ref BSDFContext data structure encodes these preferences and is * supplied to most \ref BSDF methods. */ -struct MI_EXPORT_LIB BSDFContext { +template struct BSDFContext { + using Index = dr::uint32_array_t; + // ============================================================= //! @{ \name Fields // ============================================================= @@ -141,18 +143,16 @@ struct MI_EXPORT_LIB BSDFContext { * Bit mask for requested BSDF component types to be sampled/evaluated * The default value (equal to \ref BSDFFlags::All) enables all components. */ - uint32_t type_mask = (uint32_t) 0x1FFu; + Index type_mask = (Index) 0x1FFu; /// Integer value of requested BSDF component index to be sampled/evaluated. - uint32_t component = (uint32_t) -1; + Index component = (Index) -1; //! @} // ============================================================= - BSDFContext() = default; - - BSDFContext(TransportMode mode, uint32_t type_mask = (uint32_t) 0x1FFu, - uint32_t component = (uint32_t) -1) + BSDFContext(TransportMode mode, Index type_mask = (Index) 0x1FFu, + Index component = (Index) -1) : mode(mode), type_mask(type_mask), component(component) { } /** @@ -168,11 +168,13 @@ struct MI_EXPORT_LIB BSDFContext { * Checks whether a given BSDF component type and BSDF component index are * enabled in this context. */ - bool is_enabled(BSDFFlags type_, uint32_t component_ = 0) const { - uint32_t type = (uint32_t) type_; - return (type_mask == (uint32_t) -1 || (type_mask & type) == type) - && (component == (uint32_t) -1 || component == component_); + dr::mask_t is_enabled(Index type_, Index component_ = 0) const { + Index type = (Index) type_; + return (dr::eq(type_mask, (Index) -1) || dr::eq(type_mask & type, type)) + && (dr::eq(component, (Index) -1) || dr::eq(component, component_)); } + + DRJIT_STRUCT(BSDFContext, mode, type_mask, component); }; /// Data structure holding the result of BSDF sampling operations. @@ -550,8 +552,65 @@ class MI_EXPORT_LIB BSDF : public Object { extern MI_EXPORT_LIB std::ostream &operator<<(std::ostream &os, const TransportMode &mode); -extern MI_EXPORT_LIB std::ostream &operator<<(std::ostream &os, - const BSDFContext& ctx); +template +std::string type_mask_to_string(Index type_mask) { + std::ostringstream oss; + oss << "{ "; + +#define PROCESS(flag, name) \ + if (has_flag(type_mask, flag)) { \ + oss << #name << " "; \ + type_mask = type_mask & ~flag; \ + } + PROCESS(BSDFFlags::All, all) + PROCESS(BSDFFlags::Reflection, reflection) + PROCESS(BSDFFlags::Transmission, transmission) + PROCESS(BSDFFlags::Smooth, smooth) + PROCESS(BSDFFlags::Diffuse, diffuse) + PROCESS(BSDFFlags::Glossy, glossy) + PROCESS(BSDFFlags::Delta, delta) + PROCESS(BSDFFlags::Delta1D, delta_1d) + PROCESS(BSDFFlags::DiffuseReflection, diffuse_reflection) + PROCESS(BSDFFlags::DiffuseTransmission, diffuse_transmission) + PROCESS(BSDFFlags::GlossyReflection, glossy_reflection) + PROCESS(BSDFFlags::GlossyTransmission, glossy_transmission) + PROCESS(BSDFFlags::DeltaReflection, delta_reflection) + PROCESS(BSDFFlags::DeltaTransmission, delta_transmission) + PROCESS(BSDFFlags::Delta1DReflection, delta_1d_reflection) + PROCESS(BSDFFlags::Delta1DTransmission, delta_1d_transmission) + PROCESS(BSDFFlags::Null, null) + PROCESS(BSDFFlags::Anisotropic, anisotropic) + PROCESS(BSDFFlags::FrontSide, front_side) + PROCESS(BSDFFlags::BackSide, back_side) + PROCESS(BSDFFlags::SpatiallyVarying, spatially_varying) + PROCESS(BSDFFlags::NonSymmetric, non_symmetric) +#undef PROCESS + + Assert(type_mask == 0); + oss << "}"; + return oss.str(); +} + +template +std::ostream &operator<<(std::ostream &os, const BSDFContext& ctx) { + if constexpr (dr::is_jit_v) { + os << "BSDFContext[" << std::endl + << " mode = " << ctx.mode << "," << std::endl + << " type_mask = " << ctx.type_mask << "," << std::endl + << " component = " << ctx.component; + } else { + os << "BSDFContext[" << std::endl + << " mode = " << ctx.mode << "," << std::endl + << " type_mask = " << type_mask_to_string(ctx.type_mask) << "," << std::endl + << " component = "; + if (ctx.component == (uint32_t) -1) + os << "all"; + else + os << ctx.component; + } + os << std::endl << "]"; + return os; +} template std::ostream &operator<<(std::ostream &os, const BSDFSample3& bs) { diff --git a/include/mitsuba/render/fwd.h b/include/mitsuba/render/fwd.h index 3bc6e23ff3..63ca855678 100644 --- a/include/mitsuba/render/fwd.h +++ b/include/mitsuba/render/fwd.h @@ -6,7 +6,6 @@ NAMESPACE_BEGIN(mitsuba) -struct BSDFContext; template class BSDF; template class OptixDenoiser; template class Emitter; @@ -37,6 +36,7 @@ template class MeshAttribute; template struct DirectionSample; template struct PositionSample; template struct BSDFSample3; +template struct BSDFContext; template struct PhaseFunctionContext; template struct Interaction; template struct MediumInteraction; @@ -64,6 +64,7 @@ template struct RenderAliases { using PositionSample3f = PositionSample; using DirectionSample3f = DirectionSample; using BSDFSample3f = BSDFSample3; + using BSDFContext = mitsuba::BSDFContext; using PhaseFunctionContext = mitsuba::PhaseFunctionContext; using Interaction3f = Interaction; using MediumInteraction3f = MediumInteraction; @@ -155,6 +156,7 @@ template struct RenderAliases { using MediumInteraction3f = typename RenderAliases::MediumInteraction3f; \ using PreliminaryIntersection3f = typename RenderAliases::PreliminaryIntersection3f; \ using BSDFSample3f = typename RenderAliases::BSDFSample3f; \ + using BSDFContext = typename RenderAliases::BSDFContext; \ DRJIT_MAP(MI_IMPORT_TYPES_MACRO, __VA_ARGS__) #define MI_IMPORT_OBJECT_TYPES() \ diff --git a/src/bsdfs/CMakeLists.txt b/src/bsdfs/CMakeLists.txt index 5ee85198db..c707131e92 100644 --- a/src/bsdfs/CMakeLists.txt +++ b/src/bsdfs/CMakeLists.txt @@ -7,9 +7,11 @@ add_plugin(dielectric dielectric.cpp) add_plugin(diffuse diffuse.cpp) add_plugin(mask mask.cpp) add_plugin(measured measured.cpp) +add_plugin(measured_polarized measured_polarized.cpp) add_plugin(normalmap normalmap.cpp) add_plugin(null null.cpp) add_plugin(plastic plastic.cpp) +add_plugin(pplastic pplastic.cpp) add_plugin(roughconductor roughconductor.cpp) add_plugin(roughdielectric roughdielectric.cpp) add_plugin(roughplastic roughplastic.cpp) @@ -18,8 +20,6 @@ add_plugin(twosided twosided.cpp) add_plugin(polarizer polarizer.cpp) add_plugin(retarder retarder.cpp) add_plugin(circular circular.cpp) -add_plugin(measured_polarized measured_polarized.cpp) -add_plugin(pplastic pplastic.cpp) add_plugin(principled principled.cpp) add_plugin(principledthin principledthin.cpp) diff --git a/src/bsdfs/blendbsdf.cpp b/src/bsdfs/blendbsdf.cpp index 85ef451d94..43778cb54e 100644 --- a/src/bsdfs/blendbsdf.cpp +++ b/src/bsdfs/blendbsdf.cpp @@ -115,24 +115,27 @@ class BlendBSDF final : public BSDF { Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFSample, active); + BSDFSample3f bs = dr::zeros(); + Spectrum result(0.f); + + Mask sample_all = dr::eq(ctx.component, (uint32_t) -1); + Float weight = eval_weight(si, active); - if (unlikely(ctx.component != (uint32_t) -1)) { - bool sample_first = ctx.component < m_nested_bsdf[0]->component_count(); + + if (dr::any_or(!sample_all)) { + Mask sample_first = ctx.component < m_nested_bsdf[0]->component_count(); BSDFContext ctx2(ctx); - if (!sample_first) - ctx2.component -= (uint32_t) m_nested_bsdf[0]->component_count(); - else - weight = 1.f - weight; - auto [bs, result] = m_nested_bsdf[sample_first ? 0 : 1]->sample(ctx2, si, sample1, sample2, active); - result *= weight; - return { bs, result }; + dr::masked(ctx2.component, !sample_first) -= (uint32_t) m_nested_bsdf[0]->component_count(); + dr::masked(weight, sample_first) = 1.f - weight; + auto [bs_, result_] = dr::select(sample_first, + m_nested_bsdf[0]->sample(ctx2, si, sample1, sample2, active), + m_nested_bsdf[1]->sample(ctx2, si, sample1, sample2, active)); + dr::masked(bs, !sample_all) = bs_; + dr::masked(result, !sample_all) = result_ * weight; } - BSDFSample3f bs = dr::zeros(); - Spectrum result(0.f); - - Mask m0 = active && sample1 > weight, - m1 = active && sample1 <= weight; + Mask m0 = active && sample1 > weight && sample_all, + m1 = active && sample1 <= weight && sample_all; if (dr::any_or(m0)) { auto [bs0, result0] = m_nested_bsdf[0]->sample( @@ -155,36 +158,57 @@ class BlendBSDF final : public BSDF { const Vector3f &wo, Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); + Spectrum result(0.f); + + Mask sample_all = dr::eq(ctx.component, (uint32_t) -1); Float weight = eval_weight(si, active); - if (unlikely(ctx.component != (uint32_t) -1)) { - bool sample_first = ctx.component < m_nested_bsdf[0]->component_count(); + + if (dr::any_or(!sample_all)) { + Mask sample_first = ctx.component < m_nested_bsdf[0]->component_count(); BSDFContext ctx2(ctx); - if (!sample_first) - ctx2.component -= (uint32_t) m_nested_bsdf[0]->component_count(); - else - weight = 1.f - weight; - return weight * m_nested_bsdf[sample_first ? 0 : 1]->eval(ctx2, si, wo, active); + dr::masked(ctx2.component, !sample_first) -= (uint32_t) m_nested_bsdf[0]->component_count(); + dr::masked(weight, sample_first) = 1.f - weight; + dr::masked(result, !sample_all) = + weight * dr::select(sample_first, + m_nested_bsdf[0]->eval(ctx2, si, wo, active), + m_nested_bsdf[1]->eval(ctx2, si, wo, active)); } - return m_nested_bsdf[0]->eval(ctx, si, wo, active) * (1 - weight) + - m_nested_bsdf[1]->eval(ctx, si, wo, active) * weight; + if (dr::any_or(sample_all)) { + dr::masked(result, sample_all) = + m_nested_bsdf[0]->eval(ctx, si, wo, active) * (1 - weight) + + m_nested_bsdf[1]->eval(ctx, si, wo, active) * weight; + } + + return result; } Float pdf(const BSDFContext &ctx, const SurfaceInteraction3f &si, const Vector3f &wo, Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); - if (unlikely(ctx.component != (uint32_t) -1)) { - bool sample_first = ctx.component < m_nested_bsdf[0]->component_count(); + Float result(0.f); + + Mask sample_all = dr::eq(ctx.component, (uint32_t) -1); + + if (dr::any_or(!sample_all)) { + Mask sample_first = ctx.component < m_nested_bsdf[0]->component_count(); BSDFContext ctx2(ctx); - if (!sample_first) - ctx2.component -= (uint32_t) m_nested_bsdf[0]->component_count(); - return m_nested_bsdf[sample_first ? 0 : 1]->pdf(ctx2, si, wo, active); + dr::masked(ctx2.component, !sample_first) -= (uint32_t) m_nested_bsdf[0]->component_count(); + dr::masked(result, !sample_all) = + dr::select(sample_first, + m_nested_bsdf[0]->pdf(ctx2, si, wo, active), + m_nested_bsdf[1]->pdf(ctx2, si, wo, active)); } - Float weight = eval_weight(si, active); - return m_nested_bsdf[0]->pdf(ctx, si, wo, active) * (1 - weight) + - m_nested_bsdf[1]->pdf(ctx, si, wo, active) * weight; + if (dr::any_or(sample_all)) { + Float weight = eval_weight(si, active); + dr::masked(result, sample_all) = + m_nested_bsdf[0]->pdf(ctx, si, wo, active) * (1 - weight) + + m_nested_bsdf[1]->pdf(ctx, si, wo, active) * weight; + } + + return result; } std::pair eval_pdf(const BSDFContext &ctx, @@ -193,24 +217,33 @@ class BlendBSDF final : public BSDF { Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); + Spectrum val(0.f); + Float pdf(0.f); + + Mask sample_all = dr::eq(ctx.component, (uint32_t) -1); Float weight = eval_weight(si, active); - if (unlikely(ctx.component != (uint32_t) -1)) { - bool sample_first = ctx.component < m_nested_bsdf[0]->component_count(); - BSDFContext ctx2(ctx); - if (!sample_first) - ctx2.component -= (uint32_t) m_nested_bsdf[0]->component_count(); - else - weight = 1.f - weight; - auto [val, pdf] = m_nested_bsdf[sample_first ? 0 : 1]->eval_pdf(ctx2, si, wo, active); - return { weight * val, pdf }; + if (dr::any_or(!sample_all)) { + Mask sample_first = ctx.component < m_nested_bsdf[0]->component_count(); + BSDFContext ctx2(ctx); + dr::masked(ctx2.component, !sample_first) -= (uint32_t) m_nested_bsdf[0]->component_count(); + dr::masked(weight, sample_first) = 1.f - weight; + auto [val_, pdf_] = + dr::select(sample_first, + m_nested_bsdf[0]->eval_pdf(ctx2, si, wo, active), + m_nested_bsdf[1]->eval_pdf(ctx2, si, wo, active)); + dr::masked(val, !sample_all) = weight * val_; + dr::masked(pdf, !sample_all) = pdf_; } - auto [val_0, pdf_0] = m_nested_bsdf[0]->eval_pdf(ctx, si, wo, active); - auto [val_1, pdf_1] = m_nested_bsdf[1]->eval_pdf(ctx, si, wo, active); + if (dr::any_or(sample_all)) { + auto [val_0, pdf_0] = m_nested_bsdf[0]->eval_pdf(ctx, si, wo, active); + auto [val_1, pdf_1] = m_nested_bsdf[1]->eval_pdf(ctx, si, wo, active); + dr::masked(val, sample_all) = val_0 * (1 - weight) + val_1 * weight; + dr::masked(pdf, sample_all) = pdf_0 * (1 - weight) + pdf_1 * weight; + } - return { val_0 * (1 - weight) + val_1 * weight, - pdf_0 * (1 - weight) + pdf_1 * weight }; + return { val, pdf }; } MI_INLINE Float eval_weight(const SurfaceInteraction3f &si, const Mask &active) const { diff --git a/src/bsdfs/conductor.cpp b/src/bsdfs/conductor.cpp index 6f8cf39663..7f8a081d22 100644 --- a/src/bsdfs/conductor.cpp +++ b/src/bsdfs/conductor.cpp @@ -257,7 +257,10 @@ class SmoothConductor final : public BSDF { BSDFSample3f bs = dr::zeros(); Spectrum value(0.f); - if (unlikely(dr::none_or(active) || !ctx.is_enabled(BSDFFlags::DeltaReflection))) + + active &= ctx.is_enabled(+BSDFFlags::DeltaReflection); + + if (unlikely(dr::none_or(active))) return { bs, value }; bs.sampled_component = 0; diff --git a/src/bsdfs/dielectric.cpp b/src/bsdfs/dielectric.cpp index f5b788cb23..00fd220785 100644 --- a/src/bsdfs/dielectric.cpp +++ b/src/bsdfs/dielectric.cpp @@ -254,8 +254,10 @@ class SmoothDielectric final : public BSDF { Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFSample, active); - bool has_reflection = ctx.is_enabled(BSDFFlags::DeltaReflection, 0), - has_transmission = ctx.is_enabled(BSDFFlags::DeltaTransmission, 1); + Mask has_reflection = ctx.is_enabled(+BSDFFlags::DeltaReflection, 0), + has_transmission = ctx.is_enabled(+BSDFFlags::DeltaTransmission, 1); + + active &= has_reflection | has_transmission; // Evaluate the Fresnel equations for unpolarized illumination Float cos_theta_i = Frame3f::cos_theta(si.wi); @@ -265,20 +267,19 @@ class SmoothDielectric final : public BSDF { // Lobe selection BSDFSample3f bs = dr::zeros(); - Mask selected_r; - if (likely(has_reflection && has_transmission)) { - selected_r = sample1 <= r_i && active; - bs.pdf = dr::detach(dr::select(selected_r, r_i, t_i)); - } else { - if (has_reflection || has_transmission) { - selected_r = Mask(has_reflection) && active; - bs.pdf = 1.f; - } else { - return { bs, 0.f }; - } - } + + if (unlikely(dr::none_or(active))) + return { bs, 0.f }; + + Mask selected_r = (sample1 <= r_i || !has_transmission) && has_reflection && active; Mask selected_t = !selected_r && active; + bs.pdf = dr::select( + has_reflection && has_transmission, + dr::detach(dr::select(selected_r, r_i, t_i)), + 1.f + ); + bs.sampled_component = dr::select(selected_r, UInt32(0), UInt32(1)); bs.sampled_type = dr::select(selected_r, UInt32(+BSDFFlags::DeltaReflection), UInt32(+BSDFFlags::DeltaTransmission)); @@ -309,12 +310,11 @@ class SmoothDielectric final : public BSDF { Spectrum R = mueller::specular_reflection(UnpolarizedSpectrum(cos_theta_o_hat), UnpolarizedSpectrum(m_eta)), T = mueller::specular_transmission(UnpolarizedSpectrum(cos_theta_o_hat), UnpolarizedSpectrum(m_eta)); - if (likely(has_reflection && has_transmission)) { - weight = dr::select(selected_r, R, T) / bs.pdf; - } else if (has_reflection || has_transmission) { - weight = has_reflection ? R : T; - bs.pdf = 1.f; - } + weight = dr::select( + has_reflection && has_transmission, + dr::select(selected_r, R, T) / bs.pdf, + dr::select(has_reflection, R, T) + ); /* The Stokes reference frame vector of this matrix lies perpendicular to the plane of reflection. */ @@ -335,17 +335,19 @@ class SmoothDielectric final : public BSDF { weight[selected_t] *= mueller::absorber(transmittance); } else { - if (likely(has_reflection && has_transmission)) { - weight = 1.f; - /* For differentiable variants, lobe choice has to be detached to avoid bias. - Sampling weights should be computed accordingly. */ - if constexpr (dr::is_diff_v) { - if (dr::grad_enabled(r_i)) { - weight = dr::select(selected_r, r_i / dr::detach(r_i), t_i / dr::detach(t_i)); - } + weight = dr::select( + has_reflection && has_transmission, + 1.f, + dr::select(has_reflection, r_i, t_i) + ); + + /* For differentiable variants, lobe choice has to be detached to + avoid bias. Sampling weights should be computed accordingly. */ + if constexpr (dr::is_diff_v) { + if (dr::grad_enabled(r_i)) { + dr::masked(weight, has_reflection && has_transmission) + = dr::select(selected_r, r_i / dr::detach(r_i), t_i / dr::detach(t_i)); } - } else if (has_reflection || has_transmission) { - weight = has_reflection ? r_i : t_i; } if (dr::any_or(selected_r)) diff --git a/src/bsdfs/diffuse.cpp b/src/bsdfs/diffuse.cpp index 4e00835383..829f34c112 100644 --- a/src/bsdfs/diffuse.cpp +++ b/src/bsdfs/diffuse.cpp @@ -108,9 +108,8 @@ class SmoothDiffuse final : public BSDF { Float cos_theta_i = Frame3f::cos_theta(si.wi); BSDFSample3f bs = dr::zeros(); - active &= cos_theta_i > 0.f; - if (unlikely(dr::none_or(active) || - !ctx.is_enabled(BSDFFlags::DiffuseReflection))) + active &= (cos_theta_i > 0.f) && ctx.is_enabled(+BSDFFlags::DiffuseReflection); + if (unlikely(dr::none_or(active))) return { bs, 0.f }; bs.wo = warp::square_to_cosine_hemisphere(sample2); @@ -128,8 +127,7 @@ class SmoothDiffuse final : public BSDF { const Vector3f &wo, Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); - if (!ctx.is_enabled(BSDFFlags::DiffuseReflection)) - return 0.f; + active &= ctx.is_enabled(+BSDFFlags::DiffuseReflection); Float cos_theta_i = Frame3f::cos_theta(si.wi), cos_theta_o = Frame3f::cos_theta(wo); @@ -146,8 +144,7 @@ class SmoothDiffuse final : public BSDF { const Vector3f &wo, Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); - if (!ctx.is_enabled(BSDFFlags::DiffuseReflection)) - return 0.f; + active &= ctx.is_enabled(+BSDFFlags::DiffuseReflection); Float cos_theta_i = Frame3f::cos_theta(si.wi), cos_theta_o = Frame3f::cos_theta(wo); @@ -163,8 +160,7 @@ class SmoothDiffuse final : public BSDF { Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); - if (!ctx.is_enabled(BSDFFlags::DiffuseReflection)) - return { 0.f, 0.f }; + active &= ctx.is_enabled(+BSDFFlags::DiffuseReflection); Float cos_theta_i = Frame3f::cos_theta(si.wi), cos_theta_o = Frame3f::cos_theta(wo); diff --git a/src/bsdfs/mask.cpp b/src/bsdfs/mask.cpp index 6c60901347..b7b2c91f0d 100644 --- a/src/bsdfs/mask.cpp +++ b/src/bsdfs/mask.cpp @@ -130,17 +130,15 @@ class MaskBSDF final : public BSDF { uint32_t null_index = (uint32_t) component_count() - 1; - bool sample_transmission = ctx.is_enabled(BSDFFlags::Null, null_index); - bool sample_nested = ctx.component == (uint32_t) -1 || ctx.component < null_index; + Mask sample_transmission = ctx.is_enabled(+BSDFFlags::Null, null_index); + Mask sample_nested = dr::eq(ctx.component, (uint32_t) -1) | (ctx.component < null_index); + active &= sample_transmission & sample_nested; BSDFSample3f bs = dr::zeros(); Spectrum result(0.f); - if (unlikely(!sample_transmission && !sample_nested)) - return { bs, result }; Float opacity = eval_opacity(si, active); - if (sample_transmission != sample_nested) - opacity = sample_transmission ? 1.f : 0.f; + dr::masked(opacity, sample_transmission != sample_nested) = 1.f; bs.wo = -si.wi; bs.eta = 1.f; @@ -173,15 +171,13 @@ class MaskBSDF final : public BSDF { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); uint32_t null_index = (uint32_t) component_count() - 1; - bool sample_transmission = ctx.is_enabled(BSDFFlags::Null, null_index); - bool sample_nested = ctx.component == (uint32_t) -1 || ctx.component < null_index; - - if (!sample_nested) - return 0.f; + Mask sample_transmission = ctx.is_enabled(+BSDFFlags::Null, null_index); + Mask sample_nested = dr::eq(ctx.component, (uint32_t) -1) | (ctx.component < null_index); + active &= sample_nested; Float result = m_nested_bsdf->pdf(ctx, si, wo, active); - if (sample_transmission) - result *= eval_opacity(si, active); + + dr::masked(result, sample_transmission) *= eval_opacity(si, active); return result; } @@ -193,19 +189,16 @@ class MaskBSDF final : public BSDF { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); uint32_t null_index = (uint32_t) component_count() - 1; - bool sample_transmission = ctx.is_enabled(BSDFFlags::Null, null_index); - bool sample_nested = ctx.component == (uint32_t) -1 || ctx.component < null_index; + Mask sample_transmission = ctx.is_enabled(+BSDFFlags::Null, null_index); + Mask sample_nested = dr::eq(ctx.component, (uint32_t) -1) | (ctx.component < null_index); + active &= sample_nested; auto [value, pdf] = m_nested_bsdf->eval_pdf(ctx, si, wo, active); Float opacity = eval_opacity(si, active); value *= opacity; - if (!sample_nested) - pdf = 0.f; - - if (sample_transmission) - pdf *= opacity; + dr::masked(pdf, sample_transmission) *= opacity; return { value, pdf }; } diff --git a/src/bsdfs/measured.cpp b/src/bsdfs/measured.cpp index 4a875ead1e..d8cba0779c 100644 --- a/src/bsdfs/measured.cpp +++ b/src/bsdfs/measured.cpp @@ -180,8 +180,9 @@ class Measured final : public BSDF { BSDFSample3f bs = dr::zeros(); Vector3f wi = si.wi; active &= Frame3f::cos_theta(wi) > 0; + active &= ctx.is_enabled(+BSDFFlags::GlossyReflection); - if (!ctx.is_enabled(BSDFFlags::GlossyReflection) || dr::none_or(active)) + if (dr::none_or(active)) return { bs, 0.f }; Float sx = -1.f, sy = -1.f; @@ -281,7 +282,9 @@ class Measured final : public BSDF { active &= Frame3f::cos_theta(wi) > 0.f && Frame3f::cos_theta(wo) > 0.f; - if (!ctx.is_enabled(BSDFFlags::GlossyReflection) || dr::none_or(active)) + active &= ctx.is_enabled(+BSDFFlags::GlossyReflection); + + if (dr::none_or(active)) return Spectrum(0.f); if (m_reduction >= 2) { @@ -334,7 +337,9 @@ class Measured final : public BSDF { active &= Frame3f::cos_theta(wi) > 0.f && Frame3f::cos_theta(wo) > 0.f; - if (!ctx.is_enabled(BSDFFlags::GlossyReflection) || dr::none_or(active)) + active &= ctx.is_enabled(+BSDFFlags::GlossyReflection); + + if (dr::none_or(active)) return 0.f; if (m_reduction >= 2) { diff --git a/src/bsdfs/measured_polarized.cpp b/src/bsdfs/measured_polarized.cpp index ec6e5067cc..81e5424547 100644 --- a/src/bsdfs/measured_polarized.cpp +++ b/src/bsdfs/measured_polarized.cpp @@ -183,8 +183,10 @@ class MeasuredPolarized final : public BSDF { Float cos_theta_i = Frame3f::cos_theta(si.wi); active &= cos_theta_i > 0.f; + active &= ctx.is_enabled(+BSDFFlags::GlossyReflection); + BSDFSample3f bs; - if (unlikely(dr::none_or(active) || !ctx.is_enabled(BSDFFlags::GlossyReflection))) + if (unlikely(dr::none_or(active))) return { bs, 0.f }; MicrofacetDistribution distr(MicrofacetType::GGX, @@ -219,7 +221,9 @@ class MeasuredPolarized final : public BSDF { cos_theta_o = Frame3f::cos_theta(wo); active &= (cos_theta_i > 0.f && cos_theta_o > 0.f); - if (unlikely(dr::none_or(active) || !ctx.is_enabled(BSDFFlags::GlossyReflection))) + active &= ctx.is_enabled(+BSDFFlags::GlossyReflection); + + if (unlikely(dr::none_or(active))) return 0.f; /* Due to the coordinate system rotations for polarization-aware @@ -323,7 +327,9 @@ class MeasuredPolarized final : public BSDF { const Vector3f &wo, Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); - if (unlikely(dr::none_or(active) || !ctx.is_enabled(BSDFFlags::GlossyReflection))) + active &= ctx.is_enabled(+BSDFFlags::GlossyReflection); + + if (unlikely(dr::none_or(active))) return 0.f; Float cos_theta_i = Frame3f::cos_theta(si.wi), diff --git a/src/bsdfs/null.cpp b/src/bsdfs/null.cpp index 90df05b242..c42e951207 100644 --- a/src/bsdfs/null.cpp +++ b/src/bsdfs/null.cpp @@ -45,15 +45,16 @@ class Null final : public BSDF { const Point2f & /*sample2*/, Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFSample, active); - bool sample_transmission = ctx.is_enabled(BSDFFlags::Null, 0); + Mask sample_transmission = ctx.is_enabled(+BSDFFlags::Null, 0); BSDFSample3f bs = dr::zeros(); Spectrum result(0.f); - if (sample_transmission) { - bs.wo = -si.wi; - bs.sampled_component = 0; - bs.sampled_type = UInt32(+BSDFFlags::Null); - bs.eta = 1.f; - bs.pdf = 1.f; + + if (dr::any_or(sample_transmission)) { + dr::masked(bs.wo, sample_transmission) = -si.wi; + dr::masked(bs.sampled_component, sample_transmission) = 0; + dr::masked(bs.sampled_type, sample_transmission) = UInt32(+BSDFFlags::Null); + dr::masked(bs.eta, sample_transmission) = 1.f; + dr::masked(bs.pdf, sample_transmission) = 1.f; /* In an ordinary BSDF we would use depolarizer(1.f) here to construct a depolarizing Mueller matrix. However, the null @@ -61,7 +62,7 @@ class Null final : public BSDF { this is one of the few places where it is safe to directly use a scalar (which will broadcast to the identity matrix in polarized rendering modes). */ - result = 1.f; + dr::masked(result, sample_transmission) = 1.f; } return { bs, result }; diff --git a/src/bsdfs/plastic.cpp b/src/bsdfs/plastic.cpp index e853af7b71..9481053deb 100644 --- a/src/bsdfs/plastic.cpp +++ b/src/bsdfs/plastic.cpp @@ -214,15 +214,17 @@ class SmoothPlastic final : public BSDF { Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFSample, active); - bool has_specular = ctx.is_enabled(BSDFFlags::DeltaReflection, 0), - has_diffuse = ctx.is_enabled(BSDFFlags::DiffuseReflection, 1); + Mask has_specular = ctx.is_enabled(+BSDFFlags::DeltaReflection, 0), + has_diffuse = ctx.is_enabled(+BSDFFlags::DiffuseReflection, 1); Float cos_theta_i = Frame3f::cos_theta(si.wi); active &= cos_theta_i > 0.f; + active &= (has_specular || has_diffuse); BSDFSample3f bs = dr::zeros(); UnpolarizedSpectrum result(0.f); - if (unlikely((!has_specular && !has_diffuse) || dr::none_or(active))) + + if (unlikely(dr::none_or(active))) return { bs, result }; // Determine which component should be sampled @@ -230,10 +232,11 @@ class SmoothPlastic final : public BSDF { prob_specular = f_i * m_specular_sampling_weight, prob_diffuse = (1.f - f_i) * (1.f - m_specular_sampling_weight); - if (unlikely(has_specular != has_diffuse)) - prob_specular = has_specular ? 1.f : 0.f; - else - prob_specular = prob_specular / (prob_specular + prob_diffuse); + prob_specular = dr::select( + dr::neq(has_specular, has_diffuse), + dr::select(has_specular, 1.f, 0.f), + prob_specular / (prob_specular + prob_diffuse) + ); prob_diffuse = 1.f - prob_specular; @@ -275,14 +278,13 @@ class SmoothPlastic final : public BSDF { const Vector3f &wo, Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); - bool has_diffuse = ctx.is_enabled(BSDFFlags::DiffuseReflection, 1); - Float cos_theta_i = Frame3f::cos_theta(si.wi), cos_theta_o = Frame3f::cos_theta(wo); active &= cos_theta_i > 0.f && cos_theta_o > 0.f; + active &= ctx.is_enabled(+BSDFFlags::DiffuseReflection, 1); - if (unlikely(!has_diffuse || dr::none_or(active))) + if (unlikely(dr::none_or(active))) return 0.f; Float f_i = std::get<0>(fresnel(cos_theta_i, Float(m_eta))), @@ -305,18 +307,17 @@ class SmoothPlastic final : public BSDF { cos_theta_o = Frame3f::cos_theta(wo); active &= cos_theta_i > 0.f && cos_theta_o > 0.f; + active &= ctx.is_enabled(+BSDFFlags::DiffuseReflection, 1); - if (unlikely(!ctx.is_enabled(BSDFFlags::DiffuseReflection, 1) || dr::none_or(active))) + if (unlikely(dr::none_or(active))) return 0.f; - Float prob_diffuse = 1.f; + Float f_i = std::get<0>(fresnel(cos_theta_i, Float(m_eta))); + Float prob_specular = f_i * m_specular_sampling_weight; + Float prob_diffuse = (1.f - f_i) * (1.f - m_specular_sampling_weight); + prob_diffuse = prob_diffuse / (prob_specular + prob_diffuse); - if (ctx.is_enabled(BSDFFlags::DeltaReflection, 0)) { - Float f_i = std::get<0>(fresnel(cos_theta_i, Float(m_eta))), - prob_specular = f_i * m_specular_sampling_weight; - prob_diffuse = (1.f - f_i) * (1.f - m_specular_sampling_weight); - prob_diffuse = prob_diffuse / (prob_specular + prob_diffuse); - } + dr::masked(prob_diffuse, !ctx.is_enabled(+BSDFFlags::DeltaReflection, 0)) = 1.f; Float pdf = warp::square_to_cosine_hemisphere_pdf(wo) * prob_diffuse; @@ -329,14 +330,13 @@ class SmoothPlastic final : public BSDF { Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); - bool has_diffuse = ctx.is_enabled(BSDFFlags::DiffuseReflection, 1); - Float cos_theta_i = Frame3f::cos_theta(si.wi), cos_theta_o = Frame3f::cos_theta(wo); active &= cos_theta_i > 0.f && cos_theta_o > 0.f; + active &= ctx.is_enabled(+BSDFFlags::DiffuseReflection, 1); - if (unlikely(!has_diffuse || dr::none_or(active))) + if (unlikely(dr::none_or(active))) return { 0.f, 0.f }; Float f_i = std::get<0>(fresnel(cos_theta_i, Float(m_eta))), @@ -349,12 +349,11 @@ class SmoothPlastic final : public BSDF { diff *= hemi_pdf * m_inv_eta_2 * (1.f - f_i) * (1.f - f_o); - Float prob_diffuse = 1.f; - if (ctx.is_enabled(BSDFFlags::DeltaReflection, 0)) { - Float prob_specular = f_i * m_specular_sampling_weight; - prob_diffuse = (1.f - f_i) * (1.f - m_specular_sampling_weight); - prob_diffuse = prob_diffuse / (prob_specular + prob_diffuse); - } + Float prob_specular = f_i * m_specular_sampling_weight; + Float prob_diffuse = (1.f - f_i) * (1.f - m_specular_sampling_weight); + prob_diffuse = prob_diffuse / (prob_specular + prob_diffuse); + + dr::masked(prob_diffuse, !ctx.is_enabled(+BSDFFlags::DeltaReflection, 0)) = 1.f; return { dr::select(active, depolarizer(diff), 0.f), dr::select(active, hemi_pdf * prob_diffuse, 0.f) }; diff --git a/src/bsdfs/pplastic.cpp b/src/bsdfs/pplastic.cpp index 321f99d076..e6176eb2fe 100644 --- a/src/bsdfs/pplastic.cpp +++ b/src/bsdfs/pplastic.cpp @@ -221,19 +221,20 @@ class PolarizedPlastic final : public BSDF { Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFSample, active); - bool has_specular = ctx.is_enabled(BSDFFlags::GlossyReflection, 0), - has_diffuse = ctx.is_enabled(BSDFFlags::DiffuseReflection, 1); + Mask has_specular = ctx.is_enabled(+BSDFFlags::GlossyReflection, 0), + has_diffuse = ctx.is_enabled(+BSDFFlags::DiffuseReflection, 1); Float cos_theta_i = Frame3f::cos_theta(si.wi); active &= cos_theta_i > 0.f; + active &= (has_specular || has_diffuse); BSDFSample3f bs = dr::zeros(); - if (unlikely((!has_specular && !has_diffuse) || dr::none_or(active))) + if (unlikely((dr::none_or(active)))) return { bs, 0.f }; Float prob_specular = m_specular_sampling_weight; - if (unlikely(has_specular != has_diffuse)) - prob_specular = has_specular ? 1.f : 0.f; + dr::masked(prob_specular, dr::neq(has_specular, has_diffuse)) + = dr::select(has_specular, 1.f, 0.f); Mask sample_specular = active && (sample1 < prob_specular), sample_diffuse = active && !sample_specular; @@ -266,14 +267,16 @@ class PolarizedPlastic final : public BSDF { const Vector3f &wo, Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); - bool has_specular = ctx.is_enabled(BSDFFlags::GlossyReflection, 0), - has_diffuse = ctx.is_enabled(BSDFFlags::DiffuseReflection, 1); + Mask has_specular = ctx.is_enabled(+BSDFFlags::GlossyReflection, 0), + has_diffuse = ctx.is_enabled(+BSDFFlags::DiffuseReflection, 1); Float cos_theta_i = Frame3f::cos_theta(si.wi), cos_theta_o = Frame3f::cos_theta(wo); active &= cos_theta_i > 0.f && cos_theta_o > 0.f; - if (unlikely((!has_specular && !has_diffuse) || dr::none_or(active))) + active &= (has_specular || has_diffuse); + + if (unlikely((dr::none_or(active)))) return 0.f; Spectrum result = 0.f; @@ -286,7 +289,7 @@ class PolarizedPlastic final : public BSDF { Vector3f wo_hat = ctx.mode == TransportMode::Radiance ? wo : si.wi, wi_hat = ctx.mode == TransportMode::Radiance ? si.wi : wo; - if (has_specular) { + if (dr::any_or(has_specular)) { MicrofacetDistribution distr(m_type, m_alpha_u, m_alpha_v, m_sample_visible); Vector3f H = dr::normalize(wo + si.wi); Float D = distr.eval(H); @@ -310,10 +313,10 @@ class PolarizedPlastic final : public BSDF { UnpolarizedSpectrum spec = m_specular_reflectance ? m_specular_reflectance->eval(si, active) : 1.f; - result += spec * F * value; + dr::masked(result, has_specular) += spec * F * value; } - if (has_diffuse) { + if (dr::any_or(has_diffuse)) { /* Diffuse scattering is modeled a a sequence of events: 1) Specular refraction inside 2) Subsurface scattering @@ -347,10 +350,10 @@ class PolarizedPlastic final : public BSDF { -wo_hat, s_axis_in, mueller::stokes_basis(-wo_hat), wi_hat, s_axis_out, mueller::stokes_basis(wi_hat)); - result += diff * dr::InvPi * cos_theta_o; + dr::masked(result, has_diffuse) += diff * dr::InvPi * cos_theta_o; } } else { - if (has_specular) { + if (dr::any_or(has_specular)) { MicrofacetDistribution distr(m_type, m_alpha_u, m_alpha_v, m_sample_visible); Vector3f H = dr::normalize(wo + si.wi); Float D = distr.eval(H); @@ -361,10 +364,10 @@ class PolarizedPlastic final : public BSDF { UnpolarizedSpectrum spec = m_specular_reflectance ? m_specular_reflectance->eval(si, active) : 1.f; - result += spec * F * value; + dr::masked(result, has_specular) += spec * F * value; } - if (has_diffuse) { + if (dr::any_or(has_diffuse)) { UnpolarizedSpectrum diff = m_diffuse_reflectance->eval(si, active); /* Diffuse scattering is modeled a a sequence of events: 1) Specular refraction inside @@ -374,7 +377,7 @@ class PolarizedPlastic final : public BSDF { UnpolarizedSpectrum r_i = std::get<0>(fresnel(cos_theta_i, m_eta)), r_o = std::get<0>(fresnel(cos_theta_o, m_eta)); diff = (1.f - r_o) * diff * (1.f - r_i); - result += diff * dr::InvPi * cos_theta_o; + dr::masked(result, has_diffuse) += diff * dr::InvPi * cos_theta_o; } } @@ -385,21 +388,23 @@ class PolarizedPlastic final : public BSDF { const Vector3f &wo, Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); - bool has_specular = ctx.is_enabled(BSDFFlags::GlossyReflection, 0), - has_diffuse = ctx.is_enabled(BSDFFlags::DiffuseReflection, 1); + Mask has_specular = ctx.is_enabled(+BSDFFlags::GlossyReflection, 0), + has_diffuse = ctx.is_enabled(+BSDFFlags::DiffuseReflection, 1); Float cos_theta_i = Frame3f::cos_theta(si.wi), cos_theta_o = Frame3f::cos_theta(wo); active &= cos_theta_i > 0.f && cos_theta_o > 0.f; + active &= (has_specular || has_diffuse); - if (unlikely((!has_specular && !has_diffuse) || dr::none_or(active))) + if (unlikely((dr::none_or(active)))) return 0.f; Float prob_specular = m_specular_sampling_weight, prob_diffuse = 1.f - prob_specular; - if (unlikely(has_specular != has_diffuse)) - prob_specular = has_specular ? 1.f : 0.f; + + dr::masked(prob_specular, dr::neq(has_specular, has_diffuse)) = + dr::select(has_specular, 1.f, 0.f); // Specular component Vector3f H = dr::normalize(wo + si.wi); diff --git a/src/bsdfs/roughconductor.cpp b/src/bsdfs/roughconductor.cpp index 77df7e5f56..a5102aab1b 100755 --- a/src/bsdfs/roughconductor.cpp +++ b/src/bsdfs/roughconductor.cpp @@ -233,8 +233,9 @@ class RoughConductor final : public BSDF { BSDFSample3f bs = dr::zeros(); Float cos_theta_i = Frame3f::cos_theta(si.wi); active &= cos_theta_i > 0.f; + active &= ctx.is_enabled(+BSDFFlags::GlossyReflection); - if (unlikely(!ctx.is_enabled(BSDFFlags::GlossyReflection) || dr::none_or(active))) + if (unlikely(dr::none_or(active))) return { bs, 0.f }; /* Construct a microfacet distribution matching the @@ -312,8 +313,9 @@ class RoughConductor final : public BSDF { cos_theta_o = Frame3f::cos_theta(wo); active &= cos_theta_i > 0.f && cos_theta_o > 0.f; + active &= ctx.is_enabled(+BSDFFlags::GlossyReflection); - if (unlikely(!ctx.is_enabled(BSDFFlags::GlossyReflection) || dr::none_or(active))) + if (unlikely(dr::none_or(active))) return 0.f; // Calculate the half-direction vector @@ -390,8 +392,9 @@ class RoughConductor final : public BSDF { density computation as well. */ active &= cos_theta_i > 0.f && cos_theta_o > 0.f && dr::dot(si.wi, m) > 0.f && dr::dot(wo, m) > 0.f; + active &= ctx.is_enabled(+BSDFFlags::GlossyReflection); - if (unlikely(!ctx.is_enabled(BSDFFlags::GlossyReflection) || dr::none_or(active))) + if (unlikely(dr::none_or(active))) return 0.f; /* Construct a microfacet distribution matching the @@ -429,8 +432,9 @@ class RoughConductor final : public BSDF { density computation as well. */ active &= cos_theta_i > 0.f && cos_theta_o > 0.f && dr::dot(si.wi, H) > 0.f && dr::dot(wo, H) > 0.f; + active &= ctx.is_enabled(+BSDFFlags::GlossyReflection); - if (unlikely(!ctx.is_enabled(BSDFFlags::GlossyReflection) || dr::none_or(active))) + if (unlikely(dr::none_or(active))) return { 0.f, 0.f }; /* Construct a microfacet distribution matching the diff --git a/src/bsdfs/roughdielectric.cpp b/src/bsdfs/roughdielectric.cpp index cb2d825027..8bf7cc8705 100644 --- a/src/bsdfs/roughdielectric.cpp +++ b/src/bsdfs/roughdielectric.cpp @@ -245,8 +245,8 @@ class RoughDielectric final : public BSDF { MI_MASKED_FUNCTION(ProfilerPhase::BSDFSample, active); // Determine the type of interaction - bool has_reflection = ctx.is_enabled(BSDFFlags::GlossyReflection, 0), - has_transmission = ctx.is_enabled(BSDFFlags::GlossyTransmission, 1); + Mask has_reflection = ctx.is_enabled(+BSDFFlags::GlossyReflection, 0), + has_transmission = ctx.is_enabled(+BSDFFlags::GlossyTransmission, 1); BSDFSample3f bs = dr::zeros(); @@ -254,6 +254,10 @@ class RoughDielectric final : public BSDF { // Ignore perfectly grazing configurations active &= dr::neq(cos_theta_i, 0.f); + active &= has_reflection | has_transmission; + + if (unlikely(dr::none_or(active))) + return { bs, 0.f }; /* Construct the microfacet distribution matching the roughness values at the current surface position. */ MicrofacetDistribution distr(m_type, @@ -278,30 +282,30 @@ class RoughDielectric final : public BSDF { fresnel(dr::dot(si.wi, m), m_eta); // Select the lobe to be sampled - UnpolarizedSpectrum weight; - Mask selected_r, selected_t; - if (likely(has_reflection && has_transmission)) { - selected_r = sample1 <= F && active; - weight = 1.f; - /* For differentiable variants, lobe choice has to be detached to avoid bias. - Sampling weights should be computed accordingly. */ - if constexpr (dr::is_diff_v) { - if (dr::grad_enabled(F)) { - weight = dr::select(selected_r, F / dr::detach(F), (1 - F) / (1.f - dr::detach(F))); - } - } - bs.pdf *= dr::detach(dr::select(selected_r, F, 1.f - F)); - } else { - if (has_reflection || has_transmission) { - selected_r = Mask(has_reflection) && active; - weight = has_reflection ? F : (1.f - F); - } else { - return { bs, 0.f }; + Mask selected_r = (sample1 <= F || !has_transmission) && has_reflection && active; + Mask selected_t = !selected_r && active; + + bs.pdf *= dr::select( + has_reflection && has_transmission, + dr::detach(dr::select(selected_r, F, 1.f - F)), + 1.f + ); + + UnpolarizedSpectrum weight = dr::select( + has_reflection && has_transmission, + 1.f, + dr::select(has_reflection, F, (1.f - F)) + ); + + /* For differentiable variants, lobe choice has to be detached to avoid + bias. Sampling weights should be computed accordingly. */ + if constexpr (dr::is_diff_v) { + if (dr::grad_enabled(F)) { + dr::masked(weight, has_reflection && has_transmission) + = dr::select(selected_r, F / dr::detach(F), (1 - F) / (1.f - dr::detach(F))); } } - selected_t = !selected_r && active; - bs.eta = dr::select(selected_r, Float(1.f), eta_it); bs.sampled_component = dr::select(selected_r, UInt32(0), UInt32(1)); bs.sampled_type = dr::select(selected_r, @@ -364,8 +368,8 @@ class RoughDielectric final : public BSDF { active &= dr::neq(cos_theta_i, 0.f); // Determine the type of interaction - bool has_reflection = ctx.is_enabled(BSDFFlags::GlossyReflection, 0), - has_transmission = ctx.is_enabled(BSDFFlags::GlossyTransmission, 1); + Mask has_reflection = ctx.is_enabled(+BSDFFlags::GlossyReflection, 0), + has_transmission = ctx.is_enabled(+BSDFFlags::GlossyTransmission, 1); Mask reflect = cos_theta_i * cos_theta_o > 0.f; @@ -397,8 +401,8 @@ class RoughDielectric final : public BSDF { UnpolarizedSpectrum result(0.f); - Mask eval_r = Mask(has_reflection) && reflect && active, - eval_t = Mask(has_transmission) && !reflect && active; + Mask eval_r = has_reflection && reflect && active, + eval_t = has_transmission && !reflect && active; if (dr::any_or(eval_r)) { UnpolarizedSpectrum value = F * D * G / (4.f * dr::abs(cos_theta_i)); @@ -440,12 +444,12 @@ class RoughDielectric final : public BSDF { active &= dr::neq(cos_theta_i, 0.f); // Determine the type of interaction - bool has_reflection = ctx.is_enabled(BSDFFlags::GlossyReflection, 0), - has_transmission = ctx.is_enabled(BSDFFlags::GlossyTransmission, 1); + Mask has_reflection = ctx.is_enabled(+BSDFFlags::GlossyReflection, 0), + has_transmission = ctx.is_enabled(+BSDFFlags::GlossyTransmission, 1); Mask reflect = cos_theta_i * cos_theta_o > 0.f; - active &= (Mask(has_reflection) && reflect) || - (Mask(has_transmission) && !reflect); + active &= (has_reflection && reflect) || + (has_transmission && !reflect); // Determine the relative index of refraction Float eta = dr::select(cos_theta_i > 0.f, m_eta, m_inv_eta); @@ -486,9 +490,10 @@ class RoughDielectric final : public BSDF { // Evaluate the microfacet model sampling density function Float prob = sample_distr.pdf(dr::mulsign(si.wi, Frame3f::cos_theta(si.wi)), m); - if (likely(has_transmission && has_reflection)) { + if (dr::any_or(has_transmission && has_reflection)) { Float F = std::get<0>(fresnel(dr::dot(si.wi, m), m_eta)); - prob *= dr::select(reflect, F, 1.f - F); + dr::masked(prob, has_transmission && has_reflection) + *= dr::select(reflect, F, 1.f - F); } return dr::select(active, prob * dr::abs(dwh_dwo), 0.f); @@ -507,13 +512,13 @@ class RoughDielectric final : public BSDF { active &= dr::neq(cos_theta_i, 0.f); // Determine the type of interaction - bool has_reflection = ctx.is_enabled(BSDFFlags::GlossyReflection, 0), - has_transmission = ctx.is_enabled(BSDFFlags::GlossyTransmission, 1); + Mask has_reflection = ctx.is_enabled(+BSDFFlags::GlossyReflection, 0), + has_transmission = ctx.is_enabled(+BSDFFlags::GlossyTransmission, 1); Mask reflect = cos_theta_i * cos_theta_o > 0.f; - active &= (Mask(has_reflection) && reflect) || - (Mask(has_transmission) && !reflect); + active &= (has_reflection && reflect) || + (has_transmission && !reflect); // Determine the relative index of refraction Float eta = dr::select(cos_theta_i > 0.f, m_eta, m_inv_eta), @@ -553,8 +558,8 @@ class RoughDielectric final : public BSDF { UnpolarizedSpectrum result(0.f); - Mask eval_r = Mask(has_reflection) && reflect && active, - eval_t = Mask(has_transmission) && !reflect && active; + Mask eval_r = has_reflection && reflect && active, + eval_t = has_transmission && !reflect && active; if (dr::any_or(eval_r)) { UnpolarizedSpectrum value = F * D * G / (4.f * dr::abs(cos_theta_i)); @@ -591,8 +596,8 @@ class RoughDielectric final : public BSDF { // Evaluate the microfacet model sampling density function Float pdf = distr.pdf(dr::mulsign(si.wi, cos_theta_i), m); - if (likely(has_transmission && has_reflection)) - pdf *= dr::select(reflect, F, 1.f - F); + dr::masked(pdf, has_transmission && has_reflection) + *= dr::select(reflect, F, 1.f - F); // Jacobian of the half-direction mapping Float dwh_dwo = dr::select(reflect, dr::rcp(4.f * dot_wo_m), diff --git a/src/bsdfs/roughplastic.cpp b/src/bsdfs/roughplastic.cpp index bd6ea62354..f6d38492f1 100644 --- a/src/bsdfs/roughplastic.cpp +++ b/src/bsdfs/roughplastic.cpp @@ -261,15 +261,17 @@ class RoughPlastic final : public BSDF { Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFSample, active); - bool has_specular = ctx.is_enabled(BSDFFlags::GlossyReflection, 0), - has_diffuse = ctx.is_enabled(BSDFFlags::DiffuseReflection, 1); + Mask has_specular = ctx.is_enabled(+BSDFFlags::GlossyReflection, 0), + has_diffuse = ctx.is_enabled(+BSDFFlags::DiffuseReflection, 1); Float cos_theta_i = Frame3f::cos_theta(si.wi); active &= cos_theta_i > 0.f; + active &= has_specular || has_diffuse; BSDFSample3f bs = dr::zeros(); Spectrum result(0.f); - if (unlikely((!has_specular && !has_diffuse) || dr::none_or(active))) + + if (unlikely(dr::none_or(active))) return { bs, result }; Float t_i = lerp_gather(m_external_transmittance, cos_theta_i, @@ -279,10 +281,11 @@ class RoughPlastic final : public BSDF { Float prob_specular = (1.f - t_i) * m_specular_sampling_weight, prob_diffuse = t_i * (1.f - m_specular_sampling_weight); - if (unlikely(has_specular != has_diffuse)) - prob_specular = has_specular ? 1.f : 0.f; - else - prob_specular = prob_specular / (prob_specular + prob_diffuse); + prob_specular = dr::select( + dr::neq(has_specular, has_diffuse), + dr::select(has_specular, 1.f, 0.f), + prob_specular / (prob_specular + prob_diffuse) + ); prob_diffuse = 1.f - prob_specular; Mask sample_specular = active && (sample1 < prob_specular), @@ -316,19 +319,20 @@ class RoughPlastic final : public BSDF { const Vector3f &wo, Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); - bool has_specular = ctx.is_enabled(BSDFFlags::GlossyReflection, 0), - has_diffuse = ctx.is_enabled(BSDFFlags::DiffuseReflection, 1); + Mask has_specular = ctx.is_enabled(+BSDFFlags::GlossyReflection, 0), + has_diffuse = ctx.is_enabled(+BSDFFlags::DiffuseReflection, 1); Float cos_theta_i = Frame3f::cos_theta(si.wi), cos_theta_o = Frame3f::cos_theta(wo); active &= cos_theta_i > 0.f && cos_theta_o > 0.f; + active &= has_specular || has_diffuse; - if (unlikely((!has_specular && !has_diffuse) || dr::none_or(active))) + if (unlikely(dr::none_or(active))) return 0.f; UnpolarizedSpectrum value(0.f); - if (has_specular) { + if (dr::any_or(has_specular)) { MicrofacetDistribution distr(m_type, m_alpha, m_sample_visible); // Calculate the reflection half-vector @@ -344,13 +348,13 @@ class RoughPlastic final : public BSDF { Float G = distr.G(si.wi, wo, H); // Calculate the specular reflection component - value = F * D * G / (4.f * cos_theta_i); + dr::masked(value, has_specular) = F * D * G / (4.f * cos_theta_i); if (m_specular_reflectance) - value *= m_specular_reflectance->eval(si, active); + dr::masked(value, has_specular) *= m_specular_reflectance->eval(si, active); } - if (has_diffuse) { + if (dr::any_or(has_diffuse)) { Float t_i = lerp_gather(m_external_transmittance, cos_theta_i, MI_ROUGH_TRANSMITTANCE_RES, active), t_o = lerp_gather(m_external_transmittance, cos_theta_o, @@ -360,7 +364,7 @@ class RoughPlastic final : public BSDF { diff /= 1.f - (m_nonlinear ? (diff * m_internal_reflectance) : UnpolarizedSpectrum(m_internal_reflectance)); - value += diff * (dr::InvPi * m_inv_eta_2 * cos_theta_o * t_i * t_o); + dr::masked(value, has_diffuse) += diff * (dr::InvPi * m_inv_eta_2 * cos_theta_o * t_i * t_o); } return depolarizer(value) & active; @@ -382,15 +386,16 @@ class RoughPlastic final : public BSDF { const Vector3f &wo, Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); - bool has_specular = ctx.is_enabled(BSDFFlags::GlossyReflection, 0), - has_diffuse = ctx.is_enabled(BSDFFlags::DiffuseReflection, 1); + Mask has_specular = ctx.is_enabled(+BSDFFlags::GlossyReflection, 0), + has_diffuse = ctx.is_enabled(+BSDFFlags::DiffuseReflection, 1); Float cos_theta_i = Frame3f::cos_theta(si.wi), cos_theta_o = Frame3f::cos_theta(wo); active &= cos_theta_i > 0.f && cos_theta_o > 0.f; + active &= has_specular || has_diffuse; - if (unlikely((!has_specular && !has_diffuse) || dr::none_or(active))) + if (unlikely(dr::none_or(active))) return 0.f; Float t_i = lerp_gather(m_external_transmittance, cos_theta_i, @@ -400,10 +405,11 @@ class RoughPlastic final : public BSDF { Float prob_specular = (1.f - t_i) * m_specular_sampling_weight, prob_diffuse = t_i * (1.f - m_specular_sampling_weight); - if (unlikely(has_specular != has_diffuse)) - prob_specular = has_specular ? 1.f : 0.f; - else - prob_specular = prob_specular / (prob_specular + prob_diffuse); + prob_specular = dr::select( + dr::neq(has_specular, has_diffuse), + dr::select(has_specular, 1.f, 0.f), + prob_specular / (prob_specular + prob_diffuse) + ); prob_diffuse = 1.f - prob_specular; Vector3f H = dr::normalize(wo + si.wi); @@ -428,15 +434,16 @@ class RoughPlastic final : public BSDF { Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFEvaluate, active); - bool has_specular = ctx.is_enabled(BSDFFlags::GlossyReflection, 0), - has_diffuse = ctx.is_enabled(BSDFFlags::DiffuseReflection, 1); + Mask has_specular = ctx.is_enabled(+BSDFFlags::GlossyReflection, 0), + has_diffuse = ctx.is_enabled(+BSDFFlags::DiffuseReflection, 1); Float cos_theta_i = Frame3f::cos_theta(si.wi), cos_theta_o = Frame3f::cos_theta(wo); active &= cos_theta_i > 0.f && cos_theta_o > 0.f; + active &= has_specular || has_diffuse; - if (unlikely((!has_specular && !has_diffuse) || dr::none_or(active))) + if (unlikely(dr::none_or(active))) return { 0.f, 0.f }; Float t_i = lerp_gather(m_external_transmittance, cos_theta_i, @@ -446,10 +453,11 @@ class RoughPlastic final : public BSDF { Float prob_specular = (1.f - t_i) * m_specular_sampling_weight, prob_diffuse = t_i * (1.f - m_specular_sampling_weight); - if (unlikely(has_specular != has_diffuse)) - prob_specular = has_specular ? 1.f : 0.f; - else - prob_specular = prob_specular / (prob_specular + prob_diffuse); + prob_specular = dr::select( + dr::neq(has_specular, has_diffuse), + dr::select(has_specular, 1.f, 0.f), + prob_specular / (prob_specular + prob_diffuse) + ); prob_diffuse = 1.f - prob_specular; // Calculate the reflection half-vector @@ -473,7 +481,7 @@ class RoughPlastic final : public BSDF { pdf += prob_diffuse * warp::square_to_cosine_hemisphere_pdf(wo); UnpolarizedSpectrum value(0.f); - if (has_specular) { + if (dr::any_or(has_specular)) { // Fresnel term Float F = std::get<0>(fresnel(dr::dot(si.wi, H), Float(m_eta))); @@ -481,13 +489,13 @@ class RoughPlastic final : public BSDF { Float G = distr.smith_g1(wo, H) * smith_g1_wi; // Calculate the specular reflection component - value = F * D * G / (4.f * cos_theta_i); + dr::masked(value, has_specular) = F * D * G / (4.f * cos_theta_i); if (m_specular_reflectance) - value *= m_specular_reflectance->eval(si, active); + dr::masked(value, has_specular) *= m_specular_reflectance->eval(si, active); } - if (has_diffuse) { + if (dr::any_or(has_diffuse)) { Float t_o = lerp_gather(m_external_transmittance, cos_theta_o, MI_ROUGH_TRANSMITTANCE_RES, active); @@ -495,7 +503,7 @@ class RoughPlastic final : public BSDF { diff /= 1.f - (m_nonlinear ? (diff * m_internal_reflectance) : UnpolarizedSpectrum(m_internal_reflectance)); - value += diff * (dr::InvPi * m_inv_eta_2 * cos_theta_o * t_i * t_o); + dr::masked(value, has_diffuse) += diff * (dr::InvPi * m_inv_eta_2 * cos_theta_o * t_i * t_o); } return { depolarizer(value) & active, pdf }; diff --git a/src/bsdfs/tests/test_dielectric.py b/src/bsdfs/tests/test_dielectric.py index 9c645f264a..85856e60c2 100644 --- a/src/bsdfs/tests/test_dielectric.py +++ b/src/bsdfs/tests/test_dielectric.py @@ -100,6 +100,8 @@ def test04_sample_specific_component(variant_scalar_rgb): ctx = mi.BSDFContext(mi.TransportMode.Importance if i == 0 else mi.TransportMode.Radiance) + print(i, sample, sel_type) + # Sample reflection if sel_type == 0: ctx.type_mask = mi.BSDFFlags.DeltaReflection @@ -172,7 +174,29 @@ def test06_attached_sampling(variants_all_ad_rgb): bs, weight = bsdf.sample(mi.BSDFContext(), si, 0, [0, 0]) assert dr.grad_enabled(weight) - + dr.forward(angle) assert dr.allclose(dr.grad(weight), 0.008912204764783382) - \ No newline at end of file + + +def test07_vectorized_context(variants_vec_rgb): + bsdf = mi.load_dict({'type': 'dielectric'}) + + angle = mi.Float(10 * dr.pi / 180) + si = mi.SurfaceInteraction3f() + si.p = dr.zeros(mi.Point3f, 4) + si.n = [dr.sin(angle), 0, dr.cos(angle)] + si.sh_frame = mi.Frame3f(si.n) + si.wi = si.sh_frame.to_local([0, 0, 1]) + + ctx = mi.BSDFContext(mi.TransportMode.Radiance) + ctx.type_mask = mi.UInt32([ + +mi.BSDFFlags.DeltaReflection, + +mi.BSDFFlags.DeltaReflection, + +mi.BSDFFlags.DeltaTransmission, + +mi.BSDFFlags.DeltaTransmission + ]) + + bs, weight = bsdf.sample(ctx, si, [0, 0, 0, 0], [0, 0]) + + assert dr.allclose(bs.sampled_type, ctx.type_mask) diff --git a/src/bsdfs/thindielectric.cpp b/src/bsdfs/thindielectric.cpp index cf99568d3e..a2b0701d8a 100644 --- a/src/bsdfs/thindielectric.cpp +++ b/src/bsdfs/thindielectric.cpp @@ -144,8 +144,15 @@ class ThinDielectric final : public BSDF { Mask active) const override { MI_MASKED_FUNCTION(ProfilerPhase::BSDFSample, active); - bool has_reflection = ctx.is_enabled(BSDFFlags::DeltaReflection, 0), - has_transmission = ctx.is_enabled(BSDFFlags::Null, 1); + Mask has_reflection = ctx.is_enabled(+BSDFFlags::DeltaReflection, 0), + has_transmission = ctx.is_enabled(+BSDFFlags::Null, 1); + + active &= has_reflection | has_transmission; + + BSDFSample3f bs = dr::zeros(); + + if (unlikely(dr::none_or(active))) + return { bs, 0.f }; Float r = std::get<0>(fresnel(dr::abs(Frame3f::cos_theta(si.wi)), m_eta)); @@ -155,22 +162,19 @@ class ThinDielectric final : public BSDF { Float t = 1.f - r; // Select the lobe to be sampled - BSDFSample3f bs = dr::zeros(); - UnpolarizedSpectrum weight; - Mask selected_r; - if (likely(has_reflection && has_transmission)) { - selected_r = sample1 <= r && active; - weight = 1.f; - bs.pdf = dr::select(selected_r, r, t); - } else { - if (has_reflection || has_transmission) { - selected_r = Mask(has_reflection) && active; - weight = has_reflection ? r : t; - bs.pdf = 1.f; - } else { - return { bs, 0.f }; - } - } + Mask selected_r = (sample1 <= r || !has_transmission) && has_reflection && active; + + bs.pdf = dr::select( + has_reflection && has_transmission, + dr::detach(dr::select(selected_r, r, t)), + 1.f + ); + + UnpolarizedSpectrum weight = dr::select( + has_reflection && has_transmission, + 1.f, + dr::select(has_reflection, r, t) + ); bs.wo = dr::select(selected_r, reflect(si.wi), -si.wi); bs.eta = 1.f; diff --git a/src/bsdfs/twosided.cpp b/src/bsdfs/twosided.cpp index 106466e1fc..84b7baccaa 100644 --- a/src/bsdfs/twosided.cpp +++ b/src/bsdfs/twosided.cpp @@ -134,8 +134,8 @@ class TwoSidedBRDF final : public BSDF { m_brdf[0]->sample(ctx, si, sample1, sample2, front_side); if (dr::any_or(back_side)) { - if (ctx.component != (uint32_t) -1) - ctx.component -= (uint32_t) m_brdf[0]->component_count(); + Mask sample_all = dr::eq(ctx.component, (uint32_t) -1); + dr::masked(ctx.component, !sample_all) -= (uint32_t) m_brdf[0]->component_count(); si.wi.z() *= -1.f; dr::masked(result, back_side) = @@ -168,8 +168,8 @@ class TwoSidedBRDF final : public BSDF { result = m_brdf[0]->eval(ctx, si, wo, front_side); if (dr::any_or(back_side)) { - if (ctx.component != (uint32_t) -1) - ctx.component -= (uint32_t) m_brdf[0]->component_count(); + Mask sample_all = dr::eq(ctx.component, (uint32_t) -1); + dr::masked(ctx.component, !sample_all) -= (uint32_t) m_brdf[0]->component_count(); si.wi.z() *= -1.f; wo.z() *= -1.f; @@ -203,8 +203,8 @@ class TwoSidedBRDF final : public BSDF { result = m_brdf[0]->pdf(ctx, si, wo, front_side); if (dr::any_or(back_side)) { - if (ctx.component != (uint32_t) -1) - ctx.component -= (uint32_t) m_brdf[0]->component_count(); + Mask sample_all = dr::eq(ctx.component, (uint32_t) -1); + dr::masked(ctx.component, !sample_all) -= (uint32_t) m_brdf[0]->component_count(); si.wi.z() *= -1.f; wo.z() *= -1.f; @@ -241,8 +241,8 @@ class TwoSidedBRDF final : public BSDF { std::tie(value, pdf) = m_brdf[0]->eval_pdf(ctx, si, wo, front_side); if (dr::any_or(back_side)) { - if (ctx.component != (uint32_t) -1) - ctx.component -= (uint32_t) m_brdf[0]->component_count(); + Mask sample_all = dr::eq(ctx.component, (uint32_t) -1); + dr::masked(ctx.component, !sample_all) -= (uint32_t) m_brdf[0]->component_count(); si.wi.z() *= -1.f; wo.z() *= -1.f; diff --git a/src/python/main_v.cpp b/src/python/main_v.cpp index c55ab57c97..239e917469 100644 --- a/src/python/main_v.cpp +++ b/src/python/main_v.cpp @@ -83,6 +83,7 @@ MI_PY_DECLARE(quad); // render MI_PY_DECLARE(BSDFSample); +MI_PY_DECLARE(BSDFContext); MI_PY_DECLARE(BSDF); MI_PY_DECLARE(Emitter); MI_PY_DECLARE(Endpoint); @@ -187,6 +188,7 @@ PYBIND11_MODULE(MODULE_NAME, m) { MI_PY_IMPORT(PositionSample); MI_PY_IMPORT(DirectionSample); MI_PY_IMPORT(BSDFSample); + MI_PY_IMPORT(BSDFContext); MI_PY_IMPORT(BSDF); MI_PY_IMPORT(Film); MI_PY_IMPORT(fresnel); diff --git a/src/render/bsdf.cpp b/src/render/bsdf.cpp index a544cdfbef..c72c2fc899 100644 --- a/src/render/bsdf.cpp +++ b/src/render/bsdf.cpp @@ -40,120 +40,6 @@ MI_VARIANT Spectrum BSDF::eval_diffuse_reflectance( return eval(ctx, si, wo, active) * dr::Pi; } -template -std::string type_mask_to_string(Index type_mask) { - std::ostringstream oss; - oss << "{ "; - -#define is_set(flag) has_flag(type_mask, flag) - if (is_set(BSDFFlags::All)) { - oss << "all "; - type_mask = type_mask & ~BSDFFlags::All; - } - if (is_set(BSDFFlags::Reflection)) { - oss << "reflection "; - type_mask = type_mask & ~BSDFFlags::Reflection; - } - if (is_set(BSDFFlags::Transmission)) { - oss << "transmission "; - type_mask = type_mask & ~BSDFFlags::Transmission; - } - if (is_set(BSDFFlags::Smooth)) { - oss << "smooth "; - type_mask = type_mask & ~BSDFFlags::Smooth; - } - if (is_set(BSDFFlags::Diffuse)) { - oss << "diffuse "; - type_mask = type_mask & ~BSDFFlags::Diffuse; - } - if (is_set(BSDFFlags::Glossy)) { - oss << "glossy "; - type_mask = type_mask & ~BSDFFlags::Glossy; - } - if (is_set(BSDFFlags::Delta)) { - oss << "delta"; - type_mask = type_mask & ~BSDFFlags::Delta; - } - if (is_set(BSDFFlags::Delta1D)) { - oss << "delta_1d "; - type_mask = type_mask & ~BSDFFlags::Delta1D; - } - if (is_set(BSDFFlags::DiffuseReflection)) { - oss << "diffuse_reflection "; - type_mask = type_mask & ~BSDFFlags::DiffuseReflection; - } - if (is_set(BSDFFlags::DiffuseTransmission)) { - oss << "diffuse_transmission "; - type_mask = type_mask & ~BSDFFlags::DiffuseTransmission; - } - if (is_set(BSDFFlags::GlossyReflection)) { - oss << "glossy_reflection "; - type_mask = type_mask & ~BSDFFlags::GlossyReflection; - } - if (is_set(BSDFFlags::GlossyTransmission)) { - oss << "glossy_transmission "; - type_mask = type_mask & ~BSDFFlags::GlossyTransmission; - } - if (is_set(BSDFFlags::DeltaReflection)) { - oss << "delta_reflection "; - type_mask = type_mask & ~BSDFFlags::DeltaReflection; - } - if (is_set(BSDFFlags::DeltaTransmission)) { - oss << "delta_transmission "; - type_mask = type_mask & ~BSDFFlags::DeltaTransmission; - } - if (is_set(BSDFFlags::Delta1DReflection)) { - oss << "delta_1d_reflection "; - type_mask = type_mask & ~BSDFFlags::Delta1DReflection; - } - if (is_set(BSDFFlags::Delta1DTransmission)) { - oss << "delta_1d_transmission "; - type_mask = type_mask & ~BSDFFlags::Delta1DTransmission; - } - if (is_set(BSDFFlags::Null)) { - oss << "null "; - type_mask = type_mask & ~BSDFFlags::Null; - } - if (is_set(BSDFFlags::Anisotropic)) { - oss << "anisotropic "; - type_mask = type_mask & ~BSDFFlags::Anisotropic; - } - if (is_set(BSDFFlags::FrontSide)) { - oss << "front_side "; - type_mask = type_mask & ~BSDFFlags::FrontSide; - } - if (is_set(BSDFFlags::BackSide)) { - oss << "back_side "; - type_mask = type_mask & ~BSDFFlags::BackSide; - } - if (is_set(BSDFFlags::SpatiallyVarying)) { - oss << "spatially_varying "; - type_mask = type_mask & ~BSDFFlags::SpatiallyVarying; - } - if (is_set(BSDFFlags::NonSymmetric)) { - oss << "non_symmetric "; - type_mask = type_mask & ~BSDFFlags::NonSymmetric; - } -#undef is_set - - Assert(type_mask == 0); - oss << "}"; - return oss.str(); -} - -std::ostream &operator<<(std::ostream &os, const BSDFContext& ctx) { - os << "BSDFContext[" << std::endl - << " mode = " << ctx.mode << "," << std::endl - << " type_mask = " << type_mask_to_string(ctx.type_mask) << "," << std::endl - << " component = "; - if (ctx.component == (uint32_t) -1) - os << "all"; - else - os << ctx.component; - os << std::endl << "]"; - return os; -} - std::ostream &operator<<(std::ostream &os, const TransportMode &mode) { switch (mode) { case TransportMode::Radiance: os << "radiance"; break; diff --git a/src/render/python/bsdf.cpp b/src/render/python/bsdf.cpp index 5c2097e157..9d81a541e4 100644 --- a/src/render/python/bsdf.cpp +++ b/src/render/python/bsdf.cpp @@ -32,16 +32,4 @@ MI_PY_EXPORT(BSDFContext) { .def_value(BSDFFlags, All); MI_PY_DECLARE_ENUM_OPERATORS(BSDFFlags, e) - - py::class_(m, "BSDFContext", D(BSDFContext)) - .def(py::init(), - "mode"_a = TransportMode::Radiance, D(BSDFContext, BSDFContext)) - .def(py::init(), - "mode"_a, "type_mask"_a, "component"_a, D(BSDFContext, BSDFContext, 2)) - .def_method(BSDFContext, reverse) - .def_method(BSDFContext, is_enabled, "type"_a, "component"_a = 0) - .def_field(BSDFContext, mode, D(BSDFContext, mode)) - .def_field(BSDFContext, type_mask, D(BSDFContext, type_mask)) - .def_field(BSDFContext, component, D(BSDFContext, component)) - .def_repr(BSDFContext); } diff --git a/src/render/python/bsdf_v.cpp b/src/render/python/bsdf_v.cpp index 7f0edc033c..586a1c6964 100644 --- a/src/render/python/bsdf_v.cpp +++ b/src/render/python/bsdf_v.cpp @@ -23,6 +23,24 @@ MI_PY_EXPORT(BSDFSample) { MI_PY_DRJIT_STRUCT(bs, BSDFSample3f, wo, pdf, eta, sampled_type, sampled_component); } +MI_PY_EXPORT(BSDFContext) { + MI_PY_IMPORT_TYPES() + + auto bc = py::class_(m, "BSDFContext", D(BSDFContext)) + .def(py::init(), + "mode"_a = TransportMode::Radiance, D(BSDFContext, BSDFContext)) + .def(py::init(), + "mode"_a, "type_mask"_a, "component"_a, D(BSDFContext, BSDFContext, 2)) + .def_method(BSDFContext, reverse) + .def_method(BSDFContext, is_enabled, "type"_a, "component"_a = 0) + .def_field(BSDFContext, mode, D(BSDFContext, mode)) + .def_field(BSDFContext, type_mask, D(BSDFContext, type_mask)) + .def_field(BSDFContext, component, D(BSDFContext, component)) + .def_repr(BSDFContext); + + MI_PY_DRJIT_STRUCT(bc, BSDFContext, mode, type_mask, component); +} + /// Trampoline for derived types implemented in Python MI_VARIANT class PyBSDF : public BSDF { public: