@@ -101,6 +101,7 @@ struct MxOrenNayarDiffuseParams {
101101 float roughness;
102102 // optional
103103 ustring label;
104+ int energy_compensation;
104105};
105106
106107struct MxBurleyDiffuseParams {
@@ -316,6 +317,8 @@ register_closures(OSL::ShadingSystem* shadingsys)
316317 CLOSURE_COLOR_PARAM (MxOrenNayarDiffuseParams, albedo),
317318 CLOSURE_FLOAT_PARAM (MxOrenNayarDiffuseParams, roughness),
318319 CLOSURE_STRING_KEYPARAM (MxOrenNayarDiffuseParams, label, " label" ),
320+ CLOSURE_INT_KEYPARAM (MxOrenNayarDiffuseParams, energy_compensation,
321+ " energy_compensation" ),
319322 CLOSURE_FINISH_PARAM (MxOrenNayarDiffuseParams) } },
320323 { " burley_diffuse_bsdf" ,
321324 MX_BURLEY_DIFFUSE_ID,
@@ -459,23 +462,24 @@ template<int trans> struct Diffuse final : public BSDF, DiffuseParams {
459462struct OrenNayar final : public BSDF, OrenNayarParams {
460463 OrenNayar (const OrenNayarParams& params) : BSDF(), OrenNayarParams(params)
461464 {
462- // precompute some constants
463- float s2 = sigma * sigma;
464- A = 1 - 0 .50f * s2 / (s2 + 0 .33f );
465- B = 0 .45f * s2 / (s2 + 0 .09f );
466465 }
467466 Sample eval (const Vec3& wo, const OSL::Vec3& wi) const override
468467 {
469468 float NL = N.dot (wi);
470469 float NV = N.dot (wo);
471470 if (NL > 0 && NV > 0 ) {
471+ float LV = wo.dot (wi);
472+ float s = LV - NL * NV;
472473 // Simplified math from: "A tiny improvement of Oren-Nayar reflectance model"
473474 // by Yasuhiro Fujii
474475 // http://mimosa-pudica.net/improved-oren-nayar.html
475- // NOTE: This is using the math to match the original ON model, not the tweak
476- // proposed in the text which is a slightly different BRDF
477- float LV = wo.dot (wi);
478- float s = LV - NL * NV;
476+ // NOTE: This is using the math to match the original qualitative ON model
477+ // (QON in the paper above) and not the tweak proposed in the text which
478+ // is a slightly different BRDF (FON in the paper above). This is done for
479+ // backwards compatibility purposes only.
480+ float s2 = sigma * sigma;
481+ float A = 1 - 0 .50f * s2 / (s2 + 0 .33f );
482+ float B = 0 .45f * s2 / (s2 + 0 .09f );
479483 float stinv = s > 0 ? s / std::max (NL, NV) : 0 .0f ;
480484 return { wi, Color3 (A + B * stinv), NL * float (M_1_PI), 1 .0f };
481485 }
@@ -489,9 +493,77 @@ struct OrenNayar final : public BSDF, OrenNayarParams {
489493 Sampling::sample_cosine_hemisphere (N, rx, ry, out_dir, pdf);
490494 return eval (wo, out_dir);
491495 }
496+ };
497+
498+ struct EnergyCompensatedOrenNayar : public BSDF , MxOrenNayarDiffuseParams {
499+ EnergyCompensatedOrenNayar (const MxOrenNayarDiffuseParams& params)
500+ : BSDF(), MxOrenNayarDiffuseParams(params)
501+ {
502+ }
503+ Sample eval (const Vec3& wo, const OSL::Vec3& wi) const override
504+ {
505+ float NL = N.dot (wi);
506+ float NV = N.dot (wo);
507+ if (NL > 0 && NV > 0 ) {
508+ float LV = wo.dot (wi);
509+ float s = LV - NL * NV;
510+ // Code below from Jamie Portsmouth's tech report on Energy conversion Oren-Nayar
511+ // See slack thread for whitepaper:
512+ // https://academysoftwarefdn.slack.com/files/U03SWQFPD08/F06S50CUKV1/oren_nayar.pdf
513+
514+ // TODO: rho should be the albedo which is a parameter of the closure in the Mx parameters
515+ // This only matters for the color-saturation aspect of the BRDF which is rather subtle anyway
516+ // and not always desireable for artists. Hardcoding to 1 leaves the coloring entirely up to the
517+ // closure weight.
518+
519+ const Color3 rho = albedo;
520+ const float sigma = roughness;
521+
522+ float AF = 1 .0f / (1 .0f + constant1_FON * sigma);
523+ float stinv = s > 0 ? s / std::max (NL, NV) : s;
524+ float f_ss = AF * (1.0 + sigma * stinv); // single-scatt. BRDF
525+ float EFo = E_FON_analytic (NV); // EFo at rho=1 (analytic)
526+ float EFi = E_FON_analytic (NL); // EFi at rho=1 (analytic)
527+ float avgEF = AF * (1 .0f + constant2_FON * sigma); // avg. albedo
528+ Color3 rho_ms = (rho * rho) * avgEF
529+ / (Color3 (1 .0f )
530+ - rho * std::max (0 .0f , 1 .0f - avgEF));
531+ float f_ms = std::max (1e-7f , 1 .0f - EFo)
532+ * std::max (1e-7f , 1 .0f - EFi)
533+ / std::max (1e-7f , 1 .0f - avgEF); // multi-scatter lobe
534+ return { wi, Color3 (rho * f_ss + rho_ms * f_ms), NL * float (M_1_PI),
535+ 1 .0f };
536+ }
537+ return {};
538+ }
539+
540+ Sample sample (const Vec3& wo, float rx, float ry,
541+ float /* rz*/ ) const override
542+ {
543+ Vec3 out_dir;
544+ float pdf;
545+ Sampling::sample_cosine_hemisphere (N, rx, ry, out_dir, pdf);
546+ return eval (wo, out_dir);
547+ }
492548
493549private:
494- float A, B;
550+ static constexpr float constant1_FON = float (0.5 - 2.0 / (3.0 * M_PI));
551+ static constexpr float constant2_FON = float (2.0 / 3.0
552+ - 28.0 / (15.0 * M_PI));
553+
554+ float E_FON_analytic (float mu) const
555+ {
556+ const float sigma = roughness;
557+ float AF = 1 .0f
558+ / (1 .0f
559+ + constant1_FON * sigma); // Fujii model A coefficient
560+ float BF = sigma * AF; // Fujii model B coefficient
561+ float Si = sqrtf (std::max (0 .0f , 1 .0f - mu * mu));
562+ float G = Si * (OIIO::fast_acos (mu) - Si * mu)
563+ + 2.0 * ((Si / mu) * (1.0 - Si * Si * Si) - Si) / 3 .0f ;
564+ float E = AF + (BF * float (M_1_PI)) * G;
565+ return E;
566+ }
495567};
496568
497569struct Phong final : public BSDF, PhongParams {
@@ -1615,14 +1687,20 @@ process_bsdf_closure(const OSL::ShaderGlobals& sg, ShadingResult& result,
16151687 ok = result.bsdf .add_bsdf <Transparent>(cw);
16161688 break ;
16171689 case MX_OREN_NAYAR_DIFFUSE_ID: {
1618- // translate MaterialX parameters into existing closure
16191690 const MxOrenNayarDiffuseParams* srcparams
16201691 = comp->as <MxOrenNayarDiffuseParams>();
1621- OrenNayarParams params = {};
1622- params.N = srcparams->N ;
1623- params.sigma = srcparams->roughness ;
1624- ok = result.bsdf .add_bsdf <OrenNayar>(cw * srcparams->albedo ,
1625- params);
1692+ if (srcparams->energy_compensation ) {
1693+ // energy compensation handled by its own BSDF
1694+ ok = result.bsdf .add_bsdf <EnergyCompensatedOrenNayar>(
1695+ cw, *srcparams);
1696+ } else {
1697+ // translate MaterialX parameters into existing closure
1698+ OrenNayarParams params = {};
1699+ params.N = srcparams->N ;
1700+ params.sigma = srcparams->roughness ;
1701+ ok = result.bsdf .add_bsdf <OrenNayar>(cw * srcparams->albedo ,
1702+ params);
1703+ }
16261704 break ;
16271705 }
16281706 case MX_BURLEY_DIFFUSE_ID: {
0 commit comments