Skip to content

Commit d91d505

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 d91d505

36 files changed

Lines changed: 963 additions & 49 deletions

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,8 @@ jobs:
377377
setenvs: export CMAKE_BUILD_TYPE=Debug
378378
LLVM_VERSION=14.0.0 LLVM_DISTRO_NAME=ubuntu-18.04
379379
PUGIXML_VERSION=v1.9
380-
CTEST_TEST_TIMEOUT=240
380+
CTEST_TEST_TIMEOUT=1200
381+
OSL_CMAKE_FLAGS="-DOSL_TEST_BIG_TIMEOUT=1200"
381382
- desc: gcc10/C++17 llvm14 oiio-2.5 avx2
382383
nametag: linux-2021ish-gcc10-llvm14
383384
runner: ubuntu-22.04

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: 157 additions & 34 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"
@@ -16,6 +17,7 @@
1617
#include <BSDL/MTX/bsdf_sheen_impl.h>
1718
#include <BSDL/MTX/bsdf_translucent_impl.h>
1819
#include <BSDL/SPI/bsdf_thinlayer_impl.h>
20+
#include <BSDL/SPI/bsdf_volume_impl.h>
1921
#include <BSDL/spectrum_impl.h>
2022

2123
using namespace OSL;
@@ -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,86 @@ 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,
1338+
result.medium_data.sigma_t.x),
1339+
std::min(result.medium_data.sigma_s.y,
1340+
result.medium_data.sigma_t.y),
1341+
std::min(result.medium_data.sigma_s.z,
1342+
result.medium_data.sigma_t.z) };
1343+
1344+
closure = nullptr;
12851345
break;
12861346
}
12871347
case MX_MEDIUM_VDF_ID: {
12881348
const ClosureComponent* comp = closure->as_comp();
12891349
Color3 cw = weight * comp->w;
12901350
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;
1351+
1352+
// when both albedo and transmission_color are black, this is
1353+
// a vacuum medium used only to carry the IOR for dielectric
1354+
// surfaces.
1355+
bool is_vacuum = is_black(params.albedo)
1356+
&& is_black(params.transmission_color);
1357+
1358+
if (is_vacuum) {
1359+
result.medium_data.sigma_t = Color3(0.0f);
1360+
result.medium_data.sigma_s = Color3(0.0f);
1361+
} else {
1362+
constexpr float epsilon = 1e-10f;
1363+
const Color3& t_color = params.transmission_color;
1364+
result.medium_data.sigma_t
1365+
= Color3(-OIIO::fast_log(fmaxf(t_color.x, epsilon)),
1366+
-OIIO::fast_log(fmaxf(t_color.y, epsilon)),
1367+
-OIIO::fast_log(fmaxf(t_color.z, epsilon)));
1368+
1369+
result.medium_data.sigma_t *= cw / params.transmission_depth;
1370+
result.medium_data.sigma_s = params.albedo
1371+
* result.medium_data.sigma_t;
1372+
1373+
// clamp sigma_s to be less than sigma_t
1374+
result.medium_data.sigma_s
1375+
= { std::min(result.medium_data.sigma_s.x,
1376+
result.medium_data.sigma_t.x),
1377+
std::min(result.medium_data.sigma_s.y,
1378+
result.medium_data.sigma_t.y),
1379+
std::min(result.medium_data.sigma_s.z,
1380+
result.medium_data.sigma_t.z) };
1381+
}
1382+
1383+
result.medium_data.medium_g = params.anisotropy;
1384+
1385+
result.medium_data.refraction_ior = sg.backfacing
1386+
? 1.0f / params.ior
1387+
: params.ior;
1388+
result.medium_data.priority = params.priority;
1389+
1390+
closure = nullptr;
13031391
break;
13041392
}
13051393
case MxDielectric::closureid(): {
13061394
const ClosureComponent* comp = closure->as_comp();
13071395
const MxDielectric::Data& params = *comp->as<MxDielectric::Data>();
13081396
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;
1397+
float new_ior = sg.backfacing ? 1.0f / params.IOR : params.IOR;
1398+
1399+
result.medium_data.refraction_ior = new_ior;
1400+
1401+
const MediumParams* current_params
1402+
= medium_stack.get_current_params();
1403+
if (current_params
1404+
&& result.medium_data.priority
1405+
<= current_params->priority) {
1406+
result.medium_data.refraction_ior
1407+
= current_params->refraction_ior;
1408+
}
13121409
}
13131410
closure = nullptr;
13141411
break;
@@ -1318,13 +1415,23 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
13181415
const MxGeneralizedSchlick::Data& params
13191416
= *comp->as<MxGeneralizedSchlick::Data>();
13201417
if (!is_black(weight * comp->w * params.refr_tint)) {
1321-
// TODO: properly track a medium stack here ...
13221418
float avg_F0 = clamp((params.F0.x + params.F0.y + params.F0.z)
13231419
/ 3.0f,
13241420
0.0f, 0.99f);
13251421
float sqrt_F0 = sqrtf(avg_F0);
13261422
float ior = (1 + sqrt_F0) / (1 - sqrt_F0);
1327-
result.refraction_ior = sg.backfacing ? 1.0f / ior : ior;
1423+
float new_ior = sg.backfacing ? 1.0f / ior : ior;
1424+
1425+
result.medium_data.refraction_ior = new_ior;
1426+
1427+
const MediumParams* current_params
1428+
= medium_stack.get_current_params();
1429+
if (current_params
1430+
&& result.medium_data.priority
1431+
<= current_params->priority) {
1432+
result.medium_data.refraction_ior
1433+
= current_params->refraction_ior;
1434+
}
13281435
}
13291436
closure = nullptr;
13301437
break;
@@ -1341,8 +1448,9 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
13411448
// recursively walk through the closure tree, creating bsdfs as we go
13421449
OSL_HOSTDEVICE void
13431450
process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
1344-
ShadingResult& result, const ClosureColor* closure,
1345-
const Color3& w, bool light_only)
1451+
ShadingResult& result, MediumStack& medium_stack,
1452+
const ClosureColor* closure, const Color3& w,
1453+
bool light_only)
13461454
{
13471455
static const ustringhash uh_ggx("ggx");
13481456
static const ustringhash uh_beckmann("beckmann");
@@ -1472,16 +1580,29 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
14721580
case MxDielectric::closureid(): {
14731581
const MxDielectric::Data& params
14741582
= *comp->as<MxDielectric::Data>();
1475-
ok = result.bsdf.add_bsdf<MxDielectric>(cw, params, -sg.I,
1476-
sg.backfacing,
1477-
path_roughness);
1583+
1584+
if (medium_stack.false_intersection_with(
1585+
result.medium_data)) {
1586+
ok = result.bsdf.add_bsdf<Transparent>(cw);
1587+
} else {
1588+
ok = result.bsdf.add_bsdf<MxDielectric>(cw, params,
1589+
-sg.I,
1590+
sg.backfacing,
1591+
path_roughness);
1592+
}
14781593
break;
14791594
}
14801595
case MxGeneralizedSchlick::closureid(): {
14811596
const MxGeneralizedSchlick::Data& params
14821597
= *comp->as<MxGeneralizedSchlick::Data>();
1483-
ok = result.bsdf.add_bsdf<MxGeneralizedSchlick>(
1484-
cw, params, -sg.I, sg.backfacing, path_roughness);
1598+
1599+
if (medium_stack.false_intersection_with(
1600+
result.medium_data)) {
1601+
ok = result.bsdf.add_bsdf<Transparent>(cw);
1602+
} else {
1603+
ok = result.bsdf.add_bsdf<MxGeneralizedSchlick>(
1604+
cw, params, -sg.I, sg.backfacing, path_roughness);
1605+
}
14851606
break;
14861607
}
14871608
case MxConductor::closureid(): {
@@ -1574,11 +1695,14 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
15741695

15751696
OSL_HOSTDEVICE void
15761697
process_closure(const ShaderGlobalsType& sg, float path_roughness,
1577-
ShadingResult& result, const ClosureColor* Ci, bool light_only)
1698+
ShadingResult& result, MediumStack& medium_stack,
1699+
const ClosureColor* Ci, bool light_only)
15781700
{
15791701
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);
1702+
process_medium_closure(sg, path_roughness, result, medium_stack, Ci,
1703+
Color3(1));
1704+
process_bsdf_closure(sg, path_roughness, result, medium_stack, Ci,
1705+
Color3(1), light_only);
15821706
}
15831707

15841708
OSL_HOSTDEVICE Vec3
@@ -1639,5 +1763,4 @@ BSDF::sample_vrtl(const Vec3& wo, float rx, float ry, float rz) const
16391763
return dispatch([&](auto bsdf) { return bsdf.sample(wo, rx, ry, rz); });
16401764
}
16411765

1642-
16431766
OSL_NAMESPACE_END

0 commit comments

Comments
 (0)