Skip to content

Commit a8bf1de

Browse files
committed
Add testrender volumes
Alongside the rest of the initialization for subpixel_radiance, an empty MediumStack is initialized. It stores the medium params with pointers in two arrays. One tracks the entry order and the other sorted based on priority. There is a priority handling scheme that will shuffle the incumbent medium pointers to give high priority (lower integer priorities) mediums lower indices. When processing BSDF closures, intersections with MxDielectric or MxGeneralizedSchlick may only be added when priority < current_priority or are both 0 (the precious priority). MediumStack exposes an integrate method that operates on aggregated parameters from all media sharing the same priority as the topmost medium; these aggregated parameters are stored on the stack and updated whenever media are added. A CDF built from these parameters is then sampled to select one of the overlapping media for scattering. At present, only Henyey–Greenstein phase functions are supported, implemented as a BSDF subclass and wraps the implementation in BSDL. Signed-off-by: Owen O'Malley <theowen@email.com>
1 parent 354ece3 commit a8bf1de

File tree

41 files changed

+974
-60
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+974
-60
lines changed

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/libbsdl/include/BSDL/tools.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212

1313
BSDL_ENTER_NAMESPACE
1414

15+
BSDL_INLINE float
16+
MAX(float a, float b)
17+
{
18+
return std::max(a, b);
19+
}
20+
1521
BSDL_INLINE float
1622
MAX_ABS_XYZ(const Imath::V3f& v)
1723
{

src/testrender/optixraytracer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1175,7 +1175,7 @@ OptixRaytracer::processPrintfBuffer(void* buffer_data, size_t buffer_size)
11751175
if (format[j] == '%') {
11761176
fmt_string = "%";
11771177
bool format_end_found = false;
1178-
for (size_t i = 0; !format_end_found; i++) {
1178+
for (; !format_end_found; ) {
11791179
j++;
11801180
fmt_string += format[j];
11811181
switch (format[j]) {

src/testrender/shading.cpp

Lines changed: 149 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55

66
#include "shading.h"
7+
#include "OSL/oslconfig.h"
78
#include <OSL/genclosure.h>
89
#include "optics.h"
910
#include "sampling.h"
@@ -12,6 +13,7 @@
1213
#include <BSDL/MTX/bsdf_dielectric_impl.h>
1314
#include <BSDL/SPI/bsdf_thinlayer_impl.h>
1415
#include <BSDL/spectrum_impl.h>
16+
#include <BSDL/SPI/bsdf_volume_impl.h>
1517

1618
using namespace OSL;
1719

@@ -1513,6 +1515,51 @@ struct ZeltnerBurleySheen final : public BSDF, MxSheenParams {
15131515
}
15141516
};
15151517

1518+
struct HenyeyGreenstein : public bsdl::spi::VolumeLobe<BSDLLobe> {
1519+
using Base = bsdl::spi::VolumeLobe<BSDLLobe>;
1520+
1521+
OSL_HOSTDEVICE HenyeyGreenstein(const Data& data, const Vec3& wo,
1522+
float path_roughness)
1523+
: Base(this,
1524+
bsdl::BsdfGlobals(wo,
1525+
Vec3(0), // Nf
1526+
Vec3(0), // Ngf
1527+
false,
1528+
path_roughness,
1529+
1.0f, // outer_ior
1530+
0), // hero wavelength off
1531+
data)
1532+
{
1533+
}
1534+
1535+
OSL_HOSTDEVICE BSDF::Sample eval(const Vec3& wo, const Vec3& wi) const
1536+
{
1537+
bsdl::Sample s = Base::eval_impl(Base::frame.local(wo),
1538+
Base::frame.local(wi));
1539+
return { wi, s.weight.toRGB(0), s.pdf, s.roughness };
1540+
}
1541+
OSL_HOSTDEVICE BSDF::Sample sample(const Vec3& wo, float rx, float ry,
1542+
float rz) const
1543+
{
1544+
bsdl::Sample s = Base::sample_impl(Base::frame.local(wo),
1545+
{ rx, ry, rz });
1546+
return { Base::frame.world(s.wi), s.weight.toRGB(0), s.pdf,
1547+
s.roughness };
1548+
}
1549+
};
1550+
1551+
1552+
OSL_HOSTDEVICE BSDF::Sample MediumParams::sample_phase_func(const Vec3& wo, float rx,
1553+
float ry, float rz) const {
1554+
if (is_empty) {
1555+
return { Vec3(1.0f), Color3(1.0f), 0.0f, 0.0f };
1556+
}
1557+
1558+
HenyeyGreenstein::Data data { medium_g, medium_g, 0.0f };
1559+
HenyeyGreenstein phase_func(data, wo, 0.0f);
1560+
return phase_func.sample(wo, rx, ry, rz);
1561+
}
1562+
15161563
OSL_HOSTDEVICE Color3
15171564
evaluate_layer_opacity(const ShaderGlobalsType& sg, float path_roughness,
15181565
const ClosureColor* closure)
@@ -1606,8 +1653,8 @@ evaluate_layer_opacity(const ShaderGlobalsType& sg, float path_roughness,
16061653

16071654
OSL_HOSTDEVICE void
16081655
process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
1609-
ShadingResult& result, const ClosureColor* closure,
1610-
const Color3& w)
1656+
ShadingResult& result, MediumStack& medium_stack,
1657+
const ClosureColor* closure, const Color3& w)
16111658
{
16121659
if (!closure)
16131660
return;
@@ -1649,37 +1696,72 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
16491696
const ClosureComponent* comp = closure->as_comp();
16501697
Color3 cw = weight * comp->w;
16511698
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;
1699+
result.medium_data.sigma_t = cw * params.extinction;
1700+
result.medium_data.sigma_s = params.albedo
1701+
* result.medium_data.sigma_t;
1702+
result.medium_data.medium_g = params.anisotropy;
1703+
result.medium_data.priority = 0; // always intersect
1704+
1705+
result.medium_data.is_empty = result.medium_data.is_vaccum();
1706+
1707+
closure = nullptr;
16561708
break;
16571709
}
16581710
case MX_MEDIUM_VDF_ID: {
16591711
const ClosureComponent* comp = closure->as_comp();
16601712
Color3 cw = weight * comp->w;
16611713
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;
1714+
1715+
// when both albedo and transmission_color are black, this is
1716+
// a vacuum medium used only to carry the IOR for dielectric
1717+
// surfaces.
1718+
bool is_vacuum = is_black(params.albedo)
1719+
&& is_black(params.transmission_color);
1720+
1721+
if (is_vacuum) {
1722+
result.medium_data.sigma_t = Color3(0.0f);
1723+
result.medium_data.sigma_s = Color3(0.0f);
1724+
result.medium_data.is_empty = true;
1725+
} else {
1726+
const float epsilon = 1e-10f;
1727+
result.medium_data.sigma_t = Color3(
1728+
-OIIO::fast_log(fmaxf(params.transmission_color.x, epsilon)),
1729+
-OIIO::fast_log(fmaxf(params.transmission_color.y, epsilon)),
1730+
-OIIO::fast_log(fmaxf(params.transmission_color.z, epsilon))
1731+
);
1732+
1733+
result.medium_data.sigma_t *= cw / params.transmission_depth;
1734+
result.medium_data.sigma_s = params.albedo
1735+
* result.medium_data.sigma_t;
1736+
result.medium_data.is_empty = result.medium_data.is_vaccum();
1737+
}
1738+
1739+
result.medium_data.medium_g = params.anisotropy;
1740+
1741+
result.medium_data.refraction_ior = sg.backfacing
1742+
? 1.0f / params.ior
1743+
: params.ior;
1744+
result.medium_data.priority = params.priority;
1745+
1746+
closure = nullptr;
16741747
break;
16751748
}
16761749
case MxDielectric::closureid(): {
16771750
const ClosureComponent* comp = closure->as_comp();
16781751
const MxDielectric::Data& params = *comp->as<MxDielectric::Data>();
16791752
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;
1753+
float new_ior = sg.backfacing ? 1.0f / params.IOR : params.IOR;
1754+
1755+
result.medium_data.refraction_ior = new_ior;
1756+
1757+
const MediumParams* current_params
1758+
= medium_stack.get_current_params();
1759+
if (current_params
1760+
&& result.medium_data.priority
1761+
<= current_params->priority) {
1762+
result.medium_data.refraction_ior
1763+
= current_params->refraction_ior;
1764+
}
16831765
}
16841766
closure = nullptr;
16851767
break;
@@ -1688,13 +1770,23 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
16881770
const ClosureComponent* comp = closure->as_comp();
16891771
const auto& params = *comp->as<MxGeneralizedSchlickParams>();
16901772
if (!is_black(weight * comp->w * params.transmission_tint)) {
1691-
// TODO: properly track a medium stack here ...
16921773
float avg_F0 = clamp((params.f0.x + params.f0.y + params.f0.z)
16931774
/ 3.0f,
16941775
0.0f, 0.99f);
16951776
float sqrt_F0 = sqrtf(avg_F0);
16961777
float ior = (1 + sqrt_F0) / (1 - sqrt_F0);
1697-
result.refraction_ior = sg.backfacing ? 1.0f / ior : ior;
1778+
float new_ior = sg.backfacing ? 1.0f / ior : ior;
1779+
1780+
result.medium_data.refraction_ior = new_ior;
1781+
1782+
const MediumParams* current_params
1783+
= medium_stack.get_current_params();
1784+
if (current_params
1785+
&& result.medium_data.priority
1786+
<= current_params->priority) {
1787+
result.medium_data.refraction_ior
1788+
= current_params->refraction_ior;
1789+
}
16981790
}
16991791
closure = nullptr;
17001792
break;
@@ -1711,8 +1803,9 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
17111803
// recursively walk through the closure tree, creating bsdfs as we go
17121804
OSL_HOSTDEVICE void
17131805
process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
1714-
ShadingResult& result, const ClosureColor* closure,
1715-
const Color3& w, bool light_only)
1806+
ShadingResult& result, MediumStack& medium_stack,
1807+
const ClosureColor* closure, const Color3& w,
1808+
bool light_only)
17161809
{
17171810
static const ustringhash uh_ggx("ggx");
17181811
static const ustringhash uh_beckmann("beckmann");
@@ -1845,9 +1938,16 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
18451938
case MxDielectric::closureid(): {
18461939
const MxDielectric::Data& params
18471940
= *comp->as<MxDielectric::Data>();
1848-
ok = result.bsdf.add_bsdf<MxDielectric>(cw, params, -sg.I,
1849-
sg.backfacing,
1850-
path_roughness);
1941+
1942+
if (medium_stack.false_intersection_with(
1943+
result.medium_data)) {
1944+
ok = result.bsdf.add_bsdf<Transparent>(cw);
1945+
} else {
1946+
ok = result.bsdf.add_bsdf<MxDielectric>(cw, params,
1947+
-sg.I,
1948+
sg.backfacing,
1949+
path_roughness);
1950+
}
18511951
break;
18521952
}
18531953
case MxConductor::closureid(): {
@@ -1860,15 +1960,21 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
18601960
case MX_GENERALIZED_SCHLICK_ID: {
18611961
const MxGeneralizedSchlickParams& params
18621962
= *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);
1963+
1964+
if (medium_stack.false_intersection_with(
1965+
result.medium_data)) {
1966+
ok = result.bsdf.add_bsdf<Transparent>(cw);
1967+
} else {
1968+
if (is_black(params.transmission_tint)) {
1969+
ok = result.bsdf.add_bsdf<MxMicrofacet<
1970+
MxGeneralizedSchlickParams, GGXDist, false>>(
1971+
cw, params, 1.0f);
1972+
} else {
1973+
ok = result.bsdf.add_bsdf<MxMicrofacet<
1974+
MxGeneralizedSchlickParams, GGXDist, true>>(
1975+
cw, params, result.medium_data.refraction_ior);
1976+
}
1977+
}
18721978
break;
18731979
};
18741980
case MX_TRANSLUCENT_ID: {
@@ -1957,11 +2063,14 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
19572063

19582064
OSL_HOSTDEVICE void
19592065
process_closure(const ShaderGlobalsType& sg, float path_roughness,
1960-
ShadingResult& result, const ClosureColor* Ci, bool light_only)
2066+
ShadingResult& result, MediumStack& medium_stack,
2067+
const ClosureColor* Ci, bool light_only)
19612068
{
19622069
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);
2070+
process_medium_closure(sg, path_roughness, result, medium_stack, Ci,
2071+
Color3(1));
2072+
process_bsdf_closure(sg, path_roughness, result, medium_stack, Ci,
2073+
Color3(1), light_only);
19652074
}
19662075

19672076
OSL_HOSTDEVICE Vec3
@@ -2022,5 +2131,4 @@ BSDF::sample_vrtl(const Vec3& wo, float rx, float ry, float rz) const
20222131
return dispatch([&](auto bsdf) { return bsdf.sample(wo, rx, ry, rz); });
20232132
}
20242133

2025-
20262134
OSL_NAMESPACE_END

0 commit comments

Comments
 (0)