Skip to content

Commit e6291a8

Browse files
acontylgritz
authored andcommitted
Move MaterialX sheen BSDF into BSDL (#2084)
Consolidate sheen implementation from testrender and SPI into a single BSDL-backed MaterialX lobe (`mtx::SheenLobe`), so sheen behavior is defined in one place and reused consistently across paths. - Move sheen logic into BSDL/MTX and wire it through the BSDL closure path - Replace ad-hoc testrender-side handling with the new BSDL implementation - Keep Conty/Zeltner mode selection inside the unified sheen lobe - Update sheen tests and references to validate the new path - Turn the furnace sheen test into a true energy-conservation check (sheen layered over white diffuse should converge to flat gray) AI Disclaimer: AI has been used to code review and bug hunt, but all the code and fixes have been written by huomans. Signed-off-by: Alejandro Conty <aconty@imageworks.com>
1 parent ba62821 commit e6291a8

30 files changed

+691
-634
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// Copyright Contributors to the Open Shading Language project.
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4+
5+
#pragma once
6+
7+
#include <BSDL/bsdf_decl.h>
8+
9+
BSDL_ENTER_NAMESPACE
10+
11+
namespace mtx {
12+
13+
template<typename BSDF_ROOT> struct SheenLobe : public Lobe<BSDF_ROOT> {
14+
using Base = Lobe<BSDF_ROOT>;
15+
16+
enum Mode { CONTY = 0, ZELTNER = 1 };
17+
18+
struct Data : public LayeredData {
19+
Imath::V3f N;
20+
Imath::C3f albedo;
21+
float roughness;
22+
int mode;
23+
Stringhash label;
24+
using lobe_type = SheenLobe<BSDF_ROOT>;
25+
};
26+
27+
template<typename D> static typename LobeRegistry<D>::Entry entry()
28+
{
29+
static_assert(
30+
std::is_base_of<Data, D>::value); // Make no other assumptions
31+
using R = LobeRegistry<D>;
32+
return { name(),
33+
{ R::param(&D::N), R::param(&D::albedo),
34+
R::param(&D::roughness), R::param(&D::label, "label"),
35+
R::param(&D::mode, "mode"), R::close() } };
36+
}
37+
38+
template<typename T>
39+
BSDL_INLINE_METHOD SheenLobe(T*, const BsdfGlobals& globals,
40+
const Data& data);
41+
42+
static constexpr const char* name() { return "sheen_bsdf"; }
43+
44+
BSDL_INLINE_METHOD Power albedo_impl() const { return Power(1 - Emiss, 1); }
45+
BSDL_INLINE_METHOD Power filter_o(const Imath::V3f& wo) const
46+
{
47+
return Power(Emiss, 1);
48+
}
49+
50+
BSDL_INLINE_METHOD Sample eval_impl(const Imath::V3f& wo,
51+
const Imath::V3f& wi) const;
52+
BSDL_INLINE_METHOD Sample sample_impl(const Imath::V3f& wo,
53+
const Imath::V3f& rnd) const;
54+
55+
private:
56+
BSDL_INLINE_METHOD bool use_zeltner() const
57+
{
58+
return sheen_mode == ZELTNER;
59+
}
60+
61+
Power tint;
62+
float sheen_alpha;
63+
float Emiss;
64+
int sheen_mode;
65+
bool is_backfacing;
66+
};
67+
68+
// SHD_LEGACY tells the distribution to use the shadowing term from
69+
// the original paper. Otherwise we use Dassault Systemes improvement.
70+
template<bool SHD_LEGACY> struct ContyKullaDist {
71+
static constexpr float MIN_ROUGHNESS = 0.06f;
72+
73+
BSDL_INLINE_METHOD ContyKullaDist(float rough)
74+
: a(CLAMP(rough, MIN_ROUGHNESS, 1.0f))
75+
{
76+
}
77+
78+
BSDL_INLINE_METHOD float D(const Imath::V3f& Hr) const;
79+
BSDL_INLINE_METHOD float get_lambda(float cosNO) const;
80+
BSDL_INLINE_METHOD float G2(const Imath::V3f& wo,
81+
const Imath::V3f& wi) const;
82+
BSDL_INLINE_METHOD float roughness() const { return a; }
83+
84+
private:
85+
float a;
86+
};
87+
88+
template<typename Dist> struct SheenMicrofacet {
89+
// describe how tabulation should be done
90+
static constexpr int Nc = 16;
91+
static constexpr int Nr = 16;
92+
static constexpr int Nf = 1;
93+
94+
static constexpr float get_cosine(int i)
95+
{
96+
return std::max(float(i) * (1.0f / (Nc - 1)), 1e-6f);
97+
}
98+
explicit BSDL_INLINE_METHOD SheenMicrofacet(float rough) : d(rough) {}
99+
BSDL_INLINE_METHOD Sample eval(const Imath::V3f& wo,
100+
const Imath::V3f& wi) const;
101+
BSDL_INLINE_METHOD Sample sample(const Imath::V3f& wo, float randu,
102+
float randv, float) const;
103+
BSDL_INLINE_METHOD float roughness() const { return d.roughness(); }
104+
105+
private:
106+
Dist d;
107+
};
108+
109+
template<bool SHD_LEGACY>
110+
struct ContyKullaSheenGen : public SheenMicrofacet<ContyKullaDist<SHD_LEGACY>> {
111+
using SheenMicrofacet<ContyKullaDist<SHD_LEGACY>>::SheenMicrofacet;
112+
};
113+
114+
struct ContyKullaSheen : public ContyKullaSheenGen<true> {
115+
using ContyKullaSheenGen<true>::ContyKullaSheenGen;
116+
explicit BSDL_INLINE_METHOD ContyKullaSheen(float, float rough, float)
117+
: ContyKullaSheenGen(rough)
118+
{
119+
}
120+
BSDL_INLINE_METHOD float albedo(float cosNO) const;
121+
struct Energy {
122+
float data[Nf * Nr * Nc];
123+
};
124+
static BSDL_INLINE_METHOD Energy& get_energy();
125+
126+
static constexpr const char* NS = "mtx";
127+
static const char* lut_header() { return "MTX/bsdf_contysheen_luts.h"; }
128+
static const char* struct_name() { return "ContyKullaSheen"; }
129+
};
130+
131+
struct ContyKullaSheenMTX : public ContyKullaSheenGen<false> {
132+
using ContyKullaSheenGen<false>::ContyKullaSheenGen;
133+
BSDL_INLINE_METHOD float albedo(float cosNO) const;
134+
};
135+
136+
struct ZeltnerBurleySheen {
137+
#if BAKE_BSDL_TABLES
138+
// Use uniform sampling, more reliable
139+
static constexpr bool LTC_SAMPLING = false;
140+
#else
141+
static constexpr bool LTC_SAMPLING = true;
142+
#endif
143+
// Skip albedo tables, use LTC coefficents. Disabling is useful for validation
144+
static constexpr bool LTC_ALBEDO = true;
145+
// This flattens the look a bit, so leaving it disabled for now
146+
static constexpr bool FITTED_LTC = false;
147+
148+
// LTC sampling is weak at low roughness, gains energy, so we clamp it.
149+
static constexpr float MIN_ROUGHNESS = LTC_SAMPLING ? 0.02f : 0.0f;
150+
151+
// describe how tabulation should be done
152+
static constexpr int Nc = 16;
153+
static constexpr int Nr = 16;
154+
static constexpr int Nf = 1;
155+
static constexpr int ltc_res = 32;
156+
157+
explicit BSDL_INLINE_METHOD ZeltnerBurleySheen(float rough)
158+
: roughness(CLAMP(rough, MIN_ROUGHNESS, 1.0f))
159+
{
160+
}
161+
// This constructor is just for baking albedo tables
162+
explicit ZeltnerBurleySheen(float, float rough, float) : roughness(rough) {}
163+
164+
static constexpr float get_cosine(int i)
165+
{
166+
return std::max(float(i) * (1.0f / (Nc - 1)), 1e-6f);
167+
}
168+
169+
BSDL_INLINE_METHOD Sample eval(Imath::V3f wo, Imath::V3f wi) const;
170+
BSDL_INLINE_METHOD Sample eval(Imath::V3f wo, Imath::V3f wi,
171+
Imath::V3f ltc) const;
172+
BSDL_INLINE_METHOD Sample sample(Imath::V3f wo, float randu, float randv,
173+
float randw) const;
174+
BSDL_INLINE_METHOD float albedo(float cosNO) const;
175+
176+
struct Energy {
177+
float data[Nf * Nr * Nc];
178+
};
179+
struct Param {
180+
Imath::V3f data[32][32];
181+
};
182+
183+
static BSDL_INLINE_METHOD Energy& get_energy();
184+
185+
typedef const Imath::V3f (*V32_array)[32];
186+
static BSDL_INLINE_METHOD V32_array param_ptr();
187+
188+
static constexpr const char* NS = "mtx";
189+
static const char* lut_header() { return "MTX/bsdf_zeltnersheen_luts.h"; }
190+
static const char* struct_name() { return "ZeltnerBurleySheen"; }
191+
192+
// Fetch the LTC coefficients by bilinearly interpolating entries in a 32x32
193+
// lookup table or using a fit.
194+
BSDL_INLINE_METHOD Imath::V3f fetch_coeffs(float cosNO) const;
195+
196+
private:
197+
float roughness;
198+
};
199+
200+
} // namespace mtx
201+
202+
BSDL_LEAVE_NAMESPACE

0 commit comments

Comments
 (0)