Skip to content

Commit 64f56b6

Browse files
committed
Add testrender volumes
Alongside the rest of the initialization for subpixel_radiance, an empty MediumStack is initialized. Its behavior is conceptually similar to the existing CompositeBSDF. Media are stored in a statically allocated char pool, with pointers maintained in mediums[MaxMediums]. In processing medium closures, either HomogeneousMedium or EmptyMedium are inserted into the medium stack. There is a simple priority handling scheme that will shuffle the incumbent medium pointers to give high priority mediums lower indices. They both inherit from a shared CRTP interface, Medium. When processing BSDF closures, intersections with MxDielectric or MxGeneralizedSchlick may only be added when priority < current_priority or are both 0 (the precious priority). Attached to the MediumStack, an integrate method transmittance samples each medium and scatters using the phase function of the highest priority medium. As of now, only HenyeyGreenstein is implemented and inherits from BSDF. Signed-off-by: Owen O'Malley <[email protected]>
1 parent d4e959a commit 64f56b6

37 files changed

Lines changed: 1024 additions & 58 deletions

File tree

src/cmake/testing.cmake

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ macro (osl_add_all_tests)
362362
render-cornell
363363
render-displacement
364364
render-furnace-diffuse
365+
render-mx-anisotropic-vdf
365366
render-mx-furnace-burley-diffuse
366367
render-mx-furnace-oren-nayar
367368
render-mx-furnace-sheen
@@ -371,7 +372,9 @@ macro (osl_add_all_tests)
371372
render-mx-generalized-schlick render-mx-generalized-schlick-glass
372373
render-mx-layer
373374
render-mx-sheen
374-
render-microfacet render-oren-nayar
375+
render-mx-medium-vdf
376+
render-mx-medium-vdf-glass
377+
render-microfacet render-oren-nayar
375378
render-spi-thinlayer
376379
render-uv render-veachmis render-ward
377380
render-raytypes

src/testrender/shading.cpp

Lines changed: 241 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,127 @@ struct ZeltnerBurleySheen final : public BSDF, MxSheenParams {
15131513
}
15141514
};
15151515

