Skip to content

Commit 1f01e44

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 d9dfaa2 commit 1f01e44

35 files changed

Lines changed: 956 additions & 51 deletions

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+
while(!format_end_found) {
11791179
j++;
11801180
fmt_string += format[j];
11811181
switch (format[j]) {

src/testrender/shading.cpp

Lines changed: 153 additions & 33 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"
@@ -17,6 +18,7 @@
1718
#include <BSDL/MTX/bsdf_translucent_impl.h>
1819
#include <BSDL/SPI/bsdf_thinlayer_impl.h>
1920
#include <BSDL/spectrum_impl.h>
21+
#include <BSDL/SPI/bsdf_volume_impl.h>
2022

2123
using namespace OSL;
2224

@@ -1147,6 +1149,52 @@ struct Transparent final : public BSDF {
11471149
}
11481150
};
11491151

1152+
struct HenyeyGreenstein : public bsdl::spi::VolumeLobe<BSDLLobe> {
1153+
using Base = bsdl::spi::VolumeLobe<BSDLLobe>;
1154+
1155+
OSL_HOSTDEVICE HenyeyGreenstein(const Data& data, const Vec3& wo,
1156+
float path_roughness)
1157+
: Base(this,
1158+
bsdl::BsdfGlobals(wo,
1159+
Vec3(0), // Nf
1160+
Vec3(0), // Ngf
1161+
false, path_roughness,
1162+
1.0f, // outer_ior
1163+
0), // hero wavelength off
1164+
data)
1165+
{
1166+
}
1167+
1168+
OSL_HOSTDEVICE BSDF::Sample eval(const Vec3& wo, const Vec3& wi) const
1169+
{
1170+
bsdl::Sample s = Base::eval_impl(Base::frame.local(wo),
1171+
Base::frame.local(wi));
1172+
return { wi, s.weight.toRGB(0), s.pdf, s.roughness };
1173+
}
1174+
OSL_HOSTDEVICE BSDF::Sample sample(const Vec3& wo, float rx, float ry,
1175+
float rz) const
1176+
{
1177+
bsdl::Sample s = Base::sample_impl(Base::frame.local(wo),
1178+
{ rx, ry, rz });
1179+
return { Base::frame.world(s.wi), s.weight.toRGB(0), s.pdf,
1180+
s.roughness };
1181+
}
1182+
};
1183+
1184+
1185+
BSDF::Sample
1186+
MediumParams::sample_phase_func(const Vec3& wo, float rx, float ry,
1187+
float rz) const
1188+
{
1189+
if (is_vaccum()) {
1190+
return { Vec3(1.0f), Color3(1.0f), 0.0f, 0.0f };
1191+
}
1192+
1193+
HenyeyGreenstein::Data data { medium_g, medium_g, 0.0f };
1194+
HenyeyGreenstein phase_func(data, wo, 0.0f);
1195+
return phase_func.sample(wo, rx, ry, rz);
1196+
}
1197+
11501198
OSL_HOSTDEVICE Color3
11511199
evaluate_layer_opacity(const ShaderGlobalsType& sg, float path_roughness,
11521200
const ClosureColor* closure)
@@ -1235,8 +1283,8 @@ evaluate_layer_opacity(const ShaderGlobalsType& sg, float path_roughness,
12351283

12361284
OSL_HOSTDEVICE void
12371285
process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
1238-
ShadingResult& result, const ClosureColor* closure,
1239-
const Color3& w)
1286+
ShadingResult& result, MediumStack& medium_stack,
1287+
const ClosureColor* closure, const Color3& w)
12401288
{
12411289
if (!closure)
12421290
return;
@@ -1278,37 +1326,82 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
12781326
const ClosureComponent* comp = closure->as_comp();
12791327
Color3 cw = weight * comp->w;
12801328
const auto& params = *comp->as<MxAnisotropicVdfParams>();
1281-
result.sigma_t = cw * params.extinction;
1282-
result.sigma_s = params.albedo * result.sigma_t;
1283-
result.medium_g = params.anisotropy;
1284-
closure = nullptr;
1329+
result.medium_data.sigma_t = cw * params.extinction;
1330+
result.medium_data.sigma_s = params.albedo
1331+
* result.medium_data.sigma_t;
1332+
result.medium_data.medium_g = params.anisotropy;
1333+
result.medium_data.priority = 0; // always intersect
1334+
1335+
// clamp sigma_s to be less than sigma_t
1336+
result.medium_data.sigma_s = {
1337+
std::min(result.medium_data.sigma_s.x, result.medium_data.sigma_t.x),
1338+
std::min(result.medium_data.sigma_s.y, result.medium_data.sigma_t.y),
1339+
std::min(result.medium_data.sigma_s.z, result.medium_data.sigma_t.z)
1340+
};
1341+
1342+
closure = nullptr;
12851343
break;
12861344
}
12871345
case MX_MEDIUM_VDF_ID: {
12881346
const ClosureComponent* comp = closure->as_comp();
12891347
Color3 cw = weight * comp->w;
12901348
const auto& params = *comp->as<MxMediumVdfParams>();
1291-
result.sigma_t = { -OIIO::fast_log(params.transmission_color.x),
1292-
-OIIO::fast_log(params.transmission_color.y),
1293-
-OIIO::fast_log(params.transmission_color.z) };
1294-
// NOTE: closure weight scales the extinction parameter
1295-
result.sigma_t *= cw / params.transmission_depth;
1296-
result.sigma_s = params.albedo * result.sigma_t;
1297-
result.medium_g = params.anisotropy;
1298-
// TODO: properly track a medium stack here ...
1299-
result.refraction_ior = sg.backfacing ? 1.0f / params.ior
1300-
: params.ior;
1301-
result.priority = params.priority;
1302-
closure = nullptr;
1349+
1350+
// when both albedo and transmission_color are black, this is
1351+
// a vacuum medium used only to carry the IOR for dielectric
1352+
// surfaces.
1353+
bool is_vacuum = is_black(params.albedo)
1354+
&& is_black(params.transmission_color);
1355+
1356+
if (is_vacuum) {
1357+
result.medium_data.sigma_t = Color3(0.0f);
1358+
result.medium_data.sigma_s = Color3(0.0f);
1359+
} else {
1360+
constexpr float epsilon = 1e-10f;
1361+
const Color3& t_color = params.transmission_color;
1362+
result.medium_data.sigma_t
1363+
= Color3(-OIIO::fast_log(fmaxf(t_color.x, epsilon)),
1364+
-OIIO::fast_log(fmaxf(t_color.y, epsilon)),
1365+
-OIIO::fast_log(fmaxf(t_color.z, epsilon)));
1366+
1367+
result.medium_data.sigma_t *= cw / params.transmission_depth;
1368+
result.medium_data.sigma_s = params.albedo
1369+
* result.medium_data.sigma_t;
1370+
1371+
// clamp sigma_s to be less than sigma_t
1372+
result.medium_data.sigma_s = {
1373+
std::min(result.medium_data.sigma_s.x, result.medium_data.sigma_t.x),
1374+
std::min(result.medium_data.sigma_s.y, result.medium_data.sigma_t.y),
1375+
std::min(result.medium_data.sigma_s.z, result.medium_data.sigma_t.z)
1376+
};
1377+
}
1378+
1379+
result.medium_data.medium_g = params.anisotropy;
1380+
1381+
result.medium_data.refraction_ior = sg.backfacing
1382+
? 1.0f / params.ior
1383+
: params.ior;
1384+
result.medium_data.priority = params.priority;
1385+
1386+
closure = nullptr;
13031387
break;
13041388
}
13051389
case MxDielectric::closureid(): {
13061390
const ClosureComponent* comp = closure->as_comp();
13071391
const MxDielectric::Data& params = *comp->as<MxDielectric::Data>();
13081392
if (!is_black(weight * comp->w * params.refr_tint)) {
1309-
// TODO: properly track a medium stack here ...
1310-
result.refraction_ior = sg.backfacing ? 1.0f / params.IOR
1311-
: params.IOR;
1393+
float new_ior = sg.backfacing ? 1.0f / params.IOR : params.IOR;
1394+
1395+
result.medium_data.refraction_ior = new_ior;
1396+
1397+
const MediumParams* current_params
1398+
= medium_stack.get_current_params();
1399+
if (current_params
1400+
&& result.medium_data.priority
1401+
<= current_params->priority) {
1402+
result.medium_data.refraction_ior
1403+
= current_params->refraction_ior;
1404+
}
13121405
}
13131406
closure = nullptr;
13141407
break;
@@ -1324,7 +1417,18 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
13241417
0.0f, 0.99f);
13251418
float sqrt_F0 = sqrtf(avg_F0);
13261419
float ior = (1 + sqrt_F0) / (1 - sqrt_F0);
1327-
result.refraction_ior = sg.backfacing ? 1.0f / ior : ior;
1420+
float new_ior = sg.backfacing ? 1.0f / ior : ior;
1421+
1422+
result.medium_data.refraction_ior = new_ior;
1423+
1424+
const MediumParams* current_params
1425+
= medium_stack.get_current_params();
1426+
if (current_params
1427+
&& result.medium_data.priority
1428+
<= current_params->priority) {
1429+
result.medium_data.refraction_ior
1430+
= current_params->refraction_ior;
1431+
}
13281432
}
13291433
closure = nullptr;
13301434
break;
@@ -1341,8 +1445,9 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
13411445
// recursively walk through the closure tree, creating bsdfs as we go
13421446
OSL_HOSTDEVICE void
13431447
process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
1344-
ShadingResult& result, const ClosureColor* closure,
1345-
const Color3& w, bool light_only)
1448+
ShadingResult& result, MediumStack& medium_stack,
1449+
const ClosureColor* closure, const Color3& w,
1450+
bool light_only)
13461451
{
13471452
static const ustringhash uh_ggx("ggx");
13481453
static const ustringhash uh_beckmann("beckmann");
@@ -1472,16 +1577,29 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
14721577
case MxDielectric::closureid(): {
14731578
const MxDielectric::Data& params
14741579
= *comp->as<MxDielectric::Data>();
1475-
ok = result.bsdf.add_bsdf<MxDielectric>(cw, params, -sg.I,
1476-
sg.backfacing,
1477-
path_roughness);
1580+
1581+
if (medium_stack.false_intersection_with(
1582+
result.medium_data)) {
1583+
ok = result.bsdf.add_bsdf<Transparent>(cw);
1584+
} else {
1585+
ok = result.bsdf.add_bsdf<MxDielectric>(cw, params,
1586+
-sg.I,
1587+
sg.backfacing,
1588+
path_roughness);
1589+
}
14781590
break;
14791591
}
14801592
case MxGeneralizedSchlick::closureid(): {
14811593
const MxGeneralizedSchlick::Data& params
14821594
= *comp->as<MxGeneralizedSchlick::Data>();
1483-
ok = result.bsdf.add_bsdf<MxGeneralizedSchlick>(
1484-
cw, params, -sg.I, sg.backfacing, path_roughness);
1595+
1596+
if (medium_stack.false_intersection_with(
1597+
result.medium_data)) {
1598+
ok = result.bsdf.add_bsdf<Transparent>(cw);
1599+
} else {
1600+
ok = result.bsdf.add_bsdf<MxGeneralizedSchlick>(
1601+
cw, params, -sg.I, sg.backfacing, path_roughness);
1602+
}
14851603
break;
14861604
}
14871605
case MxConductor::closureid(): {
@@ -1574,11 +1692,14 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
15741692

15751693
OSL_HOSTDEVICE void
15761694
process_closure(const ShaderGlobalsType& sg, float path_roughness,
1577-
ShadingResult& result, const ClosureColor* Ci, bool light_only)
1695+
ShadingResult& result, MediumStack& medium_stack,
1696+
const ClosureColor* Ci, bool light_only)
15781697
{
15791698
if (!light_only)
1580-
process_medium_closure(sg, path_roughness, result, Ci, Color3(1));
1581-
process_bsdf_closure(sg, path_roughness, result, Ci, Color3(1), light_only);
1699+
process_medium_closure(sg, path_roughness, result, medium_stack, Ci,
1700+
Color3(1));
1701+
process_bsdf_closure(sg, path_roughness, result, medium_stack, Ci,
1702+
Color3(1), light_only);
15821703
}
15831704

15841705
OSL_HOSTDEVICE Vec3
@@ -1639,5 +1760,4 @@ BSDF::sample_vrtl(const Vec3& wo, float rx, float ry, float rz) const
16391760
return dispatch([&](auto bsdf) { return bsdf.sample(wo, rx, ry, rz); });
16401761
}
16411762

1642-
16431763
OSL_NAMESPACE_END

0 commit comments

Comments
 (0)