diff --git a/src/libbsdl/bsdl.cmake b/src/libbsdl/bsdl.cmake index 74a247ce9a..3a6623928d 100644 --- a/src/libbsdl/bsdl.cmake +++ b/src/libbsdl/bsdl.cmake @@ -16,7 +16,8 @@ function(ADD_BSDL_LIBRARY NAME) add_executable (genluts ${CMAKE_CURRENT_SOURCE_DIR}/${bsdl_SUBDIR}/src/genluts.cpp) target_link_libraries(genluts PRIVATE BSDL_BOOTSTRAP Threads::Threads) file(MAKE_DIRECTORY ${BSDL_GEN_HEADERS}/BSDL/SPI) - add_custom_command(TARGET genluts POST_BUILD USES_TERMINAL COMMAND $ ${BSDL_GEN_HEADERS}/BSDL/SPI + file(MAKE_DIRECTORY ${BSDL_GEN_HEADERS}/BSDL/MTX) + add_custom_command(TARGET genluts POST_BUILD USES_TERMINAL COMMAND $ ${BSDL_GEN_HEADERS}/BSDL COMMENT "Generating BSDL lookup tables ...") if (DEFINED bsdl_SPECTRAL_COLOR_SPACES) diff --git a/src/libbsdl/include/BSDL/MTX/bsdf_conductor_decl.h b/src/libbsdl/include/BSDL/MTX/bsdf_conductor_decl.h index d99df19e11..45fbf78786 100644 --- a/src/libbsdl/include/BSDL/MTX/bsdf_conductor_decl.h +++ b/src/libbsdl/include/BSDL/MTX/bsdf_conductor_decl.h @@ -36,7 +36,9 @@ template struct ConductorLobe : public Lobe { // fresnel params Imath::C3f IOR; Imath::C3f extinction; - const char* distribution; + Stringhash distribution; + float thinfilm_thickness; + float thinfilm_ior; using lobe_type = ConductorLobe; }; template static typename LobeRegistry::Entry entry() @@ -48,7 +50,8 @@ template struct ConductorLobe : public Lobe { { R::param(&D::N), R::param(&D::U), R::param(&D::roughness_x), R::param(&D::roughness_y), R::param(&D::IOR), R::param(&D::extinction), R::param(&D::distribution), - R::close() } }; + R::param(&D::thinfilm_thickness, "thinfilm_thickness"), + R::param(&D::thinfilm_ior, "thinfilm_ior"), R::close() } }; } template diff --git a/src/libbsdl/include/BSDL/MTX/bsdf_dielectric_decl.h b/src/libbsdl/include/BSDL/MTX/bsdf_dielectric_decl.h new file mode 100644 index 0000000000..c42f6d677d --- /dev/null +++ b/src/libbsdl/include/BSDL/MTX/bsdf_dielectric_decl.h @@ -0,0 +1,210 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + + +#pragma once + +#include +#include +#include + +BSDL_ENTER_NAMESPACE + +namespace mtx { + +struct DielectricFresnel { + DielectricFresnel() = default; + BSDL_INLINE_METHOD DielectricFresnel(float _eta, bool backside); + BSDL_INLINE_METHOD Power eval(const float c) const; + static BSDL_INLINE_METHOD DielectricFresnel from_table_index(float tx, + bool backside); + BSDL_INLINE_METHOD float table_index() const; + + float eta; + + static constexpr float IOR_MIN = 1.001f; + static constexpr float IOR_MAX = 5.0f; +}; + +struct DielectricRefl { + // describe how tabulation should be done + static constexpr int Nc = 16; + static constexpr int Nr = 16; + static constexpr int Nf = 32; + + static constexpr float get_cosine(int i) + { + return std::max(float(i) * (1.0f / (Nc - 1)), 1e-6f); + } + + explicit BSDL_INLINE_METHOD + DielectricRefl(float cosNO, float roughness_index, float fresnel_index); + + BSDL_INLINE_METHOD + DielectricRefl(const GGXDist& dist, const DielectricFresnel& fresnel, + float cosNO, float roughness); + + DielectricRefl() = default; + + BSDL_INLINE_METHOD Sample eval(Imath::V3f wo, Imath::V3f wi) const; + BSDL_INLINE_METHOD Sample sample(Imath::V3f wo, float randu, float randv, + float randw) const; + + BSDL_INLINE_METHOD DielectricFresnel fresnel() const { return f; } + + struct Energy { + float data[Nf * Nr * Nc]; + }; + static constexpr const char* NS = "mtx"; + +protected: + GGXDist d; + DielectricFresnel f; + float E_ms; // No fresnel here +}; + +struct DielectricReflFront : public DielectricRefl { + DielectricReflFront() = default; + explicit BSDL_INLINE_METHOD DielectricReflFront(float cosNO, + float roughness_index, + float fresnel_index); + static BSDL_INLINE_METHOD Energy& get_energy(); + static const char* lut_header() + { + return "MTX/bsdf_dielectric_reflfront_luts.h"; + } + static const char* struct_name() { return "DielectricReflFront"; } +}; + +struct DielectricBoth { + static constexpr int Nc = 16; + static constexpr int Nr = 16; + static constexpr int Nf = 32; + + static constexpr float get_cosine(int i) + { + return std::max(float(i) * (1.0f / (Nc - 1)), 1e-6f); + } + + DielectricBoth() = default; + explicit BSDL_INLINE_METHOD DielectricBoth(float cosNO, + float roughness_index, + float fresnel_index, + bool backside); + BSDL_INLINE_METHOD + DielectricBoth(const GGXDist& dist, const DielectricFresnel& fresnel); + + BSDL_INLINE_METHOD DielectricFresnel fresnel() const { return f; } + + BSDL_INLINE_METHOD Sample eval(Imath::V3f wo, Imath::V3f wi) const; + BSDL_INLINE_METHOD Sample sample(Imath::V3f wo, float randu, float randv, + float randw) const; + + struct Energy { + float data[Nf * Nr * Nc]; + }; + static constexpr const char* NS = "mtx"; + +protected: + GGXDist d; + DielectricFresnel f; +}; + +struct DielectricBothFront : public DielectricBoth { + DielectricBothFront() = default; + explicit BSDL_INLINE_METHOD DielectricBothFront(float cosNO, + float roughness_index, + float fresnel_index); + static BSDL_INLINE_METHOD Energy& get_energy(); + static const char* lut_header() + { + return "MTX/bsdf_dielectric_bothfront_luts.h"; + } + static const char* struct_name() { return "DielectricBothFront"; } +}; + +struct DielectricBothBack : public DielectricBoth { + DielectricBothBack() = default; + explicit BSDL_INLINE_METHOD + DielectricBothBack(float cosNO, float roughness_index, float fresnel_index); + static BSDL_INLINE_METHOD Energy& get_energy(); + + static const char* lut_header() + { + return "MTX/bsdf_dielectric_bothback_luts.h"; + } + static const char* struct_name() { return "DielectricBothBack"; } +}; + +template struct DielectricLobe : public Lobe { + using Base = Lobe; + struct Data : public LayeredData { + Imath::V3f N; + Imath::V3f U; + Imath::C3f refl_tint; + Imath::C3f refr_tint; + float roughness_x; + float roughness_y; + float IOR; + Stringhash distribution; + float thinfilm_thickness; + float thinfilm_ior; + float dispersion; + Imath::C3f absorption; + using lobe_type = DielectricLobe; + }; + template static typename LobeRegistry::Entry entry() + { + static_assert( + std::is_base_of::value); // Make no other assumptions + using R = LobeRegistry; + return { name(), + { R::param(&D::N), R::param(&D::U), R::param(&D::refl_tint), + R::param(&D::refr_tint), R::param(&D::roughness_x), + R::param(&D::roughness_y), R::param(&D::IOR), + R::param(&D::distribution), + R::param(&D::thinfilm_thickness, "thinfilm_thickness"), + R::param(&D::thinfilm_ior, "thinfilm_ior"), + R::param(&D::absorption, "absorption"), + R::param(&D::dispersion, "dispersion"), R::close() } }; + } + + template + BSDL_INLINE_METHOD DielectricLobe(T*, const BsdfGlobals& globals, + const Data& data); + + static constexpr const char* name() { return "dielectric_bsdf"; } + + BSDL_INLINE_METHOD Sample eval_impl(const Imath::V3f& wo, + const Imath::V3f& wi) const; + BSDL_INLINE_METHOD Sample sample_impl(const Imath::V3f& wo, + const Imath::V3f& rnd) const; + + BSDL_INLINE_METHOD Power filter_o(const Imath::V3f& wo) const + { + // wo is the same as when constructed, Eo is cached + return !dorefr ? E_ms * wo_absorption : Power::ZERO(); + } + + BSDL_INLINE_METHOD bool single_wavelength() const { return dispersion; } + +protected: + BSDL_INLINE_METHOD Power get_tint(float cosNI) const; + + union { + DielectricRefl refl; + DielectricBoth both; + } spec; + Power refl_tint; + Power refr_tint; + Power wo_absorption; + float E_ms; + bool dorefl; + bool dorefr; + bool dispersion; +}; + +} // namespace mtx + +BSDL_LEAVE_NAMESPACE diff --git a/src/libbsdl/include/BSDL/MTX/bsdf_dielectric_impl.h b/src/libbsdl/include/BSDL/MTX/bsdf_dielectric_impl.h new file mode 100644 index 0000000000..ea238bc420 --- /dev/null +++ b/src/libbsdl/include/BSDL/MTX/bsdf_dielectric_impl.h @@ -0,0 +1,357 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + + +#pragma once + +#include +#include + +#ifndef BAKE_BSDL_TABLES +# include +# include +# include +#endif + +BSDL_ENTER_NAMESPACE + +namespace mtx { + +BSDL_INLINE_METHOD +DielectricFresnel::DielectricFresnel(float _eta, bool backside) +{ + if (backside) + _eta = 1 / _eta; + eta = _eta >= 1 ? CLAMP(_eta, IOR_MIN, IOR_MAX) + : CLAMP(_eta, 1 / IOR_MAX, 1 / IOR_MIN); +} + +BSDL_INLINE_METHOD Power +DielectricFresnel::eval(const float c) const +{ + assert(c >= 0); // slightly above 1.0 is ok + float g = (eta - 1.0f) * (eta + 1.0f) + c * c; + if (g > 0) { + g = sqrtf(g); + float A = (g - c) / (g + c); + float B = (c * (g + c) - 1) / (c * (g - c) + 1); + return Power(0.5f * A * A * (1 + B * B), 1); + } + return Power::UNIT(); // TIR (no refracted component) +} + +BSDL_INLINE_METHOD DielectricFresnel +DielectricFresnel::from_table_index(float tx, bool backside) +{ + const float eta = LERP(SQR(tx), IOR_MIN, IOR_MAX); + return DielectricFresnel(eta, backside); +} + +// Note index of eta equals index of 1 / eta, so this function works for +// either side (both tables) +BSDL_INLINE_METHOD float +DielectricFresnel::table_index() const +{ + // turn the IOR value into something suitable for integrating + // this is the reverse of the method above + const float feta = eta; + const float seta = CLAMP(feta < 1 ? 1 / feta : feta, IOR_MIN, IOR_MAX); + const float x = (seta - IOR_MIN) * (1 / (IOR_MAX - IOR_MIN)); + assert(x >= 0); + assert(x <= 1); + return sqrtf(x); +} + +BSDL_INLINE_METHOD +DielectricRefl::DielectricRefl(float cosNO, float roughness_index, + float fresnel_index) + : d(roughness_index, 0) + , f(DielectricFresnel::from_table_index(fresnel_index, false)) +{ + TabulatedEnergyCurve curve(roughness_index, + fresnel_index); + E_ms = curve.Emiss_eval(cosNO); + assert(0 <= E_ms && E_ms <= 1); +} + +BSDL_INLINE_METHOD +DielectricRefl::DielectricRefl(const GGXDist& dist, + const DielectricFresnel& fresnel, float cosNO, + float roughness) + : d(dist), f(fresnel) +{ + TabulatedEnergyCurve curve(roughness, 0.0f); + E_ms = curve.Emiss_eval(cosNO); + assert(0 <= E_ms && E_ms <= 1); +} + +BSDL_INLINE_METHOD Sample +DielectricRefl::eval(Imath::V3f wo, Imath::V3f wi) const +{ + return eval_turquin_microms_reflection(d, f, E_ms, wo, wi); +} + +BSDL_INLINE_METHOD Sample +DielectricRefl::sample(Imath::V3f wo, float randu, float randv, + float randw) const +{ + return sample_turquin_microms_reflection(d, f, E_ms, wo, + { randu, randv, randw }); +} + +BSDL_INLINE_METHOD +DielectricReflFront::DielectricReflFront(float cosNO, float roughness_index, + float fresnel_index) + : DielectricRefl(cosNO, roughness_index, fresnel_index) +{ +} + +BSDL_INLINE_METHOD +DielectricBoth::DielectricBoth(float cosNO, float roughness_index, + float fresnel_index, bool backside) + : d(roughness_index, 0) + , f(DielectricFresnel::from_table_index(fresnel_index, backside)) +{ +} + +BSDL_INLINE_METHOD +DielectricBoth::DielectricBoth(const GGXDist& dist, + const DielectricFresnel& fresnel) + : d(dist), f(fresnel) +{ +} + +BSDL_INLINE_METHOD Sample +DielectricBoth::eval(Imath::V3f wo, Imath::V3f wi) const +{ + const float cosNO = wo.z; + const float cosNI = wi.z; + assert(cosNO >= 0); + if (cosNI > 0) { + const Imath::V3f m = (wo + wi).normalized(); + const float cosMO = m.dot(wo); + if (cosMO <= 0) + return {}; + const float D = d.D(m); + const float G1 = d.G1(wo); + const Power F = f.eval(cosMO); + if constexpr (BSDLConfig::use_bvn_refraction) { + // Reflection optimized density + const float D_refl_D = d.D_refl_D(wo, m); + const float D_refl = D_refl_D * D; + const float out = d.G2_G1(wi, wo) * G1 / D_refl_D; + const float pdf = D_refl / (4.0f * cosNO) * F[0]; + return { wi, Power(out, 1), pdf, 0 }; + } else { + const float out = d.G2_G1(wi, wo); + const float pdf = (G1 * D * F[0]) / (4.0f * cosNO); + return { wi, Power(out, 1), pdf, 0 }; + } + } else if (cosNI < 0) { + // flip to same side as N + const Imath::V3f Ht = (f.eta * wi + wo).normalized() + * ((f.eta > 1) ? -1 : 1); + // compute fresnel term + const float cosHO = Ht.dot(wo); + const float cosHI = Ht.dot(wi); + if (cosHO <= 0 || cosHI >= 0) + return {}; + const float Ft = 1.0f - f.eval(cosHO)[0]; + if (Ht.z <= 0 || cosHO <= 0 || cosHI >= 0 || Ft <= 0) + return {}; + const float D = d.D(Ht); + const float G1 = d.G1(wo); + float J = (-cosHI * cosHO * SQR(f.eta)) + / (wo.z * SQR(cosHI * f.eta + cosHO)); + if constexpr (BSDLConfig::use_bvn_refraction) { + // Reflection optimized density + const float D_refl_D = d.D_refl_D(wo, Ht); + const float D_refl = D_refl_D * D; + float pdf = D_refl * J * Ft; + const float out = d.G2_G1({ wi.x, wi.y, -wi.z }, wo) * G1 + / D_refl_D; + return { wi, Power(out, 1), pdf, 0 }; + } else { + const float out = d.G2_G1({ wi.x, wi.y, -wi.z }, wo); + + float pdf = J * G1 * D * Ft; + return { wi, Power(out, 1), pdf, 0 }; + } + + } else + return {}; +} + +BSDL_INLINE_METHOD Sample +DielectricBoth::sample(Imath::V3f wo, float randu, float randv, + float randw) const +{ + // This skips micro normals not valid for reflection, but they + // could be valid for refraction. Energy is ok because we renormalize + // this lobe, but refraction will be biased for high roughness. We + // trade that for reduced noise. We can disable BVN at compile time. + Imath::V3f m; + if constexpr (BSDLConfig::use_bvn_refraction) + m = d.sample_for_refl(wo, randu, randv); + else + m = d.sample(wo, randu, randv); + const float cosMO = wo.dot(m); + if (cosMO <= 0) + return {}; + const float F = f.eval(cosMO)[0]; + bool choose_reflect = randw < F; + const Imath::V3f wi = choose_reflect ? reflect(wo, m) + : refract(wo, m, f.eta); + if ((choose_reflect && wi.z <= 0) || (!choose_reflect && wi.z >= 0)) + return {}; + return eval(wo, wi); +} + +BSDL_INLINE_METHOD +DielectricBothFront::DielectricBothFront(float cosNO, float roughness_index, + float fresnel_index) + : DielectricBoth(cosNO, roughness_index, fresnel_index, false) +{ +} + +BSDL_INLINE_METHOD +DielectricBothBack::DielectricBothBack(float cosNO, float roughness_index, + float fresnel_index) + : DielectricBoth(cosNO, roughness_index, fresnel_index, true) +{ +} + +template +template +BSDL_INLINE_METHOD +DielectricLobe::DielectricLobe(T* lobe, const BsdfGlobals& globals, + const Data& data) + : Base(lobe, globals.visible_normal(data.N), data.U, 0.0f, globals.lambda_0, + MAX_RGB(data.refr_tint) > 0) + , refl_tint(globals.wave(data.refl_tint)) + , refr_tint(globals.wave(data.refr_tint)) + , wo_absorption(1.0f, globals.lambda_0) + , dispersion(data.dispersion > 0 && globals.lambda_0 > 0) +{ + dorefl = MAX_RGB(data.refl_tint) > 0; + dorefr = MAX_RGB(data.refr_tint) > 0; + Base::sample_filter = globals.get_sample_filter(Base::frame.Z, false); + // MaterialX expects the raw x/y roughness as input, but for albedo tables it + // is better to use the roughness/anisotropy parametrization so we can + // ignore roughness + const float rx = CLAMP(data.roughness_x, EPSILON, 2.0f); + const float ry = CLAMP(data.roughness_y, EPSILON, 2.0f); + const float ax = std::max(rx, ry); + const float ay = std::min(rx, ry); + const float b = ay / ax; + const float aniso = (1 - b) / (1 + b); + // Also assume we square the roughness for linearity + const float roughness = globals.regularize_roughness( + sqrtf(ax / (1 + aniso))); + const float cosNO = Base::frame.Z.dot(globals.wo); + const float IOR + = CLAMP(dispersion + ? Spectrum::get_dispersion_ior(data.dispersion, data.IOR, + globals.lambda_0) + : data.IOR, + DielectricFresnel::IOR_MIN, DielectricFresnel::IOR_MAX); + + assert(cosNO >= 0); + if (dorefl && !dorefr) { + spec.refl = DielectricRefl(GGXDist(roughness, aniso, rx < ry), + DielectricFresnel(globals.relative_eta(IOR), + globals.backfacing), + cosNO, roughness); + E_ms = TabulatedEnergyCurve( + roughness, spec.refl.fresnel().table_index()) + .Emiss_eval(cosNO); + } else if (dorefr) { + spec.both = DielectricBoth(GGXDist(roughness, aniso, rx < ry), + DielectricFresnel(globals.relative_eta(IOR), + globals.backfacing)); + if (spec.both.fresnel().eta >= 1.0f) { + E_ms = TabulatedEnergyCurve( + roughness, spec.both.fresnel().table_index()) + .Emiss_eval(cosNO); + } else { + E_ms = TabulatedEnergyCurve( + roughness, spec.both.fresnel().table_index()) + .Emiss_eval(cosNO); + } + } + + Base::set_roughness(roughness); + + if (MAX_RGB(data.absorption) > 0) { + constexpr auto fast_exp = BSDLConfig::Fast::expf; + + float cos_p = cosNO; + // Take into account how the ray bends with the refraction to compute + // the traveled distance through absorption. + const float sinNO2 = 1 - SQR(cosNO); + const float inveta2 = SQR(1 / spec.refl.fresnel().eta); + cos_p = sqrtf(1 - std::min(1.0f, inveta2 * sinNO2)); + const float dist = 1 / std::max(cos_p, FLOAT_MIN); + + const Power sigma_a = globals.wave(data.absorption); + wo_absorption + = Power([&](int i) { return fast_exp(-sigma_a[i] * dist); }, + globals.lambda_0); + } +} + +template +BSDL_INLINE_METHOD Sample +DielectricLobe::eval_impl(const Imath::V3f& wo, + const Imath::V3f& wi) const +{ + if (!dorefl && !dorefr) + return {}; + Sample s = {}; + if (dorefl && !dorefr) + s = spec.refl.eval(wo, wi); + else { + s = spec.both.eval(wo, wi); + s.weight *= 1 / std::max(0.01f, 1 - E_ms); + } + + s.weight *= get_tint(s.wi.z); + s.roughness = Base::roughness(); + return s; +} + +template +BSDL_INLINE_METHOD Sample +DielectricLobe::sample_impl(const Imath::V3f& wo, + const Imath::V3f& rnd) const +{ + if (!dorefl && !dorefr) + return {}; + + Sample s = {}; + if (dorefl && !dorefr) + s = spec.refl.sample(wo, rnd.x, rnd.y, rnd.z); + else { + s = spec.both.sample(wo, rnd.x, rnd.y, rnd.z); + s.weight *= 1 / std::max(0.01f, 1 - E_ms); + } + + if (MAX_ABS_XYZ(s.wi) < EPSILON) + return {}; + + s.weight *= get_tint(s.wi.z); + s.roughness = Base::roughness(); + return s; +} + +template +BSDL_INLINE_METHOD Power +DielectricLobe::get_tint(float cosNI) const +{ + return cosNI > 0 ? refl_tint : refr_tint; +} + +} // namespace mtx + +BSDL_LEAVE_NAMESPACE diff --git a/src/libbsdl/include/BSDL/SPI/bsdf_backscatter_decl.h b/src/libbsdl/include/BSDL/SPI/bsdf_backscatter_decl.h index ff30fd8ccf..1be197672b 100644 --- a/src/libbsdl/include/BSDL/SPI/bsdf_backscatter_decl.h +++ b/src/libbsdl/include/BSDL/SPI/bsdf_backscatter_decl.h @@ -61,7 +61,8 @@ struct CharlieSheen : public SheenMicrofacet { }; static BSDL_INLINE_METHOD Energy& get_energy(); - static const char* lut_header() { return "bsdf_backscatter_luts.h"; } + static constexpr const char* NS = "spi"; + static const char* lut_header() { return "SPI/bsdf_backscatter_luts.h"; } static const char* struct_name() { return "CharlieSheen"; } }; diff --git a/src/libbsdl/include/BSDL/SPI/bsdf_clearcoat_decl.h b/src/libbsdl/include/BSDL/SPI/bsdf_clearcoat_decl.h index 29614005c3..11c64ed32e 100644 --- a/src/libbsdl/include/BSDL/SPI/bsdf_clearcoat_decl.h +++ b/src/libbsdl/include/BSDL/SPI/bsdf_clearcoat_decl.h @@ -39,7 +39,8 @@ struct PlasticGGX : public bsdl::MicrofacetMS { }; static BSDL_INLINE_METHOD Energy& get_energy(); - static const char* lut_header() { return "bsdf_clearcoat_luts.h"; } + static constexpr const char* NS = "spi"; + static const char* lut_header() { return "SPI/bsdf_clearcoat_luts.h"; } static const char* struct_name() { return "PlasticGGX"; } }; diff --git a/src/libbsdl/include/BSDL/SPI/bsdf_dielectric_decl.h b/src/libbsdl/include/BSDL/SPI/bsdf_dielectric_decl.h index 05efd4b17f..ab7cfef0be 100644 --- a/src/libbsdl/include/BSDL/SPI/bsdf_dielectric_decl.h +++ b/src/libbsdl/include/BSDL/SPI/bsdf_dielectric_decl.h @@ -13,6 +13,7 @@ BSDL_ENTER_NAMESPACE namespace spi { struct DielectricFresnel { + DielectricFresnel() = default; BSDL_INLINE_METHOD DielectricFresnel(float _eta, bool backside); BSDL_INLINE_METHOD float eval(const float c) const; BSDL_INLINE_METHOD float avg() const; @@ -49,6 +50,8 @@ template struct Dielectric { { } + Dielectric() = default; + BSDL_INLINE_METHOD Sample eval(Imath::V3f wo, Imath::V3f wi, bool doreflect, bool dorefract) const; BSDL_INLINE_METHOD Sample sample(Imath::V3f wo, float randu, float randv, @@ -79,12 +82,17 @@ struct DielectricFront : public Dielectric { BSDL_INLINE_METHOD DielectricFront(const GGXDist& dist, const DielectricFresnel& fresnel, float prob_clamp); + DielectricFront() = default; struct Energy { float data[Nf * Nr * Nc]; }; static BSDL_INLINE_METHOD Energy& get_energy(); - static const char* lut_header() { return "bsdf_dielectric_front_luts.h"; } + static constexpr const char* NS = "spi"; + static const char* lut_header() + { + return "SPI/bsdf_dielectric_front_luts.h"; + } static const char* struct_name() { return "DielectricFront"; } }; @@ -94,12 +102,17 @@ struct DielectricBack : public Dielectric { BSDL_INLINE_METHOD DielectricBack(const bsdl::GGXDist& dist, const DielectricFresnel& fresnel, float prob_clamp); + DielectricBack() = default; struct Energy { float data[Nf * Nr * Nc]; }; static BSDL_INLINE_METHOD Energy& get_energy(); - static const char* lut_header() { return "bsdf_dielectric_back_luts.h"; } + static constexpr const char* NS = "spi"; + static const char* lut_header() + { + return "SPI/bsdf_dielectric_back_luts.h"; + } static const char* struct_name() { return "DielectricBack"; } }; diff --git a/src/libbsdl/include/BSDL/SPI/bsdf_sheenltc_decl.h b/src/libbsdl/include/BSDL/SPI/bsdf_sheenltc_decl.h index cbe6a2e2f7..651e367dd8 100644 --- a/src/libbsdl/include/BSDL/SPI/bsdf_sheenltc_decl.h +++ b/src/libbsdl/include/BSDL/SPI/bsdf_sheenltc_decl.h @@ -49,7 +49,8 @@ struct SheenLTC { typedef const Imath::V3f (*V32_array)[32]; static BSDL_INLINE_METHOD V32_array param_ptr(); - static const char* lut_header() { return "bsdf_sheenltc_luts.h"; } + static constexpr const char* NS = "spi"; + static const char* lut_header() { return "SPI/bsdf_sheenltc_luts.h"; } static const char* struct_name() { return "SheenLTC"; } BSDL_INLINE_METHOD float calculate_phi(const Imath::V3f& v) const; diff --git a/src/libbsdl/include/BSDL/SPI/bsdf_thinlayer_decl.h b/src/libbsdl/include/BSDL/SPI/bsdf_thinlayer_decl.h index 98a62d982d..55f0a77270 100644 --- a/src/libbsdl/include/BSDL/SPI/bsdf_thinlayer_decl.h +++ b/src/libbsdl/include/BSDL/SPI/bsdf_thinlayer_decl.h @@ -105,7 +105,8 @@ struct Thinlayer : public ThinMicrofacet { }; static BSDL_INLINE_METHOD Energy& get_energy(); - static const char* lut_header() { return "bsdf_thinlayer_luts.h"; } + static constexpr const char* NS = "spi"; + static const char* lut_header() { return "SPI/bsdf_thinlayer_luts.h"; } static const char* struct_name() { return "Thinlayer"; } }; diff --git a/src/libbsdl/include/BSDL/bsdf_decl.h b/src/libbsdl/include/BSDL/bsdf_decl.h index 4ec16c985f..91f91963a0 100644 --- a/src/libbsdl/include/BSDL/bsdf_decl.h +++ b/src/libbsdl/include/BSDL/bsdf_decl.h @@ -102,6 +102,7 @@ template struct Lobe : public BSDF_ROOT { { sample_filter.thinfilm = f; } + BSDL_INLINE_METHOD bool single_wavelength() const { return false; } Frame frame; typename BsdfGlobals::Filter sample_filter; diff --git a/src/libbsdl/include/BSDL/config.h b/src/libbsdl/include/BSDL/config.h index 07839f5e33..a14e2eb9e5 100644 --- a/src/libbsdl/include/BSDL/config.h +++ b/src/libbsdl/include/BSDL/config.h @@ -24,6 +24,9 @@ #ifndef BSDL_UNROLL # define BSDL_UNROLL() _Pragma("unroll") #endif +#ifndef BSDL_STRHASH +# define BSDL_STRHASH(str) reinterpret_cast(str) +#endif #include #include @@ -86,6 +89,10 @@ struct BSDLDefaultConfig { }; static constexpr int HERO_WAVELENGTH_CHANNELS = 4; + // Use bounded visible normals for refraction. Biased but cleaner. + // Note this has to be available for genluts.cpp, don't override it independently. + static constexpr bool use_bvn_refraction = true; + enum class ColorSpaceTag { sRGB, ACEScg }; diff --git a/src/libbsdl/include/BSDL/microfacet_tools_decl.h b/src/libbsdl/include/BSDL/microfacet_tools_decl.h index f5a20f6209..b702f02f9c 100644 --- a/src/libbsdl/include/BSDL/microfacet_tools_decl.h +++ b/src/libbsdl/include/BSDL/microfacet_tools_decl.h @@ -11,8 +11,6 @@ BSDL_ENTER_NAMESPACE struct GGXDist { - BSDL_INLINE_METHOD GGXDist() : ax(0), ay(0) {} - BSDL_INLINE_METHOD GGXDist(float rough, float aniso, bool flip_aniso = false) : ax(SQR(rough)), ay(ax) @@ -25,18 +23,22 @@ struct GGXDist { ax = std::max(ax * (1 + aniso), ALPHA_MIN); ay = std::max(ay * (1 - aniso), ALPHA_MIN); } + GGXDist() = default; BSDL_INLINE_METHOD float D(const Imath::V3f& Hr) const; + // When using sample_for_refl this gives you D_refl / D for convenience + BSDL_INLINE_METHOD float D_refl_D(const Imath::V3f& wo, + const Imath::V3f& m) const; BSDL_INLINE_METHOD float G1(Imath::V3f w) const; BSDL_INLINE_METHOD float G2_G1(Imath::V3f wi, Imath::V3f wo) const; + // Plain visible micro normal sampling. PDF is G1 * D BSDL_INLINE_METHOD Imath::V3f sample(const Imath::V3f& wo, float randu, float randv) const; - // Reflection specific improved sample/pdf functions + // Reflection specific improved sample function. Gives you a micro + // normal that won't reflect under the surface. PDF is D_refl_D() * D BSDL_INLINE_METHOD Imath::V3f - sample_reflection(const Imath::V3f& wo, float randu, float randv) const; - BSDL_INLINE_METHOD float pdf_reflection(const Imath::V3f& wo, - const Imath::V3f& wi) const; + sample_for_refl(const Imath::V3f& wo, float randu, float randv) const; BSDL_INLINE_METHOD float roughness() const { return std::max(ax, ay); } @@ -94,7 +96,8 @@ struct MiniMicrofacetGGX : public MiniMicrofacet { }; static BSDL_INLINE_METHOD Energy& get_energy(); - static const char* lut_header() { return "microfacet_tools_luts.h"; } + static constexpr const char* NS = "spi"; + static const char* lut_header() { return "SPI/microfacet_tools_luts.h"; } static const char* struct_name() { return "MiniMicrofacetGGX"; } }; diff --git a/src/libbsdl/include/BSDL/microfacet_tools_impl.h b/src/libbsdl/include/BSDL/microfacet_tools_impl.h index 472b6e6e93..470a52f17b 100644 --- a/src/libbsdl/include/BSDL/microfacet_tools_impl.h +++ b/src/libbsdl/include/BSDL/microfacet_tools_impl.h @@ -70,7 +70,7 @@ GGXDist::sample(const Imath::V3f& wo, float randu, float randv) const } BSDL_INLINE_METHOD Imath::V3f -GGXDist::sample_reflection(const Imath::V3f& wo, float randu, float randv) const +GGXDist::sample_for_refl(const Imath::V3f& wo, float randu, float randv) const { // From "Bounded VNDF Sampling for Smith–GGX Reflections" Eto - Tokuyoshi. // This code is taken from listing 1 almost verbatim. @@ -91,17 +91,14 @@ GGXDist::sample_reflection(const Imath::V3f& wo, float randu, float randv) const // Compute the microfacet normal m Imath::V3f m_std = i_std + o_std; Imath::V3f m = Imath::V3f(m_std.x * ax, m_std.y * ay, m_std.z).normalized(); - // Return the reflection vector o - return 2 * wo.dot(m) * m - wo; + return m; } BSDL_INLINE_METHOD float -GGXDist::pdf_reflection(const Imath::V3f& wo, const Imath::V3f& wi) const +GGXDist::D_refl_D(const Imath::V3f& wo, const Imath::V3f& m) const { // From "Bounded VNDF Sampling for Smith–GGX Reflections" Eto - Tokuyoshi. // This code is taken from listing 2 almost verbatim. - const Imath::V3f m = (wo + wi).normalized(); - const float ndf = D(m); const Imath::V2f ai = { wo.x * ax, wo.y * ay }; const float len2 = ai.dot(ai); const float t = sqrtf(len2 + SQR(wo.z)); @@ -110,7 +107,7 @@ GGXDist::pdf_reflection(const Imath::V3f& wo, const Imath::V3f& wi) const const float a2 = SQR(a); const float s2 = SQR(s); const float k = (1 - a2) * s2 / (s2 + a2 * SQR(wo.z)); // Eq. 5 - return ndf / (2 * (k * wo.z + t)); // Eq. 8 * || dm/do || + return 2 * wo.z /* * D(m) */ / (k * wo.z + t); // Eq. 8 } template @@ -357,19 +354,21 @@ eval_turquin_microms_reflection(const GGXDist& dist, const Fresnel& fresnel, Imath::V3f m = (wo + wi).normalized(); float cosMO = m.dot(wo); const float D = dist.D(m); + float D_refl_D = dist.D_refl_D(wo, m); + float D_refl = D_refl_D * D; const float G1 = dist.G1(wo); - float s_pdf = dist.pdf_reflection(wo, wi); - const float out = dist.G2_G1(wi, wo) * (G1 * D) / (4.0f * cosNO * s_pdf); + float pdf = D_refl / (4 * cosNO); + const float out = dist.G2_G1(wi, wo) * G1 / D_refl_D; // fresnel term between outgoing direction and microfacet const Power F = fresnel.eval(cosMO); // From "Practical multiple scattering compensation for microfacet models" - Emmanuel Turquin // equation 16. Should we use F0 for msf scaling? Doesn't make a big difference. const Power F_ms = F; - const float msf = E_ms / (1 - E_ms); + const float msf = E_ms / std::max(0.01f, 1 - E_ms); const Power one(1, 1); const Power O = out * F * (one + F_ms * msf); - return { wi, O, s_pdf, -1 /* roughness set by caller */ }; + return { wi, O, pdf, -1 /* roughness set by caller */ }; } template @@ -382,7 +381,8 @@ sample_turquin_microms_reflection(const GGXDist& dist, const Fresnel& fresnel, if (cosNO <= 0) return {}; - Imath::V3f wi = dist.sample_reflection(wo, rnd.x, rnd.y); + Imath::V3f m = dist.sample_for_refl(wo, rnd.x, rnd.y); + Imath::V3f wi = reflect(wo, m); if (wi.z <= 0) return {}; diff --git a/src/libbsdl/include/BSDL/params.h b/src/libbsdl/include/BSDL/params.h index 25348b3238..403d40fb77 100644 --- a/src/libbsdl/include/BSDL/params.h +++ b/src/libbsdl/include/BSDL/params.h @@ -12,6 +12,8 @@ BSDL_ENTER_NAMESPACE +using Stringhash = uintptr_t; + enum class ParamType : uint8_t { NONE = 0, VECTOR, @@ -39,7 +41,7 @@ template <> BSDL_INLINE_METHOD constexpr ParamType ParamTypeOf::get( template <> BSDL_INLINE_METHOD constexpr ParamType ParamTypeOf::get() { return ParamType::INT; } template <> BSDL_INLINE_METHOD constexpr ParamType ParamTypeOf::get() { return ParamType::FLOAT; } template <> BSDL_INLINE_METHOD constexpr ParamType ParamTypeOf::get() { return ParamType::COLOR; } -template <> BSDL_INLINE_METHOD constexpr ParamType ParamTypeOf::get() { return ParamType::STRING; } +template <> BSDL_INLINE_METHOD constexpr ParamType ParamTypeOf::get() { return ParamType::STRING; } template <> BSDL_INLINE_METHOD constexpr ParamType ParamTypeOf::get() { return ParamType::CLOSURE; } // clang-format on diff --git a/src/libbsdl/include/BSDL/spectrum_decl.h b/src/libbsdl/include/BSDL/spectrum_decl.h index 493828e29f..2bfd4404b5 100644 --- a/src/libbsdl/include/BSDL/spectrum_decl.h +++ b/src/libbsdl/include/BSDL/spectrum_decl.h @@ -130,6 +130,10 @@ struct Spectrum { } } + static BSDL_INLINE_METHOD float get_dispersion_ior(const float dispersion, + const float basic_ior, + const float wavelength); + template static BSDL_INLINE_METHOD T lookup(float lambda, const T array[LAMBDA_RES]); }; diff --git a/src/libbsdl/include/BSDL/spectrum_impl.h b/src/libbsdl/include/BSDL/spectrum_impl.h index 5882b4699f..fed9426f4f 100644 --- a/src/libbsdl/include/BSDL/spectrum_impl.h +++ b/src/libbsdl/include/BSDL/spectrum_impl.h @@ -52,6 +52,28 @@ Spectrum::spec_to_xyz(Power wave, float lambda_0) return total; } +// Modify basic IOR to get the dispersion IOR. +BSDL_INLINE_METHOD +float +Spectrum::get_dispersion_ior(const float dispersion, const float basic_ior, + const float wavelength) +{ + // Fraunhofer D, F and C spectral lines, in micrometers squared + const float nD2 = SQR(589.3f * 1e-3f); + const float nF2 = SQR(486.1f * 1e-3f); + const float nC2 = SQR(656.3f * 1e-3f); + + // convert Abbe number to Cauchy coefficients + const float abbe = 1 / dispersion; + const float cauchyC = ((basic_ior - 1.0f) / abbe) + * ((nC2 * nF2) / (nC2 - nF2)); + const float cauchyB = basic_ior - cauchyC * (1.0f / nD2); + + // Cauchy's equation with two terms + // wavelength converted from nanometers to micrometers + return cauchyB + cauchyC / SQR(wavelength * 1e-3f); +} + BSDL_INLINE_METHOD Power::Power(const Imath::C3f rgb, float lambda_0) { diff --git a/src/libbsdl/include/BSDL/spectrum_luts.h b/src/libbsdl/include/BSDL/spectrum_luts.h index 92943f681c..203d8f655d 100644 --- a/src/libbsdl/include/BSDL/spectrum_luts.h +++ b/src/libbsdl/include/BSDL/spectrum_luts.h @@ -2,7 +2,6 @@ // SPDX-License-Identifier: BSD-3-Clause // https://github.com/AcademySoftwareFoundation/OpenShadingLanguage - #pragma once #include diff --git a/src/libbsdl/src/genluts.cpp b/src/libbsdl/src/genluts.cpp index ed80c517eb..ddfedc9014 100644 --- a/src/libbsdl/src/genluts.cpp +++ b/src/libbsdl/src/genluts.cpp @@ -10,6 +10,7 @@ #include using BSDLConfig = bsdl::BSDLDefaultConfig; +#include #include #include #include @@ -25,14 +26,17 @@ using BSDLConfig = bsdl::BSDLDefaultConfig; #include #include -#define BAKE_BSDF_LIST(E) \ - E(spi::MiniMicrofacetGGX) \ - E(spi::PlasticGGX) \ - E(spi::DielectricFront) \ - E(spi::DielectricBack) \ - E(spi::CharlieSheen) \ - E(spi::SheenLTC) \ - E(spi::Thinlayer) +#define BAKE_BSDF_LIST(E) \ + E(spi::MiniMicrofacetGGX) \ + E(spi::PlasticGGX) \ + E(spi::DielectricFront) \ + E(spi::DielectricBack) \ + E(spi::CharlieSheen) \ + E(spi::SheenLTC) \ + E(spi::Thinlayer) \ + E(mtx::DielectricReflFront) \ + E(mtx::DielectricBothFront) \ + E(mtx::DielectricBothBack) #define LUT_PLACEHOLDER(type) \ BSDL_ENTER_NAMESPACE \ @@ -138,8 +142,8 @@ compute_E(float cos_theta, const BSDF& bsdf, uint32_t fresnel_index, E = LERP(1.0f / (1.0f + i), E, out); } assert(E >= 0); - assert(E <= 1); - return E; + assert(E <= 1.01f); // Allow for some error + return std::min(E, 1.0f); } template @@ -174,7 +178,7 @@ bake_emiss_tables(const std::string& output_dir) fprintf(outf, "#pragma once\n\n"); fprintf(outf, "BSDL_ENTER_NAMESPACE\n\n"); - fprintf(outf, "namespace spi {\n\n"); + fprintf(outf, "namespace %s {\n\n", BSDF::NS); fprintf(outf, "BSDL_INLINE_METHOD %s::Energy& %s::get_energy()\n", BSDF::struct_name(), BSDF::struct_name()); fprintf(outf, "{\n"); @@ -193,7 +197,7 @@ bake_emiss_tables(const std::string& output_dir) fprintf(outf, " }};\n"); fprintf(outf, " return energy;\n"); fprintf(outf, "}\n\n"); - fprintf(outf, "} // namespace spi \n\n"); + fprintf(outf, "} // namespace %s \n\n", BSDF::NS); fprintf(outf, "BSDL_LEAVE_NAMESPACE\n"); fclose(outf); printf("Wrote LUTs to %s\n", out_file.c_str()); diff --git a/src/testrender/bsdl_config.h b/src/testrender/bsdl_config.h index 5e9f59d3b0..255027a6cb 100644 --- a/src/testrender/bsdl_config.h +++ b/src/testrender/bsdl_config.h @@ -4,6 +4,7 @@ #define BSDL_INLINE_METHOD inline OSL_HOSTDEVICE #define BSDL_DECL OSL_DEVICE #define BSDL_UNROLL() // Do nothing +#define BSDL_STRHASH(str) OSL::strhash(str) #include diff --git a/src/testrender/shading.cpp b/src/testrender/shading.cpp index 3cfefbd141..96c140f96a 100644 --- a/src/testrender/shading.cpp +++ b/src/testrender/shading.cpp @@ -9,6 +9,7 @@ #include "sampling.h" #include +#include #include #include @@ -131,6 +132,41 @@ struct MxConductor : public bsdl::mtx::ConductorLobe { } }; +// This is the thin wrapper to insert mtx::ConductorLobe into testrender +struct MxDielectric : public bsdl::mtx::DielectricLobe { + using Base = bsdl::mtx::DielectricLobe; + + static constexpr int closureid() { return MX_DIELECTRIC_ID; } + + OSL_HOSTDEVICE MxDielectric(const Data& data, const Vec3& wo, + bool backfacing, float path_roughness) + : Base(this, + bsdl::BsdfGlobals(wo, + data.N, // Nf + data.N, // Ngf + backfacing, path_roughness, + 1.0f, // outer_ior + 0), // hero wavelength off + data) + { + } + + OSL_HOSTDEVICE BSDF::Sample eval(const Vec3& wo, const Vec3& wi) const + { + bsdl::Sample s = Base::eval_impl(Base::frame.local(wo), + Base::frame.local(wi)); + return { wi, s.weight.toRGB(0), s.pdf, s.roughness }; + } + OSL_HOSTDEVICE BSDF::Sample sample(const Vec3& wo, float rx, float ry, + float rz) const + { + bsdl::Sample s = Base::sample_impl(Base::frame.local(wo), + { rx, ry, rz }); + return { Base::frame.world(s.wi), s.weight.toRGB(0), s.pdf, + s.roughness }; + } +}; + #ifndef __CUDACC__ // Helper to register BSDL closures struct BSDLtoOSL { @@ -241,22 +277,6 @@ register_closures(OSL::ShadingSystem* shadingsys) CLOSURE_FLOAT_PARAM(MxBurleyDiffuseParams, roughness), CLOSURE_STRING_KEYPARAM(MxBurleyDiffuseParams, label, "label"), CLOSURE_FINISH_PARAM(MxBurleyDiffuseParams) } }, - { "dielectric_bsdf", - MX_DIELECTRIC_ID, - { CLOSURE_VECTOR_PARAM(MxDielectricParams, N), - CLOSURE_VECTOR_PARAM(MxDielectricParams, U), - CLOSURE_COLOR_PARAM(MxDielectricParams, reflection_tint), - CLOSURE_COLOR_PARAM(MxDielectricParams, transmission_tint), - CLOSURE_FLOAT_PARAM(MxDielectricParams, roughness_x), - CLOSURE_FLOAT_PARAM(MxDielectricParams, roughness_y), - CLOSURE_FLOAT_PARAM(MxDielectricParams, ior), - CLOSURE_STRING_PARAM(MxDielectricParams, distribution), - CLOSURE_FLOAT_KEYPARAM(MxDielectricParams, thinfilm_thickness, - "thinfilm_thickness"), - CLOSURE_FLOAT_KEYPARAM(MxDielectricParams, thinfilm_ior, - "thinfilm_ior"), - CLOSURE_STRING_KEYPARAM(MxDielectricParams, label, "label"), - CLOSURE_FINISH_PARAM(MxDielectricParams) } }, { "generalized_schlick_bsdf", MX_GENERALIZED_SCHLICK_ID, { CLOSURE_VECTOR_PARAM(MxGeneralizedSchlickParams, N), @@ -333,7 +353,7 @@ register_closures(OSL::ShadingSystem* shadingsys) shadingsys->register_closure(b.name, b.id, b.params, nullptr, nullptr); // BSDFs coming from BSDL - using bsdl_set = bsdl::TypeList; + using bsdl_set = bsdl::TypeList; // Register them bsdl_set::apply(BSDLtoOSL { shadingsys }); } @@ -1494,7 +1514,8 @@ struct ZeltnerBurleySheen final : public BSDF, MxSheenParams { }; OSL_HOSTDEVICE Color3 -evaluate_layer_opacity(const ShaderGlobalsType& sg, const ClosureColor* closure) +evaluate_layer_opacity(const ShaderGlobalsType& sg, float path_roughness, + const ClosureColor* closure) { // Null closure, the layer is fully transparent if (closure == nullptr) @@ -1536,17 +1557,11 @@ evaluate_layer_opacity(const ShaderGlobalsType& sg, const ClosureColor* closure) closure = nullptr; break; } - case MX_DIELECTRIC_ID: { - const MxDielectricParams& params - = *comp->as(); - // Transmissive dielectrics are opaque - if (!is_black(params.transmission_tint)) { - closure = nullptr; - break; - } - MxMicrofacet mf(params, - 1.0f); - weight *= w * mf.get_albedo(-sg.I); + case MxDielectric::closureid(): { + const MxDielectric::Data& params + = *comp->as(); + MxDielectric d(params, -sg.I, sg.backfacing, path_roughness); + weight *= w * (Color3(1) - d.filter_o(-sg.I).toRGB(0)); closure = nullptr; break; } @@ -1590,8 +1605,9 @@ evaluate_layer_opacity(const ShaderGlobalsType& sg, const ClosureColor* closure) } OSL_HOSTDEVICE void -process_medium_closure(const ShaderGlobalsType& sg, ShadingResult& result, - const ClosureColor* closure, const Color3& w) +process_medium_closure(const ShaderGlobalsType& sg, float path_roughness, + ShadingResult& result, const ClosureColor* closure, + const Color3& w) { if (!closure) return; @@ -1621,7 +1637,8 @@ process_medium_closure(const ShaderGlobalsType& sg, ShadingResult& result, const MxLayerParams* params = comp->as(); Color3 base_w = weight * (Color3(1) - - clamp(evaluate_layer_opacity(sg, params->top), + - clamp(evaluate_layer_opacity(sg, path_roughness, + params->top), 0.f, 1.f)); closure = params->top; ptr_stack[stack_idx] = params->base; @@ -1656,13 +1673,13 @@ process_medium_closure(const ShaderGlobalsType& sg, ShadingResult& result, closure = nullptr; break; } - case MX_DIELECTRIC_ID: { - const ClosureComponent* comp = closure->as_comp(); - const auto& params = *comp->as(); - if (!is_black(weight * comp->w * params.transmission_tint)) { + case MxDielectric::closureid(): { + const ClosureComponent* comp = closure->as_comp(); + const MxDielectric::Data& params = *comp->as(); + if (!is_black(weight * comp->w * params.refr_tint)) { // TODO: properly track a medium stack here ... - result.refraction_ior = sg.backfacing ? 1.0f / params.ior - : params.ior; + result.refraction_ior = sg.backfacing ? 1.0f / params.IOR + : params.IOR; } closure = nullptr; break; @@ -1825,17 +1842,12 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness, ok = result.bsdf.add_bsdf(cw, params); break; } - case MX_DIELECTRIC_ID: { - const MxDielectricParams& params - = *comp->as(); - if (is_black(params.transmission_tint)) - ok = result.bsdf.add_bsdf< - MxMicrofacet>( - cw, params, 1.0f); - else - ok = result.bsdf.add_bsdf< - MxMicrofacet>( - cw, params, result.refraction_ior); + case MxDielectric::closureid(): { + const MxDielectric::Data& params + = *comp->as(); + ok = result.bsdf.add_bsdf(cw, params, -sg.I, + sg.backfacing, + path_roughness); break; } case MxConductor::closureid(): { @@ -1897,7 +1909,8 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness, Color3 base_w = weight * (Color3(1, 1, 1) - - clamp(evaluate_layer_opacity(sg, srcparams->top), + - clamp(evaluate_layer_opacity(sg, path_roughness, + srcparams->top), 0.f, 1.f)); closure = srcparams->top; weight = cw; @@ -1947,7 +1960,7 @@ process_closure(const ShaderGlobalsType& sg, float path_roughness, ShadingResult& result, const ClosureColor* Ci, bool light_only) { if (!light_only) - process_medium_closure(sg, result, Ci, Color3(1)); + process_medium_closure(sg, path_roughness, result, Ci, Color3(1)); process_bsdf_closure(sg, path_roughness, result, Ci, Color3(1), light_only); } diff --git a/src/testrender/shading.h b/src/testrender/shading.h index a5b4959cc2..45c37103f5 100644 --- a/src/testrender/shading.h +++ b/src/testrender/shading.h @@ -122,82 +122,6 @@ struct MxMicrofacetBaseParams { ustringhash label; }; -struct MxDielectricParams : public MxMicrofacetBaseParams { - Color3 reflection_tint; - Color3 transmission_tint; - float ior; - // optional - float thinfilm_thickness; - float thinfilm_ior; - - OSL_HOSTDEVICE Color3 evalR(float cos_theta) const - { - return reflection_tint * fresnel_dielectric(cos_theta, ior); - } - - OSL_HOSTDEVICE Color3 evalT(float cos_theta) const - { - return transmission_tint * (1.0f - fresnel_dielectric(cos_theta, ior)); - } - - OSL_HOSTDEVICE Color3 dirAlbedoR(float cos_theta) const - { - float iorRatio = (ior - 1.0f) / (ior + 1.0f); - Color3 f0(iorRatio * iorRatio); - Color3 f90(1.0f); - - // Rational quadratic fit for GGX directional albedo - // https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl - float x = OIIO::clamp(cos_theta, 0.0f, 1.0f); - float y = sqrtf(roughness_x * roughness_y); // average alpha - float x2 = x * x; - float y2 = y * y; - Vec2 num = Vec2(0.1003f, 0.9345f) + Vec2(-0.6303f, -2.323f) * x - + Vec2(9.748f, 2.229f) * y + Vec2(-2.038f, -3.748f) * x * y - + Vec2(29.34f, 1.424f) * x2 + Vec2(-8.245f, -0.7684f) * y2 - + Vec2(-26.44f, 1.436f) * x2 * y - + Vec2(19.99f, 0.2913f) * x * y2 - + Vec2(-5.448f, 0.6286f) * x2 * y2; - Vec2 den = Vec2(1.0f, 1.0f) + Vec2(-1.765f, 0.2281f) * x - + Vec2(8.263f, 15.94f) * y + Vec2(11.53f, -55.83f) * x * y - + Vec2(28.96f, 13.08f) * x2 + Vec2(-7.507f, 41.26f) * y2 - + Vec2(-36.11f, 54.9f) * x2 * y - + Vec2(15.86f, 300.2f) * x * y2 - + Vec2(33.37f, -285.1f) * x2 * y2; - float a = OIIO::clamp(num.x / den.x, 0.0f, 1.0f); - float b = OIIO::clamp(num.y / den.y, 0.0f, 1.0f); - return reflection_tint * (f0 * a + f90 * b); - } -}; - -struct MxConductorParams : public MxMicrofacetBaseParams { - Color3 ior; - Color3 extinction; - // optional - float thinfilm_thickness; - float thinfilm_ior; - - OSL_HOSTDEVICE Color3 evalR(float cos_theta) const - { - return fresnel_conductor(cos_theta, ior, extinction); - } - - OSL_HOSTDEVICE Color3 evalT(float cos_theta) const { return Color3(0.0f); } - - OSL_HOSTDEVICE Color3 dirAlbedoR(float cos_theta) const - { - // TODO: Integrate the MaterialX fit for GGX directional albedo, which - // may improve multiscatter compensation for conductors. - return evalR(cos_theta); - } - - // Avoid function was declared but never referenced - // float get_ior() const - // { - // return 0; // no transmission possible - // } -}; - struct MxGeneralizedSchlickParams : public MxMicrofacetBaseParams { Color3 reflection_tint; Color3 transmission_tint; @@ -323,9 +247,8 @@ using MxGeneralizedSchlick = MxMicrofacet; using MxGeneralizedSchlickOpaque = MxMicrofacet; -using MxDielectric = MxMicrofacet; -using MxDielectricOpaque = MxMicrofacet; struct MxConductor; +struct MxDielectric; struct Transparent; struct OrenNayar; @@ -345,9 +268,9 @@ using AbstractBSDF = bsdl::StaticVirtual< Diffuse<0>, Transparent, OrenNayar, Diffuse<1>, Phong, Ward, Reflection, Refraction, MicrofacetBeckmannRefl, MicrofacetBeckmannRefr, MicrofacetBeckmannBoth, MicrofacetGGXRefl, MicrofacetGGXRefr, - MicrofacetGGXBoth, MxConductor, MxDielectricOpaque, MxDielectric, - MxBurleyDiffuse, EnergyCompensatedOrenNayar, ZeltnerBurleySheen, - CharlieSheen, MxGeneralizedSchlickOpaque, MxGeneralizedSchlick, SpiThinLayer>; + MicrofacetGGXBoth, MxConductor, MxDielectric, MxBurleyDiffuse, + EnergyCompensatedOrenNayar, ZeltnerBurleySheen, CharlieSheen, + MxGeneralizedSchlickOpaque, MxGeneralizedSchlick, SpiThinLayer>; // Then we just need to inherit from AbstractBSDF diff --git a/testsuite/render-mx-dielectric-glass/ref/out-optix-alt.exr b/testsuite/render-mx-dielectric-glass/ref/out-optix-alt.exr index a123df0555..c918a8be17 100644 Binary files a/testsuite/render-mx-dielectric-glass/ref/out-optix-alt.exr and b/testsuite/render-mx-dielectric-glass/ref/out-optix-alt.exr differ diff --git a/testsuite/render-mx-dielectric-glass/ref/out.exr b/testsuite/render-mx-dielectric-glass/ref/out.exr index 9961a1b276..df2a39fff8 100644 Binary files a/testsuite/render-mx-dielectric-glass/ref/out.exr and b/testsuite/render-mx-dielectric-glass/ref/out.exr differ diff --git a/testsuite/render-mx-dielectric-glass/run.py b/testsuite/render-mx-dielectric-glass/run.py index 9cb1e8fd65..6a73679929 100755 --- a/testsuite/render-mx-dielectric-glass/run.py +++ b/testsuite/render-mx-dielectric-glass/run.py @@ -7,5 +7,7 @@ failthresh = 0.02 failpercent = 1 hardfail = 0.02 +allowfailures = 15 +idiff_program = "idiff" outputs = [ "out.exr" ] command = testrender("-v -r 320 240 -aa 16 scene.xml out.exr") diff --git a/testsuite/render-mx-dielectric-glass/scene.xml b/testsuite/render-mx-dielectric-glass/scene.xml index f632451b5c..a8aeae51d7 100644 --- a/testsuite/render-mx-dielectric-glass/scene.xml +++ b/testsuite/render-mx-dielectric-glass/scene.xml @@ -19,14 +19,14 @@ param color Cr 0.8 0.4 0.2; - float roughness 0.2; + float roughness 0.1; float anisotropy 0.0; shader glossy layer1; - float roughness 0.3; + float roughness 0.2; float anisotropy 0.5; float ior 1.8; shader glossy layer1; @@ -34,7 +34,7 @@ - float roughness 0.5; + float roughness 0.3; float anisotropy 0.25; float ior 3.0; shader glossy layer1; diff --git a/testsuite/render-mx-dielectric/glossy.osl b/testsuite/render-mx-dielectric/glossy.osl index 4f3914fe42..6c1c0d0354 100644 --- a/testsuite/render-mx-dielectric/glossy.osl +++ b/testsuite/render-mx-dielectric/glossy.osl @@ -13,6 +13,9 @@ glossy color Ct = 0.0 [[ string description = "Transmission tint", float UImin = 0, float UImax = 1 ]], + color Base = 1.0 + [[ string description = "Base color", + float UImin = 0, float UImax = 1 ]], float ior = 1.5 [[ string description = "Index of refraction", float UImin = 1.0, float UImax = 3.0 ]], @@ -28,5 +31,7 @@ glossy float alpha_x = alpha * (1.0 - anisotropy); float alpha_y = alpha; vector U = normalize(cross(N, dPdv)); - Ci = dielectric_bsdf(N, U, Cr, Ct, alpha_x, alpha_y, ior, "ggx"); + closure color under = Base * diffuse(N); + closure color over = dielectric_bsdf(N, U, Cr, Ct, alpha_x, alpha_y, ior, "ggx"); + Ci = layer(over, under); } diff --git a/testsuite/render-mx-dielectric/ref/out-optix-alt.exr b/testsuite/render-mx-dielectric/ref/out-optix-alt.exr index 527bdf88d4..f1c5740e04 100644 Binary files a/testsuite/render-mx-dielectric/ref/out-optix-alt.exr and b/testsuite/render-mx-dielectric/ref/out-optix-alt.exr differ diff --git a/testsuite/render-mx-dielectric/ref/out.exr b/testsuite/render-mx-dielectric/ref/out.exr index 1d5c2c2593..07e8da0131 100644 Binary files a/testsuite/render-mx-dielectric/ref/out.exr and b/testsuite/render-mx-dielectric/ref/out.exr differ diff --git a/testsuite/render-mx-dielectric/scene.xml b/testsuite/render-mx-dielectric/scene.xml index 7b6d6f8d88..d8b7870963 100644 --- a/testsuite/render-mx-dielectric/scene.xml +++ b/testsuite/render-mx-dielectric/scene.xml @@ -8,6 +8,7 @@ param color Cr 0.8 0.4 0.2; + param color Base 0.4 0.05 0.02; float roughness 0.2; float anisotropy 0.0; shader glossy layer1; @@ -15,6 +16,7 @@ + param color Base 0.02 0.05 0.4; float roughness 0.5; float anisotropy 0.5; float ior 1.8; @@ -23,6 +25,7 @@ + param color Base 0.02 0.4 0.05; float roughness 0.8; float anisotropy 0.25; float ior 3.0;