44
55
66#include " shading.h"
7+ #include " OSL/oslconfig.h"
78#include < OSL/genclosure.h>
89#include " optics.h"
910#include " sampling.h"
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
2123using 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_empty) {
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+
11501198OSL_HOSTDEVICE Color3
11511199evaluate_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
12361284OSL_HOSTDEVICE void
12371285process_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,72 @@ 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+ result.medium_data .is_empty = result.medium_data .is_vaccum ();
1336+
1337+ closure = nullptr ;
12851338 break ;
12861339 }
12871340 case MX_MEDIUM_VDF_ID: {
12881341 const ClosureComponent* comp = closure->as_comp ();
12891342 Color3 cw = weight * comp->w ;
12901343 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 ;
1344+
1345+ // when both albedo and transmission_color are black, this is
1346+ // a vacuum medium used only to carry the IOR for dielectric
1347+ // surfaces.
1348+ bool is_vacuum = is_black (params.albedo )
1349+ && is_black (params.transmission_color );
1350+
1351+ if (is_vacuum) {
1352+ result.medium_data .sigma_t = Color3 (0 .0f );
1353+ result.medium_data .sigma_s = Color3 (0 .0f );
1354+ result.medium_data .is_empty = true ;
1355+ } else {
1356+ constexpr float epsilon = 1e-10f ;
1357+ const Color3& t_color = params.transmission_color ;
1358+ result.medium_data .sigma_t
1359+ = Color3 (-OIIO::fast_log (fmaxf (t_color.x , epsilon)),
1360+ -OIIO::fast_log (fmaxf (t_color.y , epsilon)),
1361+ -OIIO::fast_log (fmaxf (t_color.z , epsilon)));
1362+
1363+ result.medium_data .sigma_t *= cw / params.transmission_depth ;
1364+ result.medium_data .sigma_s = params.albedo
1365+ * result.medium_data .sigma_t ;
1366+ result.medium_data .is_empty = result.medium_data .is_vaccum ();
1367+ }
1368+
1369+ result.medium_data .medium_g = params.anisotropy ;
1370+
1371+ result.medium_data .refraction_ior = sg.backfacing
1372+ ? 1 .0f / params.ior
1373+ : params.ior ;
1374+ result.medium_data .priority = params.priority ;
1375+
1376+ closure = nullptr ;
13031377 break ;
13041378 }
13051379 case MxDielectric::closureid (): {
13061380 const ClosureComponent* comp = closure->as_comp ();
13071381 const MxDielectric::Data& params = *comp->as <MxDielectric::Data>();
13081382 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 ;
1383+ float new_ior = sg.backfacing ? 1 .0f / params.IOR : params.IOR ;
1384+
1385+ result.medium_data .refraction_ior = new_ior;
1386+
1387+ const MediumParams* current_params
1388+ = medium_stack.get_current_params ();
1389+ if (current_params
1390+ && result.medium_data .priority
1391+ <= current_params->priority ) {
1392+ result.medium_data .refraction_ior
1393+ = current_params->refraction_ior ;
1394+ }
13121395 }
13131396 closure = nullptr ;
13141397 break ;
@@ -1324,7 +1407,18 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
13241407 0 .0f , 0 .99f );
13251408 float sqrt_F0 = sqrtf (avg_F0);
13261409 float ior = (1 + sqrt_F0) / (1 - sqrt_F0);
1327- result.refraction_ior = sg.backfacing ? 1 .0f / ior : ior;
1410+ float new_ior = sg.backfacing ? 1 .0f / ior : ior;
1411+
1412+ result.medium_data .refraction_ior = new_ior;
1413+
1414+ const MediumParams* current_params
1415+ = medium_stack.get_current_params ();
1416+ if (current_params
1417+ && result.medium_data .priority
1418+ <= current_params->priority ) {
1419+ result.medium_data .refraction_ior
1420+ = current_params->refraction_ior ;
1421+ }
13281422 }
13291423 closure = nullptr ;
13301424 break ;
@@ -1341,8 +1435,9 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
13411435// recursively walk through the closure tree, creating bsdfs as we go
13421436OSL_HOSTDEVICE void
13431437process_bsdf_closure (const ShaderGlobalsType& sg, float path_roughness,
1344- ShadingResult& result, const ClosureColor* closure,
1345- const Color3& w, bool light_only)
1438+ ShadingResult& result, MediumStack& medium_stack,
1439+ const ClosureColor* closure, const Color3& w,
1440+ bool light_only)
13461441{
13471442 static const ustringhash uh_ggx (" ggx" );
13481443 static const ustringhash uh_beckmann (" beckmann" );
@@ -1472,16 +1567,29 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
14721567 case MxDielectric::closureid (): {
14731568 const MxDielectric::Data& params
14741569 = *comp->as <MxDielectric::Data>();
1475- ok = result.bsdf .add_bsdf <MxDielectric>(cw, params, -sg.I ,
1476- sg.backfacing ,
1477- path_roughness);
1570+
1571+ if (medium_stack.false_intersection_with (
1572+ result.medium_data )) {
1573+ ok = result.bsdf .add_bsdf <Transparent>(cw);
1574+ } else {
1575+ ok = result.bsdf .add_bsdf <MxDielectric>(cw, params,
1576+ -sg.I ,
1577+ sg.backfacing ,
1578+ path_roughness);
1579+ }
14781580 break ;
14791581 }
14801582 case MxGeneralizedSchlick::closureid (): {
14811583 const MxGeneralizedSchlick::Data& params
14821584 = *comp->as <MxGeneralizedSchlick::Data>();
1483- ok = result.bsdf .add_bsdf <MxGeneralizedSchlick>(
1484- cw, params, -sg.I , sg.backfacing , path_roughness);
1585+
1586+ if (medium_stack.false_intersection_with (
1587+ result.medium_data )) {
1588+ ok = result.bsdf .add_bsdf <Transparent>(cw);
1589+ } else {
1590+ ok = result.bsdf .add_bsdf <MxGeneralizedSchlick>(
1591+ cw, params, -sg.I , sg.backfacing , path_roughness);
1592+ }
14851593 break ;
14861594 }
14871595 case MxConductor::closureid (): {
@@ -1574,11 +1682,14 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
15741682
15751683OSL_HOSTDEVICE void
15761684process_closure (const ShaderGlobalsType& sg, float path_roughness,
1577- ShadingResult& result, const ClosureColor* Ci, bool light_only)
1685+ ShadingResult& result, MediumStack& medium_stack,
1686+ const ClosureColor* Ci, bool light_only)
15781687{
15791688 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);
1689+ process_medium_closure (sg, path_roughness, result, medium_stack, Ci,
1690+ Color3 (1 ));
1691+ process_bsdf_closure (sg, path_roughness, result, medium_stack, Ci,
1692+ Color3 (1 ), light_only);
15821693}
15831694
15841695OSL_HOSTDEVICE Vec3
@@ -1639,5 +1750,4 @@ BSDF::sample_vrtl(const Vec3& wo, float rx, float ry, float rz) const
16391750 return dispatch ([&](auto bsdf) { return bsdf.sample (wo, rx, ry, rz); });
16401751}
16411752
1642-
16431753OSL_NAMESPACE_END
0 commit comments