Skip to content

Commit a76c42d

Browse files
authored
Add BSDF for MaterialX dielectric with Multiple Scattering (#2009)
This moves the dielectric implementation to BSDL and adds energy conservation via Turquin style multiple scattering. That is, we create a microfacet based reflection refraction model where we precumpute the energy loss to a lookup table. Then at render time we renormalize the lobe to remove the energy loss. In addition we use Bounded Visible Normal sampling to avoid rejecting reflection samples. This improves noise significantly at the cost of losing valid refracted directions. This doesn't produce energy loss thanks to the renormalization, but biases the the refraction for high roughness and low IOR. The result is still plausible and very hard to see which one is correct. Also keeping the ability to layer a reflection only coating on top of other BSDFs via layer(). Signed-off-by: Alejandro Conty <[email protected]>
1 parent d6e4979 commit a76c42d

29 files changed

+755
-178
lines changed

src/libbsdl/bsdl.cmake

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ function(ADD_BSDL_LIBRARY NAME)
1616
add_executable (genluts ${CMAKE_CURRENT_SOURCE_DIR}/${bsdl_SUBDIR}/src/genluts.cpp)
1717
target_link_libraries(genluts PRIVATE BSDL_BOOTSTRAP Threads::Threads)
1818
file(MAKE_DIRECTORY ${BSDL_GEN_HEADERS}/BSDL/SPI)
19-
add_custom_command(TARGET genluts POST_BUILD USES_TERMINAL COMMAND $<TARGET_FILE:genluts> ${BSDL_GEN_HEADERS}/BSDL/SPI
19+
file(MAKE_DIRECTORY ${BSDL_GEN_HEADERS}/BSDL/MTX)
20+
add_custom_command(TARGET genluts POST_BUILD USES_TERMINAL COMMAND $<TARGET_FILE:genluts> ${BSDL_GEN_HEADERS}/BSDL
2021
COMMENT "Generating BSDL lookup tables ...")
2122

2223
if (DEFINED bsdl_SPECTRAL_COLOR_SPACES)

src/libbsdl/include/BSDL/MTX/bsdf_conductor_decl.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ template<typename BSDF_ROOT> struct ConductorLobe : public Lobe<BSDF_ROOT> {
3636
// fresnel params
3737
Imath::C3f IOR;
3838
Imath::C3f extinction;
39-
const char* distribution;
39+
Stringhash distribution;
40+
float thinfilm_thickness;
41+
float thinfilm_ior;
4042
using lobe_type = ConductorLobe<BSDF_ROOT>;
4143
};
4244
template<typename D> static typename LobeRegistry<D>::Entry entry()
@@ -48,7 +50,8 @@ template<typename BSDF_ROOT> struct ConductorLobe : public Lobe<BSDF_ROOT> {
4850
{ R::param(&D::N), R::param(&D::U), R::param(&D::roughness_x),
4951
R::param(&D::roughness_y), R::param(&D::IOR),
5052
R::param(&D::extinction), R::param(&D::distribution),
51-
R::close() } };
53+
R::param(&D::thinfilm_thickness, "thinfilm_thickness"),
54+
R::param(&D::thinfilm_ior, "thinfilm_ior"), R::close() } };
5255
}
5356

