@@ -1513,6 +1513,92 @@ 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 BSDF::Sample sample_phase_func (const Vec3& wo, float rx,
1570+ float ry, float rz) const
1571+ {
1572+ return phase_func.sample (wo, rx, ry, rz);
1573+ }
1574+
1575+ OSL_HOSTDEVICE const MediumParams* get_params () const { return ¶ms; }
1576+ };
1577+
1578+ struct EmptyMedium final : public Medium {
1579+ MediumParams params;
1580+
1581+ OSL_HOSTDEVICE EmptyMedium (const MediumParams& params)
1582+ : Medium(this ), params(params)
1583+ {
1584+ }
1585+
1586+ OSL_HOSTDEVICE const MediumParams* get_params () const { return ¶ms; }
1587+
1588+ OSL_HOSTDEVICE Medium::Sample sample (Ray& ray, Sampler& sampler,
1589+ Intersection& hit) const
1590+ {
1591+ return { 0 .0f , Color3 (1 .0f ), Color3 (1 .0f ) };
1592+ }
1593+
1594+ OSL_HOSTDEVICE BSDF::Sample sample_phase_func (const Vec3& wo, float rx,
1595+ float ry, float rz) const
1596+ {
1597+ return { Vec3 (1 .0f ), Color3 (1 .0f ), 0 .0f , 0 .0f };
1598+ }
1599+ };
1600+
1601+
15161602OSL_HOSTDEVICE Color3
15171603evaluate_layer_opacity (const ShaderGlobalsType& sg, float path_roughness,
15181604 const ClosureColor* closure)
@@ -1606,8 +1692,8 @@ evaluate_layer_opacity(const ShaderGlobalsType& sg, float path_roughness,
16061692
16071693OSL_HOSTDEVICE void
16081694process_medium_closure (const ShaderGlobalsType& sg, float path_roughness,
1609- ShadingResult& result, const ClosureColor* closure ,
1610- const Color3& w)
1695+ ShadingResult& result, MediumStack& medium_stack ,
1696+ const ClosureColor* closure, const Color3& w)
16111697{
16121698 if (!closure)
16131699 return ;
@@ -1649,37 +1735,72 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
16491735 const ClosureComponent* comp = closure->as_comp ();
16501736 Color3 cw = weight * comp->w ;
16511737 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 ;
1738+ result.medium_data .sigma_t = cw * params.extinction ;
1739+ result.medium_data .sigma_s = params.albedo
1740+ * result.medium_data .sigma_t ;
1741+ result.medium_data .medium_g = params.anisotropy ;
1742+ result.medium_data .priority = 0 ;
1743+
1744+ if (!sg.backfacing ) { // if entering
1745+ if (result.medium_data .is_vaccum ()) {
1746+ medium_stack.add_medium <EmptyMedium>(result.medium_data );
1747+ } else {
1748+ medium_stack.add_medium <HomogeneousMedium>(
1749+ result.medium_data );
1750+ }
1751+ }
1752+
1753+ closure = nullptr ;
16561754 break ;
16571755 }
16581756 case MX_MEDIUM_VDF_ID: {
16591757 const ClosureComponent* comp = closure->as_comp ();
16601758 Color3 cw = weight * comp->w ;
16611759 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 ;
1760+
1761+ result.medium_data .sigma_t
1762+ = Color3 (-OIIO::fast_log (params.transmission_color .x ),
1763+ -OIIO::fast_log (params.transmission_color .y ),
1764+ -OIIO::fast_log (params.transmission_color .z ));
1765+
1766+ result.medium_data .sigma_t *= cw / params.transmission_depth ;
1767+ result.medium_data .sigma_s = params.albedo
1768+ * result.medium_data .sigma_t ;
1769+ result.medium_data .medium_g = params.anisotropy ;
1770+
1771+ result.medium_data .refraction_ior = sg.backfacing
1772+ ? 1 .0f / params.ior
1773+ : params.ior ;
1774+ result.medium_data .priority = params.priority ;
1775+
1776+ if (!sg.backfacing ) { // if entering
1777+ if (result.medium_data .is_vaccum ()) {
1778+ medium_stack.add_medium <EmptyMedium>(result.medium_data );
1779+ } else {
1780+ medium_stack.add_medium <HomogeneousMedium>(
1781+ result.medium_data );
1782+ }
1783+ }
1784+
1785+ closure = nullptr ;
16741786 break ;
16751787 }
16761788 case MxDielectric::closureid (): {
16771789 const ClosureComponent* comp = closure->as_comp ();
16781790 const MxDielectric::Data& params = *comp->as <MxDielectric::Data>();
16791791 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 ;
1792+ float new_ior = sg.backfacing ? 1 .0f / params.IOR : params.IOR ;
1793+
1794+ result.medium_data .refraction_ior = new_ior;
1795+
1796+ const MediumParams* current_params
1797+ = medium_stack.get_current_params ();
1798+ if (current_params
1799+ && result.medium_data .priority
1800+ <= current_params->priority ) {
1801+ result.medium_data .refraction_ior
1802+ = current_params->refraction_ior ;
1803+ }
16831804 }
16841805 closure = nullptr ;
16851806 break ;
@@ -1688,13 +1809,23 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
16881809 const ClosureComponent* comp = closure->as_comp ();
16891810 const auto & params = *comp->as <MxGeneralizedSchlickParams>();
16901811 if (!is_black (weight * comp->w * params.transmission_tint )) {
1691- // TODO: properly track a medium stack here ...
16921812 float avg_F0 = clamp ((params.f0 .x + params.f0 .y + params.f0 .z )
16931813 / 3 .0f ,
16941814 0 .0f , 0 .99f );
16951815 float sqrt_F0 = sqrtf (avg_F0);
16961816 float ior = (1 + sqrt_F0) / (1 - sqrt_F0);
1697- result.refraction_ior = sg.backfacing ? 1 .0f / ior : ior;
1817+ float new_ior = sg.backfacing ? 1 .0f / ior : ior;
1818+
1819+ result.medium_data .refraction_ior = new_ior;
1820+
1821+ const MediumParams* current_params
1822+ = medium_stack.get_current_params ();
1823+ if (current_params
1824+ && result.medium_data .priority
1825+ <= current_params->priority ) {
1826+ result.medium_data .refraction_ior
1827+ = current_params->refraction_ior ;
1828+ }
16981829 }
16991830 closure = nullptr ;
17001831 break ;
@@ -1711,8 +1842,9 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
17111842// recursively walk through the closure tree, creating bsdfs as we go
17121843OSL_HOSTDEVICE void
17131844process_bsdf_closure (const ShaderGlobalsType& sg, float path_roughness,
1714- ShadingResult& result, const ClosureColor* closure,
1715- const Color3& w, bool light_only)
1845+ ShadingResult& result, MediumStack& medium_stack,
1846+ const ClosureColor* closure, const Color3& w,
1847+ bool light_only)
17161848{
17171849 static const ustringhash uh_ggx (" ggx" );
17181850 static const ustringhash uh_beckmann (" beckmann" );
@@ -1845,9 +1977,16 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
18451977 case MxDielectric::closureid (): {
18461978 const MxDielectric::Data& params
18471979 = *comp->as <MxDielectric::Data>();
1848- ok = result.bsdf .add_bsdf <MxDielectric>(cw, params, -sg.I ,
1849- sg.backfacing ,
1850- path_roughness);
1980+
1981+ if (medium_stack.false_intersection_with (
1982+ result.medium_data )) {
1983+ ok = result.bsdf .add_bsdf <Transparent>(cw);
1984+ } else {
1985+ ok = result.bsdf .add_bsdf <MxDielectric>(cw, params,
1986+ -sg.I ,
1987+ sg.backfacing ,
1988+ path_roughness);
1989+ }
18511990 break ;
18521991 }
18531992 case MxConductor::closureid (): {
@@ -1860,15 +1999,21 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
18601999 case MX_GENERALIZED_SCHLICK_ID: {
18612000 const MxGeneralizedSchlickParams& params
18622001 = *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 );
2002+
2003+ if (medium_stack.false_intersection_with (
2004+ result.medium_data )) {
2005+ ok = result.bsdf .add_bsdf <Transparent>(cw);
2006+ } else {
2007+ if (is_black (params.transmission_tint )) {
2008+ ok = result.bsdf .add_bsdf <MxMicrofacet<
2009+ MxGeneralizedSchlickParams, GGXDist, false >>(
2010+ cw, params, 1 .0f );
2011+ } else {
2012+ ok = result.bsdf .add_bsdf <MxMicrofacet<
2013+ MxGeneralizedSchlickParams, GGXDist, true >>(
2014+ cw, params, result.medium_data .refraction_ior );
2015+ }
2016+ }
18722017 break ;
18732018 };
18742019 case MX_TRANSLUCENT_ID: {
@@ -1957,11 +2102,14 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
19572102
19582103OSL_HOSTDEVICE void
19592104process_closure (const ShaderGlobalsType& sg, float path_roughness,
1960- ShadingResult& result, const ClosureColor* Ci, bool light_only)
2105+ ShadingResult& result, MediumStack& medium_stack,
2106+ const ClosureColor* Ci, bool light_only)
19612107{
19622108 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);
2109+ process_medium_closure (sg, path_roughness, result, medium_stack, Ci,
2110+ Color3 (1 ));
2111+ process_bsdf_closure (sg, path_roughness, result, medium_stack, Ci,
2112+ Color3 (1 ), light_only);
19652113}
19662114
19672115OSL_HOSTDEVICE Vec3
@@ -2022,5 +2170,19 @@ BSDF::sample_vrtl(const Vec3& wo, float rx, float ry, float rz) const
20222170 return dispatch ([&](auto bsdf) { return bsdf.sample (wo, rx, ry, rz); });
20232171}
20242172
2173+ OSL_HOSTDEVICE BSDF::Sample
2174+ Medium::sample_phase_func_vrtl (const Vec3& wo, float rx, float ry,
2175+ float rz) const
2176+ {
2177+ return dispatch ([&](const auto & medium) {
2178+ return medium.sample_phase_func (wo, rx, ry, rz);
2179+ });
2180+ }
2181+
2182+ OSL_HOSTDEVICE const MediumParams*
2183+ Medium::get_params_vrtl () const
2184+ {
2185+ return dispatch ([&](const auto & medium) { return medium.get_params (); });
2186+ }
20252187
20262188OSL_NAMESPACE_END
0 commit comments