1516+
1517+
struct HenyeyGreenstein final : public BSDF {
1518+
const float g;
1519+
OSL_HOSTDEVICE HenyeyGreenstein(float g) : BSDF(this), g(g) {}
1520+
1521+
static OSL_HOSTDEVICE float PhaseHG(float cos_theta, float g)
1522+
{
1523+
const float denom = 1 + g * g + 2 * g * cos_theta;
1524+
return (1 - g * g) / (4 * M_PI * denom * sqrtf(denom));
1525+
}
1526+
1527+
OSL_HOSTDEVICE Sample eval(const Vec3& wo, const Vec3& wi) const
1528+
{
1529+
const float pdf = PhaseHG(dot(wo, wi), g);
1530+
return { wi, Color3(pdf), pdf, 0.0f };
1531+
}
1532+
1533+
OSL_HOSTDEVICE Sample sample(const Vec3& wo, float rx, float ry,
1534+
float rz) const
1535+
{
1536+
TangentFrame frame = TangentFrame::from_normal(wo);
1537+
1538+
float cos_theta;
1539+
if (abs(g) < 1e-3f) {
1540+
cos_theta = 1.0f - 2.0f * rx;
1541+
} else {
1542+
float sqr_term = (1 - g * g) / (1 - g + 2 * g * rx);
1543+
cos_theta = (1 + g * g - sqr_term * sqr_term) / (2 * g);
1544+
cos_theta = OIIO::clamp(cos_theta, -1.0f, 1.0f);
1545+
}
1546+
1547+
float sin_theta = sqrtf(
1548+
OIIO::clamp(1.0f - cos_theta * cos_theta, 0.0f, 1.0f));
1549+
float phi = 2 * M_PI * ry;
1550+
Vec3 local_wi = Vec3(sin_theta * cosf(phi), sin_theta * sinf(phi),
1551+
cos_theta);
1552+
1553+
Vec3 wi = frame.toworld(local_wi);
1554+
float pdf_val = PhaseHG(cos_theta, g);
1555+
1556+
return { wi, Color3(1.0f), pdf_val, 0.0f };
1557+
}
1558+
};
1559+
1560+
struct HomogeneousMedium final : public Medium {
1561+
MediumParams params;
1562+
HenyeyGreenstein phase_func;
1563+
1564+
OSL_HOSTDEVICE HomogeneousMedium(const MediumParams& params)
1565+
: Medium(this), params(params), phase_func(params.medium_g)
1566+
{
1567+
}
1568+
1569+
OSL_HOSTDEVICE Medium::Sample sample(Ray& r, Sampler& sampler,
1570+
Intersection& hit) const
1571+
{
1572+
Vec3 rand_vol = sampler.get();
1573+
1574+
float t_volume = -logf(1.0f - rand_vol.x) / params.avg_sigma_t();
1575+
1576+
Color3 weight;
1577+
Color3 tr;
1578+
1579+
if (t_volume < hit.t) {
1580+
r.origin = r.point(t_volume);
1581+
tr = transmittance(t_volume);
1582+
1583+
Color3 albedo = params.sigma_s / params.sigma_t;
1584+
1585+
weight = albedo / tr;
1586+
} else {
1587+
tr = transmittance(hit.t);
1588+
weight = Color3(1.0 / tr.x, 1.0 / tr.y, 1.0 / tr.z);
1589+
}
1590+
1591+
return Medium::Sample { t_volume, tr, weight };
1592+
}
1593+
1594+
OSL_HOSTDEVICE BSDF::Sample sample_phase_func(const Vec3& wo, float rx,
1595+
float ry,
1596+
float rz) const
1597+
{
1598+
return phase_func.sample(wo, rx, ry, rz);
1599+
}
1600+
1601+
OSL_HOSTDEVICE const MediumParams* get_params() const { return &params; }
1602+
1603+
OSL_HOSTDEVICE Color3 transmittance(float distance) const
1604+
{ // Beer-Lambert law
1605+
return Color3(expf(-params.sigma_t.x * distance),
1606+
expf(-params.sigma_t.y * distance),
1607+
expf(-params.sigma_t.z * distance));
1608+
}
1609+
};
1610+
1611+
struct EmptyMedium final : public Medium {
1612+
MediumParams params;
1613+
1614+
OSL_HOSTDEVICE EmptyMedium(const MediumParams& params)
1615+
: Medium(this), params(params)
1616+
{
1617+
}
1618+
1619+
OSL_HOSTDEVICE const MediumParams* get_params() const { return &params; }
1620+
1621+
OSL_HOSTDEVICE Medium::Sample sample(Ray& ray, Sampler& sampler,
1622+
Intersection& hit) const
1623+
{
1624+
return { 0.0f, Color3(1.0f), Color3(1.0f) };
1625+
}
1626+
1627+
OSL_HOSTDEVICE BSDF::Sample sample_phase_func(const Vec3& wo, float rx,
1628+
float ry,
1629+
float rz) const
1630+
{
1631+
return { Vec3(1.0f), Color3(1.0f), 0.0f, 0.0f };
1632+
}
1633+
1634+
};
1635+
1636+
15161637
OSL_HOSTDEVICE Color3
15171638
evaluate_layer_opacity(const ShaderGlobalsType& sg, float path_roughness,
15181639
const ClosureColor* closure)
@@ -1606,8 +1727,8 @@ evaluate_layer_opacity(const ShaderGlobalsType& sg, float path_roughness,
16061727

16071728
OSL_HOSTDEVICE void
16081729
process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
1609-
ShadingResult& result, const ClosureColor* closure,
1610-
const Color3& w)
1730+
ShadingResult& result, MediumStack& medium_stack,
1731+
const ClosureColor* closure, const Color3& w)
16111732
{
16121733
if (!closure)
16131734
return;
@@ -1649,37 +1770,72 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
16491770
const ClosureComponent* comp = closure->as_comp();
16501771
Color3 cw = weight * comp->w;
16511772
const auto& params = *comp->as<MxAnisotropicVdfParams>();
1652-
result.sigma_t = cw * params.extinction;
1653-
result.sigma_s = params.albedo * result.sigma_t;
1654-
result.medium_g = params.anisotropy;
1655-
closure = nullptr;
1773+
result.medium_data.sigma_t = cw * params.extinction;
1774+
result.medium_data.sigma_s = params.albedo
1775+
* result.medium_data.sigma_t;
1776+
result.medium_data.medium_g = params.anisotropy;
1777+
result.medium_data.priority = 0;
1778+
1779+
if (!sg.backfacing) { // if entering
1780+
if (result.medium_data.is_vaccum()) {
1781+
medium_stack.add_medium<EmptyMedium>(result.medium_data);
1782+
} else {
1783+
medium_stack.add_medium<HomogeneousMedium>(
1784+
result.medium_data);
1785+
}
1786+
}
1787+
1788+
closure = nullptr;
16561789
break;
16571790
}
16581791
case MX_MEDIUM_VDF_ID: {
16591792
const ClosureComponent* comp = closure->as_comp();
16601793
Color3 cw = weight * comp->w;
16611794
const auto& params = *comp->as<MxMediumVdfParams>();
1662-
result.sigma_t = { -OIIO::fast_log(params.transmission_color.x),
1663-
-OIIO::fast_log(params.transmission_color.y),
1664-
-OIIO::fast_log(params.transmission_color.z) };
1665-
// NOTE: closure weight scales the extinction parameter
1666-
result.sigma_t *= cw / params.transmission_depth;
1667-
result.sigma_s = params.albedo * result.sigma_t;
1668-
result.medium_g = params.anisotropy;
1669-
// TODO: properly track a medium stack here ...
1670-
result.refraction_ior = sg.backfacing ? 1.0f / params.ior
1671-
: params.ior;
1672-
result.priority = params.priority;
1673-
closure = nullptr;
1795+
1796+
result.medium_data.sigma_t
1797+
= Color3(-OIIO::fast_log(params.transmission_color.x),
1798+
-OIIO::fast_log(params.transmission_color.y),
1799+
-OIIO::fast_log(params.transmission_color.z));
1800+
1801+
result.medium_data.sigma_t *= cw / params.transmission_depth;
1802+
result.medium_data.sigma_s = params.albedo
1803+
* result.medium_data.sigma_t;
1804+
result.medium_data.medium_g = params.anisotropy;
1805+
1806+
result.medium_data.refraction_ior = sg.backfacing
1807+
? 1.0f / params.ior
1808+
: params.ior;
1809+
result.medium_data.priority = params.priority;
1810+
1811+
if (!sg.backfacing) { // if entering
1812+
if (result.medium_data.is_vaccum()) {
1813+
medium_stack.add_medium<EmptyMedium>(result.medium_data);
1814+
} else {
1815+
medium_stack.add_medium<HomogeneousMedium>(
1816+
result.medium_data);
1817+
}
1818+
}
1819+
1820+
closure = nullptr;
16741821
break;
16751822
}
16761823
case MxDielectric::closureid(): {
16771824
const ClosureComponent* comp = closure->as_comp();
16781825
const MxDielectric::Data& params = *comp->as<MxDielectric::Data>();
16791826
if (!is_black(weight * comp->w * params.refr_tint)) {
1680-
// TODO: properly track a medium stack here ...
1681-
result.refraction_ior = sg.backfacing ? 1.0f / params.IOR
1682-
: params.IOR;
1827+
float new_ior = sg.backfacing ? 1.0f / params.IOR : params.IOR;
1828+
1829+
result.medium_data.refraction_ior = new_ior;
1830+
1831+
const MediumParams* current_params
1832+
= medium_stack.current_params();
1833+
if (current_params
1834+
&& result.medium_data.priority
1835+
<= current_params->priority) {
1836+
result.medium_data.refraction_ior
1837+
= current_params->refraction_ior;
1838+
}
16831839
}
16841840
closure = nullptr;
16851841
break;
@@ -1688,13 +1844,23 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
16881844
const ClosureComponent* comp = closure->as_comp();
16891845
const auto& params = *comp->as<MxGeneralizedSchlickParams>();
16901846
if (!is_black(weight * comp->w * params.transmission_tint)) {
1691-
// TODO: properly track a medium stack here ...
16921847
float avg_F0 = clamp((params.f0.x + params.f0.y + params.f0.z)
16931848
/ 3.0f,
16941849
0.0f, 0.99f);
16951850
float sqrt_F0 = sqrtf(avg_F0);
16961851
float ior = (1 + sqrt_F0) / (1 - sqrt_F0);
1697-
result.refraction_ior = sg.backfacing ? 1.0f / ior : ior;
1852+
float new_ior = sg.backfacing ? 1.0f / ior : ior;
1853+
1854+
result.medium_data.refraction_ior = new_ior;
1855+
1856+
const MediumParams* current_params
1857+
= medium_stack.current_params();
1858+
if (current_params
1859+
&& result.medium_data.priority
1860+
<= current_params->priority) {
1861+
result.medium_data.refraction_ior
1862+
= current_params->refraction_ior;
1863+
}
16981864
}
16991865
closure = nullptr;
17001866
break;
@@ -1711,8 +1877,9 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
17111877
// recursively walk through the closure tree, creating bsdfs as we go
17121878
OSL_HOSTDEVICE void
17131879
process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
1714-
ShadingResult& result, const ClosureColor* closure,
1715-
const Color3& w, bool light_only)
1880+
ShadingResult& result, MediumStack& medium_stack,
1881+
const ClosureColor* closure, const Color3& w,
1882+
bool light_only)
17161883
{
17171884
static const ustringhash uh_ggx("ggx");
17181885
static const ustringhash uh_beckmann("beckmann");
@@ -1845,9 +2012,16 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
18452012
case MxDielectric::closureid(): {
18462013
const MxDielectric::Data& params
18472014
= *comp->as<MxDielectric::Data>();
1848-
ok = result.bsdf.add_bsdf<MxDielectric>(cw, params, -sg.I,
1849-
sg.backfacing,
1850-
path_roughness);
2015+
2016+
if (medium_stack.false_intersection_with(
2017+
result.medium_data)) {
2018+
ok = result.bsdf.add_bsdf<Transparent>(cw);
2019+
} else {
2020+
ok = result.bsdf.add_bsdf<MxDielectric>(cw, params,
2021+
-sg.I,
2022+
sg.backfacing,
2023+
path_roughness);
2024+
}
18512025
break;
18522026
}
18532027
case MxConductor::closureid(): {
@@ -1860,15 +2034,21 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
18602034
case MX_GENERALIZED_SCHLICK_ID: {
18612035
const MxGeneralizedSchlickParams& params
18622036
= *comp->as<MxGeneralizedSchlickParams>();
1863-
if (is_black(params.transmission_tint))
1864-
ok = result.bsdf.add_bsdf<MxMicrofacet<
1865-
MxGeneralizedSchlickParams, GGXDist, false>>(cw,
1866-
params,
1867-
1.0f);
1868-
else
1869-
ok = result.bsdf.add_bsdf<MxMicrofacet<
1870-
MxGeneralizedSchlickParams, GGXDist, true>>(
1871-
cw, params, result.refraction_ior);
2037+
2038+
if (medium_stack.false_intersection_with(
2039+
result.medium_data)) {
2040+
ok = result.bsdf.add_bsdf<Transparent>(cw);
2041+
} else {
2042+
if (is_black(params.transmission_tint)) {
2043+
ok = result.bsdf.add_bsdf<MxMicrofacet<
2044+
MxGeneralizedSchlickParams, GGXDist, false>>(
2045+
cw, params, 1.0f);
2046+
} else {
2047+
ok = result.bsdf.add_bsdf<MxMicrofacet<
2048+
MxGeneralizedSchlickParams, GGXDist, true>>(
2049+
cw, params, result.medium_data.refraction_ior);
2050+
}
2051+
}
18722052
break;
18732053
};
18742054
case MX_TRANSLUCENT_ID: {
@@ -1957,11 +2137,14 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
19572137

19582138
OSL_HOSTDEVICE void
19592139
process_closure(const ShaderGlobalsType& sg, float path_roughness,
1960-
ShadingResult& result, const ClosureColor* Ci, bool light_only)
2140+
ShadingResult& result, MediumStack& medium_stack,
2141+
const ClosureColor* Ci, bool light_only)
19612142
{
19622143
if (!light_only)
1963-
process_medium_closure(sg, path_roughness, result, Ci, Color3(1));
1964-
process_bsdf_closure(sg, path_roughness, result, Ci, Color3(1), light_only);
2144+
process_medium_closure(sg, path_roughness, result, medium_stack, Ci,
2145+
Color3(1));
2146+
process_bsdf_closure(sg, path_roughness, result, medium_stack, Ci,
2147+
Color3(1), light_only);
19652148
}
19662149

19672150
OSL_HOSTDEVICE Vec3
@@ -2022,5 +2205,23 @@ BSDF::sample_vrtl(const Vec3& wo, float rx, float ry, float rz) const
20222205
return dispatch([&](auto bsdf) { return bsdf.sample(wo, rx, ry, rz); });
20232206
}
20242207

2208+
OSL_HOSTDEVICE Medium::Sample
2209+
Medium::sample_vrtl(Ray& ray, Sampler& sampler, Intersection& hit) const
2210+
{
2211+
return dispatch(
2212+
[&](const auto& medium) { return medium.sample(ray, sampler, hit); });
2213+
}
2214+
2215+
OSL_HOSTDEVICE BSDF::Sample
2216+
Medium::sample_phase_func_vrtl(const Vec3& wo, float rx, float ry, float rz) const
2217+
{
2218+
return dispatch([&](auto medium) { return medium.sample_phase_func(wo, rx, ry, rz); });
2219+
}
2220+
2221+
OSL_HOSTDEVICE const MediumParams*
2222+
Medium::get_params_vrtl() const
2223+
{
2224+
return dispatch([&](const auto& medium) { return medium.get_params(); });
2225+
}
20252226

20262227
OSL_NAMESPACE_END

0 commit comments

Comments
 (0)