5457
template<typename T>
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
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/SPI/bsdf_dielectric_decl.h>
9+
#include <BSDL/bsdf_decl.h>
10+
#include <BSDL/microfacet_tools_decl.h>
11+
12+
BSDL_ENTER_NAMESPACE
13+
14+
namespace mtx {
15+
16+
struct DielectricFresnel {
17+
DielectricFresnel() = default;
18+
BSDL_INLINE_METHOD DielectricFresnel(float _eta, bool backside);
19+
BSDL_INLINE_METHOD Power eval(const float c) const;
20+
static BSDL_INLINE_METHOD DielectricFresnel from_table_index(float tx,
21+
bool backside);
22+
BSDL_INLINE_METHOD float table_index() const;
23+
24+
float eta;
25+
26+
static constexpr float IOR_MIN = 1.001f;
27+
static constexpr float IOR_MAX = 5.0f;
28+
};
29+
30+
struct DielectricRefl {
31+
// describe how tabulation should be done
32+
static constexpr int Nc = 16;
33+
static constexpr int Nr = 16;
34+
static constexpr int Nf = 32;
35+
36+
static constexpr float get_cosine(int i)
37+
{
38+
return std::max(float(i) * (1.0f / (Nc - 1)), 1e-6f);
39+
}
40+
41+
explicit BSDL_INLINE_METHOD
42+
DielectricRefl(float cosNO, float roughness_index, float fresnel_index);
43+
44+
BSDL_INLINE_METHOD
45+
DielectricRefl(const GGXDist& dist, const DielectricFresnel& fresnel,
46+
float cosNO, float roughness);
47+
48+
DielectricRefl() = default;
49+
50+
BSDL_INLINE_METHOD Sample eval(Imath::V3f wo, Imath::V3f wi) const;
51+
BSDL_INLINE_METHOD Sample sample(Imath::V3f wo, float randu, float randv,
52+
float randw) const;
53+
54+
BSDL_INLINE_METHOD DielectricFresnel fresnel() const { return f; }
55+
56+
struct Energy {
57+
float data[Nf * Nr * Nc];
58+
};
59+
static constexpr const char* NS = "mtx";
60+
61+
protected:
62+
GGXDist d;
63+
DielectricFresnel f;
64+
float E_ms; // No fresnel here
65+
};
66+
67+
struct DielectricReflFront : public DielectricRefl {
68+
DielectricReflFront() = default;
69+
explicit BSDL_INLINE_METHOD DielectricReflFront(float cosNO,
70+
float roughness_index,
71+
float fresnel_index);
72+
static BSDL_INLINE_METHOD Energy& get_energy();
73+
static const char* lut_header()
74+
{
75+
return "MTX/bsdf_dielectric_reflfront_luts.h";
76+
}
77+
static const char* struct_name() { return "DielectricReflFront"; }
78+
};
79+
80+
struct DielectricBoth {
81+
static constexpr int Nc = 16;
82+
static constexpr int Nr = 16;
83+
static constexpr int Nf = 32;
84+
85+
static constexpr float get_cosine(int i)
86+
{
87+
return std::max(float(i) * (1.0f / (Nc - 1)), 1e-6f);
88+
}
89+
90+
DielectricBoth() = default;
91+
explicit BSDL_INLINE_METHOD DielectricBoth(float cosNO,
92+
float roughness_index,
93+
float fresnel_index,
94+
bool backside);
95+
BSDL_INLINE_METHOD
96+
DielectricBoth(const GGXDist& dist, const DielectricFresnel& fresnel);
97+
98+
BSDL_INLINE_METHOD DielectricFresnel fresnel() const { return f; }
99+
100+
BSDL_INLINE_METHOD Sample eval(Imath::V3f wo, Imath::V3f wi) const;
101+
BSDL_INLINE_METHOD Sample sample(Imath::V3f wo, float randu, float randv,
102+
float randw) const;
103+
104+
struct Energy {
105+
float data[Nf * Nr * Nc];
106+
};
107+
static constexpr const char* NS = "mtx";
108+
109+
protected:
110+
GGXDist d;
111+
DielectricFresnel f;
112+
};
113+
114+
struct DielectricBothFront : public DielectricBoth {
115+
DielectricBothFront() = default;
116+
explicit BSDL_INLINE_METHOD DielectricBothFront(float cosNO,
117+
float roughness_index,
118+
float fresnel_index);
119+
static BSDL_INLINE_METHOD Energy& get_energy();
120+
static const char* lut_header()
121+
{
122+
return "MTX/bsdf_dielectric_bothfront_luts.h";
123+
}
124+
static const char* struct_name() { return "DielectricBothFront"; }
125+
};
126+
127+
struct DielectricBothBack : public DielectricBoth {
128+
DielectricBothBack() = default;
129+
explicit BSDL_INLINE_METHOD
130+
DielectricBothBack(float cosNO, float roughness_index, float fresnel_index);
131+
static BSDL_INLINE_METHOD Energy& get_energy();
132+
133+
static const char* lut_header()
134+
{
135+
return "MTX/bsdf_dielectric_bothback_luts.h";
136+
}
137+
static const char* struct_name() { return "DielectricBothBack"; }
138+
};
139+
140+
template<typename BSDF_ROOT> struct DielectricLobe : public Lobe<BSDF_ROOT> {
141+
using Base = Lobe<BSDF_ROOT>;
142+
struct Data : public LayeredData {
143+
Imath::V3f N;
144+
Imath::V3f U;
145+
Imath::C3f refl_tint;
146+
Imath::C3f refr_tint;
147+
float roughness_x;
148+
float roughness_y;
149+
float IOR;
150+
Stringhash distribution;
151+
float thinfilm_thickness;
152+
float thinfilm_ior;
153+
float dispersion;
154+
Imath::C3f absorption;
155+
using lobe_type = DielectricLobe<BSDF_ROOT>;
156+
};
157+
template<typename D> static typename LobeRegistry<D>::Entry entry()
158+
{
159+
static_assert(
160+
std::is_base_of<Data, D>::value); // Make no other assumptions
161+
using R = LobeRegistry<D>;
162+
return { name(),
163+
{ R::param(&D::N), R::param(&D::U), R::param(&D::refl_tint),
164+
R::param(&D::refr_tint), R::param(&D::roughness_x),
165+
R::param(&D::roughness_y), R::param(&D::IOR),
166+
R::param(&D::distribution),
167+
R::param(&D::thinfilm_thickness, "thinfilm_thickness"),
168+
R::param(&D::thinfilm_ior, "thinfilm_ior"),
169+
R::param(&D::absorption, "absorption"),
170+
R::param(&D::dispersion, "dispersion"), R::close() } };
171+
}
172+
173+
template<typename T>
174+
BSDL_INLINE_METHOD DielectricLobe(T*, const BsdfGlobals& globals,
175+
const Data& data);
176+
177+
static constexpr const char* name() { return "dielectric_bsdf"; }
178+
179+
BSDL_INLINE_METHOD Sample eval_impl(const Imath::V3f& wo,
180+
const Imath::V3f& wi) const;
181+
BSDL_INLINE_METHOD Sample sample_impl(const Imath::V3f& wo,
182+
const Imath::V3f& rnd) const;
183+
184+
BSDL_INLINE_METHOD Power filter_o(const Imath::V3f& wo) const
185+
{
186+
// wo is the same as when constructed, Eo is cached
187+
return !dorefr ? E_ms * wo_absorption : Power::ZERO();
188+
}
189+
190+
BSDL_INLINE_METHOD bool single_wavelength() const { return dispersion; }
191+
192+
protected:
193+
BSDL_INLINE_METHOD Power get_tint(float cosNI) const;
194+
195+
union {
196+
DielectricRefl refl;
197+
DielectricBoth both;
198+
} spec;
199+
Power refl_tint;
200+
Power refr_tint;
201+
Power wo_absorption;
202+
float E_ms;
203+
bool dorefl;
204+
bool dorefr;
205+
bool dispersion;
206+
};
207+
208+
} // namespace mtx
209+
210+
BSDL_LEAVE_NAMESPACE

0 commit comments

Comments
 (0)