@@ -1513,6 +1513,127 @@ 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 Medium::Sample sample (Ray& r, Sampler& sampler,
1570+ Intersection& hit) const
1571+ {
1572+ Vec3 rand_vol = sampler.get ();
1573+
1574+ float t_volume = -logf (1 .0f - rand_vol.x ) / params.avg_sigma_t ();
1575+
1576+ Color3 weight;
1577+ Color3 tr;
1578+
1579+ if (t_volume < hit.t ) {
1580+ r.origin = r.point (t_volume);
1581+ tr = transmittance (t_volume);
1582+
1583+ Color3 albedo = params.sigma_s / params.sigma_t ;
1584+
1585+ weight = albedo / tr;
1586+ } else {
1587+ tr = transmittance (hit.t );
1588+ weight = Color3 (1.0 / tr.x , 1.0 / tr.y , 1.0 / tr.z );
1589+ }
1590+
1591+ return Medium::Sample { t_volume, tr, weight };
1592+ }
1593+
1594+ OSL_HOSTDEVICE BSDF::Sample sample_phase_func (const Vec3& wo, float rx,
1595+ float ry,
1596+ float rz) const
1597+ {
1598+ return phase_func.sample (wo, rx, ry, rz);
1599+ }
1600+
1601+ OSL_HOSTDEVICE const MediumParams* get_params () const { return ¶ms; }
1602+
1603+ OSL_HOSTDEVICE Color3 transmittance (float distance) const
1604+ { // Beer-Lambert law
1605+ return Color3 (expf (-params.sigma_t .x * distance),
1606+ expf (-params.sigma_t .y * distance),
1607+ expf (-params.sigma_t .z * distance));
1608+ }
1609+ };
1610+
1611+ struct EmptyMedium final : public Medium {
1612+ MediumParams params;
1613+
1614+ OSL_HOSTDEVICE EmptyMedium (const MediumParams& params)
1615+ : Medium(this ), params(params)
1616+ {
1617+ }
1618+
1619+ OSL_HOSTDEVICE const MediumParams* get_params () const { return ¶ms; }
1620+
1621+ OSL_HOSTDEVICE Medium::Sample sample (Ray& ray, Sampler& sampler,
1622+ Intersection& hit) const
1623+ {
1624+ return { 0 .0f , Color3 (1 .0f ), Color3 (1 .0f ) };
1625+ }
1626+
1627+ OSL_HOSTDEVICE BSDF::Sample sample_phase_func (const Vec3& wo, float rx,
1628+ float ry,
1629+ float rz) const
1630+ {
1631+ return { Vec3 (1 .0f ), Color3 (1 .0f ), 0 .0f , 0 .0f };
1632+ }
1633+
1634+ };
1635+
1636+
15161637OSL_HOSTDEVICE Color3
15171638evaluate_layer_opacity (const ShaderGlobalsType& sg, float path_roughness,
15181639 const ClosureColor* closure)
@@ -1606,8 +1727,8 @@ evaluate_layer_opacity(const ShaderGlobalsType& sg, float path_roughness,
16061727
16071728OSL_HOSTDEVICE void
16081729process_medium_closure (const ShaderGlobalsType& sg, float path_roughness,
1609- ShadingResult& result, const ClosureColor* closure ,
1610- const Color3& w)
1730+ ShadingResult& result, MediumStack& medium_stack ,
1731+ const ClosureColor* closure, const Color3& w)
16111732{
16121733 if (!closure)
16131734 return ;
@@ -1649,37 +1770,72 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
16491770 const ClosureComponent* comp = closure->as_comp ();
16501771 Color3 cw = weight * comp->w ;
16511772 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 ;
1773+ result.medium_data .sigma_t = cw * params.extinction ;
1774+ result.medium_data .sigma_s = params.albedo
1775+ * result.medium_data .sigma_t ;
1776+ result.medium_data .medium_g = params.anisotropy ;
1777+ result.medium_data .priority = 0 ;
1778+
1779+ if (!sg.backfacing ) { // if entering
1780+ if (result.medium_data .is_vaccum ()) {
1781+ medium_stack.add_medium <EmptyMedium>(result.medium_data );
1782+ } else {
1783+ medium_stack.add_medium <HomogeneousMedium>(
1784+ result.medium_data );
1785+ }
1786+ }
1787+
1788+ closure = nullptr ;
16561789 break ;
16571790 }
16581791 case MX_MEDIUM_VDF_ID: {
16591792 const ClosureComponent* comp = closure->as_comp ();
16601793 Color3 cw = weight * comp->w ;
16611794 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 ;
1795+
1796+ result.medium_data .sigma_t
1797+ = Color3 (-OIIO::fast_log (params.transmission_color .x ),
1798+ -OIIO::fast_log (params.transmission_color .y ),
1799+ -OIIO::fast_log (params.transmission_color .z ));
1800+
1801+ result.medium_data .sigma_t *= cw / params.transmission_depth ;
1802+ result.medium_data .sigma_s = params.albedo
1803+ * result.medium_data .sigma_t ;
1804+ result.medium_data .medium_g = params.anisotropy ;
1805+
1806+ result.medium_data .refraction_ior = sg.backfacing
1807+ ? 1 .0f / params.ior
1808+ : params.ior ;
1809+ result.medium_data .priority = params.priority ;
1810+
1811+ if (!sg.backfacing ) { // if entering
1812+ if (result.medium_data .is_vaccum ()) {
1813+ medium_stack.add_medium <EmptyMedium>(result.medium_data );
1814+ } else {
1815+ medium_stack.add_medium <HomogeneousMedium>(
1816+ result.medium_data );
1817+ }
1818+ }
1819+
1820+ closure = nullptr ;
16741821 break ;
16751822 }
16761823 case MxDielectric::closureid (): {
16771824 const ClosureComponent* comp = closure->as_comp ();
16781825 const MxDielectric::Data& params = *comp->as <MxDielectric::Data>();
16791826 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 ;
1827+ float new_ior = sg.backfacing ? 1 .0f / params.IOR : params.IOR ;
1828+
1829+ result.medium_data .refraction_ior = new_ior;
1830+
1831+ const MediumParams* current_params
1832+ = medium_stack.current_params ();
1833+ if (current_params
1834+ && result.medium_data .priority
1835+ <= current_params->priority ) {
1836+ result.medium_data .refraction_ior
1837+ = current_params->refraction_ior ;
1838+ }
16831839 }
16841840 closure = nullptr ;
16851841 break ;
@@ -1688,13 +1844,23 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
16881844 const ClosureComponent* comp = closure->as_comp ();
16891845 const auto & params = *comp->as <MxGeneralizedSchlickParams>();
16901846 if (!is_black (weight * comp->w * params.transmission_tint )) {
1691- // TODO: properly track a medium stack here ...
16921847 float avg_F0 = clamp ((params.f0 .x + params.f0 .y + params.f0 .z )
16931848 / 3 .0f ,
16941849 0 .0f , 0 .99f );
16951850 float sqrt_F0 = sqrtf (avg_F0);
16961851 float ior = (1 + sqrt_F0) / (1 - sqrt_F0);
1697- result.refraction_ior = sg.backfacing ? 1 .0f / ior : ior;
1852+ float new_ior = sg.backfacing ? 1 .0f / ior : ior;
1853+
1854+ result.medium_data .refraction_ior = new_ior;
1855+
1856+ const MediumParams* current_params
1857+ = medium_stack.current_params ();
1858+ if (current_params
1859+ && result.medium_data .priority
1860+ <= current_params->priority ) {
1861+ result.medium_data .refraction_ior
1862+ = current_params->refraction_ior ;
1863+ }
16981864 }
16991865 closure = nullptr ;
17001866 break ;
@@ -1711,8 +1877,9 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
17111877// recursively walk through the closure tree, creating bsdfs as we go
17121878OSL_HOSTDEVICE void
17131879process_bsdf_closure (const ShaderGlobalsType& sg, float path_roughness,
1714- ShadingResult& result, const ClosureColor* closure,
1715- const Color3& w, bool light_only)
1880+ ShadingResult& result, MediumStack& medium_stack,
1881+ const ClosureColor* closure, const Color3& w,
1882+ bool light_only)
17161883{
17171884 static const ustringhash uh_ggx (" ggx" );
17181885 static const ustringhash uh_beckmann (" beckmann" );
@@ -1845,9 +2012,16 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
18452012 case MxDielectric::closureid (): {
18462013 const MxDielectric::Data& params
18472014 = *comp->as <MxDielectric::Data>();
1848- ok = result.bsdf .add_bsdf <MxDielectric>(cw, params, -sg.I ,
1849- sg.backfacing ,
1850- path_roughness);
2015+
2016+ if (medium_stack.false_intersection_with (
2017+ result.medium_data )) {
2018+ ok = result.bsdf .add_bsdf <Transparent>(cw);
2019+ } else {
2020+ ok = result.bsdf .add_bsdf <MxDielectric>(cw, params,
2021+ -sg.I ,
2022+ sg.backfacing ,
2023+ path_roughness);
2024+ }
18512025 break ;
18522026 }
18532027 case MxConductor::closureid (): {
@@ -1860,15 +2034,21 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
18602034 case MX_GENERALIZED_SCHLICK_ID: {
18612035 const MxGeneralizedSchlickParams& params
18622036 = *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 );
2037+
2038+ if (medium_stack.false_intersection_with (
2039+ result.medium_data )) {
2040+ ok = result.bsdf .add_bsdf <Transparent>(cw);
2041+ } else {
2042+ if (is_black (params.transmission_tint )) {
2043+ ok = result.bsdf .add_bsdf <MxMicrofacet<
2044+ MxGeneralizedSchlickParams, GGXDist, false >>(
2045+ cw, params, 1 .0f );
2046+ } else {
2047+ ok = result.bsdf .add_bsdf <MxMicrofacet<
2048+ MxGeneralizedSchlickParams, GGXDist, true >>(
2049+ cw, params, result.medium_data .refraction_ior );
2050+ }
2051+ }
18722052 break ;
18732053 };
18742054 case MX_TRANSLUCENT_ID: {
@@ -1957,11 +2137,14 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
19572137
19582138OSL_HOSTDEVICE void
19592139process_closure (const ShaderGlobalsType& sg, float path_roughness,
1960- ShadingResult& result, const ClosureColor* Ci, bool light_only)
2140+ ShadingResult& result, MediumStack& medium_stack,
2141+ const ClosureColor* Ci, bool light_only)
19612142{
19622143 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);
2144+ process_medium_closure (sg, path_roughness, result, medium_stack, Ci,
2145+ Color3 (1 ));
2146+ process_bsdf_closure (sg, path_roughness, result, medium_stack, Ci,
2147+ Color3 (1 ), light_only);
19652148}
19662149
19672150OSL_HOSTDEVICE Vec3
@@ -2022,5 +2205,23 @@ BSDF::sample_vrtl(const Vec3& wo, float rx, float ry, float rz) const
20222205 return dispatch ([&](auto bsdf) { return bsdf.sample (wo, rx, ry, rz); });
20232206}
20242207
2208+ OSL_HOSTDEVICE Medium::Sample
2209+ Medium::sample_vrtl (Ray& ray, Sampler& sampler, Intersection& hit) const
2210+ {
2211+ return dispatch (
2212+ [&](const auto & medium) { return medium.sample (ray, sampler, hit); });
2213+ }
2214+
2215+ OSL_HOSTDEVICE BSDF::Sample
2216+ Medium::sample_phase_func_vrtl (const Vec3& wo, float rx, float ry, float rz) const
2217+ {
2218+ return dispatch ([&](auto medium) { return medium.sample_phase_func (wo, rx, ry, rz); });
2219+ }
2220+
2221+ OSL_HOSTDEVICE const MediumParams*
2222+ Medium::get_params_vrtl () const
2223+ {
2224+ return dispatch ([&](const auto & medium) { return medium.get_params (); });
2225+ }
20252226
20262227OSL_NAMESPACE_END
0 commit comments