|
| 1 | +// Copyright Contributors to the Open Shading Language project. |
| 2 | +// SPDX-License-Identifier: BSD-3-Clause |
| 3 | +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage |
| 4 | + |
| 5 | + |
| 6 | +#pragma once |
| 7 | + |
| 8 | +#include <BSDL/MTX/bsdf_oren_nayar_diffuse_decl.h> |
| 9 | + |
| 10 | +BSDL_ENTER_NAMESPACE |
| 11 | + |
| 12 | +namespace mtx { |
| 13 | + |
| 14 | +template<typename BSDF_ROOT> |
| 15 | +template<typename T> |
| 16 | +BSDL_INLINE_METHOD |
| 17 | +OrenNayarDiffuseLobe<BSDF_ROOT>::OrenNayarDiffuseLobe( |
| 18 | + T* lobe, const BsdfGlobals& globals, const Data& data) |
| 19 | + : Base(lobe, globals.visible_normal(data.N), 1.0f, globals.lambda_0, false) |
| 20 | + , diff_albedo(globals.wave(data.albedo)) |
| 21 | + , diff_roughness(CLAMP(data.roughness, 0.0f, 1.0f)) |
| 22 | + , do_energy_compensation(data.energy_compensation != 0) |
| 23 | +{ |
| 24 | + Base::sample_filter = globals.get_sample_filter(Base::frame.Z, true); |
| 25 | +} |
| 26 | + |
| 27 | +template<typename BSDF_ROOT> |
| 28 | +BSDL_INLINE_METHOD float |
| 29 | +OrenNayarDiffuseLobe<BSDF_ROOT>::E_FON_analytic(float mu) const |
| 30 | +{ |
| 31 | + const float sigma = diff_roughness; |
| 32 | + const float AF = 1.0f / (1.0f + constant1_FON * sigma); |
| 33 | + const float BF = sigma * AF; |
| 34 | + const float Si = sqrtf(std::max(0.0f, 1.0f - mu * mu)); |
| 35 | + const float G |
| 36 | + = Si * (BSDLConfig::Fast::acosf(mu) - Si * mu) |
| 37 | + + 2.0f * ((Si / std::max(mu, 1e-7f)) * (1.0f - Si * Si * Si) - Si) |
| 38 | + * (1.0f / 3.0f); |
| 39 | + return AF + (BF * ONEOVERPI) * G; |
| 40 | +} |
| 41 | + |
| 42 | +template<typename BSDF_ROOT> |
| 43 | +BSDL_INLINE_METHOD Sample |
| 44 | +OrenNayarDiffuseLobe<BSDF_ROOT>::eval_impl(const Imath::V3f& wo, |
| 45 | + const Imath::V3f& wi) const |
| 46 | +{ |
| 47 | + const float cosNI = CLAMP(wi.z, 0.0f, 1.0f); |
| 48 | + const float cosNO = CLAMP(wo.z, 0.0f, 1.0f); |
| 49 | + if (cosNI <= 0.0f || cosNO <= 0.0f) |
| 50 | + return {}; |
| 51 | + |
| 52 | + const float cosIO = CLAMP(wo.dot(wi), -1.0f, 1.0f); |
| 53 | + const float s = cosIO - cosNI * cosNO; |
| 54 | + const float pdf = cosNI * ONEOVERPI; |
| 55 | + |
| 56 | + if (!do_energy_compensation) { |
| 57 | + // Simplified math from: "A tiny improvement of Oren-Nayar reflectance model" |
| 58 | + // by Yasuhiro Fujii |
| 59 | + // http://mimosa-pudica.net/improved-oren-nayar.html |
| 60 | + // NOTE: This is using the math to match the original qualitative ON model |
| 61 | + // (QON in the paper above) and not the tweak proposed in the text which |
| 62 | + // is a slightly different BRDF (FON in the paper above). This is done for |
| 63 | + // backwards compatibility purposes only. |
| 64 | + const float s2 = SQR(diff_roughness); |
| 65 | + const float A = 1.0f - 0.50f * s2 / (s2 + 0.33f); |
| 66 | + const float B = 0.45f * s2 / (s2 + 0.09f); |
| 67 | + const float stinv = s > 0.0f ? s / std::max(cosNI, cosNO) : 0.0f; |
| 68 | + const float f_ss = A + B * stinv; |
| 69 | + return { wi, diff_albedo * f_ss, pdf, 1.0f }; |
| 70 | + } else { |
| 71 | + // Code below from Jamie Portsmouth's tech report on Energy conversion Oren-Nayar |
| 72 | + // See slack thread for whitepaper: |
| 73 | + // https://academysoftwarefdn.slack.com/files/U03SWQFPD08/F06S50CUKV1/oren_nayar.pdf |
| 74 | + // rho should be the albedo which is a parameter of the closure in the Mx parameters |
| 75 | + // This only matters for the color-saturation aspect of the BRDF which is rather subtle anyway |
| 76 | + // and not always desireable for artists. Hardcoding to 1 leaves the coloring entirely up to the |
| 77 | + // closure weight. |
| 78 | + const float AF = 1.0f / (1.0f + constant1_FON * diff_roughness); |
| 79 | + const float stinv = s > 0.0f ? s / std::max(cosNI, cosNO) : s; |
| 80 | + const float f_ss = AF * (1.0f + diff_roughness * stinv); |
| 81 | + const float EFo = E_FON_analytic(cosNO); |
| 82 | + const float EFi = E_FON_analytic(cosNI); |
| 83 | + const float avgEF = AF * (1.0f + constant2_FON * diff_roughness); |
| 84 | + |
| 85 | + const Power rho_ms( |
| 86 | + [&](int i) { |
| 87 | + return SQR(diff_albedo[i]) * avgEF |
| 88 | + / (1 - diff_albedo[i] * std::max(0.0f, 1.0f - avgEF)); |
| 89 | + }, |
| 90 | + 1); |
| 91 | + const float f_ms = std::max(1e-7f, 1.0f - EFo) |
| 92 | + * std::max(1e-7f, 1.0f - EFi) |
| 93 | + / std::max(1e-7f, 1.0f - avgEF); |
| 94 | + |
| 95 | + return { wi, diff_albedo * f_ss + rho_ms * f_ms, pdf, 1.0f }; |
| 96 | + } |
| 97 | +} |
| 98 | + |
| 99 | +template<typename BSDF_ROOT> |
| 100 | +BSDL_INLINE_METHOD Sample |
| 101 | +OrenNayarDiffuseLobe<BSDF_ROOT>::sample_impl(const Imath::V3f& wo, |
| 102 | + const Imath::V3f& rnd) const |
| 103 | +{ |
| 104 | + if (wo.z <= 0.0f) |
| 105 | + return {}; |
| 106 | + |
| 107 | + const Imath::V3f wi = sample_cos_hemisphere(rnd.x, rnd.y); |
| 108 | + return eval_impl(wo, wi); |
| 109 | +} |
| 110 | + |
| 111 | +} // namespace mtx |
| 112 | + |
| 113 | +BSDL_LEAVE_NAMESPACE |
0 commit comments