Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shader generation support for new OSL closures in MaterialXGenOsl #1039

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ option(MATERIALX_INSTALL_PYTHON "Install the MaterialX Python package as a third
option(MATERIALX_TEST_RENDER "Run rendering tests for MaterialX Render module. GPU required for graphics validation." ON)
option(MATERIALX_WARNINGS_AS_ERRORS "Interpret all compiler warnings as errors." OFF)
option(MATERIALX_DYNAMIC_ANALYSIS "Build MaterialX libraries with dynamic analysis on supporting platforms." OFF)
option(MATERIALX_OSL_LEGACY_CLOSURES "Build OSL shader generation supporting the legacy OSL closures." ON)

set(MATERIALX_PYTHON_VERSION "" CACHE STRING
"Python version to be used in building the MaterialX Python package (e.g. '2.7').")
Expand Down Expand Up @@ -119,6 +120,7 @@ mark_as_advanced(MATERIALX_PYTHON_EXECUTABLE)
mark_as_advanced(MATERIALX_PYTHON_OCIO_DIR)
mark_as_advanced(MATERIALX_PYTHON_PYBIND11_DIR)
mark_as_advanced(MATERIALX_OIIO_DIR)
mark_as_advanced(MATERIALX_OSL_LEGACY_CLOSURES)
mark_as_advanced(MATERIALX_OSL_BINARY_OSLC)
mark_as_advanced(MATERIALX_OSL_BINARY_TESTRENDER)
mark_as_advanced(MATERIALX_OSL_INCLUDE_PATH)
Expand All @@ -139,6 +141,9 @@ endif()
add_definitions(-DMATERIALX_OSL_BINARY_OSLC=\"${MATERIALX_OSL_BINARY_OSLC}\")
add_definitions(-DMATERIALX_OSL_BINARY_TESTRENDER=\"${MATERIALX_OSL_BINARY_TESTRENDER}\")
add_definitions(-DMATERIALX_OSL_INCLUDE_PATH=\"${MATERIALX_OSL_INCLUDE_PATH}\")
if (MATERIALX_OSL_LEGACY_CLOSURES)
add_definitions(-DMATERIALX_OSL_LEGACY_CLOSURES)
endif()
if(MATERIALX_BUILD_OIIO)
add_definitions(-DMATERIALX_BUILD_OIIO)
endif()
Expand Down
8 changes: 7 additions & 1 deletion libraries/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
DESTINATION "${MATERIALX_INSTALL_STDLIB_PATH}" MESSAGE_NEVER
PATTERN "CMakeLists.txt" EXCLUDE)
PATTERN "CMakeLists.txt" EXCLUDE
PATTERN "pbrlib_genosl_impl.legacy" EXCLUDE)

if (MATERIALX_OSL_LEGACY_CLOSURES)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/pbrlib/genosl/pbrlib_genosl_impl.legacy"
DESTINATION "${MATERIALX_INSTALL_STDLIB_PATH}/pbrlib/genosl/" RENAME pbrlib_genosl_impl.mtlx)
endif()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the code snippets are different when using the legacy OSL closures. This will install a separate mtlx file with implementation elements pointing to the right code.

5 changes: 5 additions & 0 deletions libraries/pbrlib/genosl/legacy/mx_anisotropic_vdf.osl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
void mx_anisotropic_vdf(vector absorption, vector scattering, float anisotropy, output VDF vdf)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old code snippets are move into a "legacy" directory.

{
// Not implemented in vanilla OSL
vdf = 0; // volume_henyey_greenstein(color(absorption), color(scattering), color(0.0), anisotropy);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "lib/mx_microfacet_specular.osl"
#include "../lib/mx_microfacet_specular.osl"

void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
{
Expand Down
36 changes: 36 additions & 0 deletions libraries/pbrlib/genosl/legacy/mx_dielectric_bsdf.osl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "../lib/mx_microfacet_specular.osl"

void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
{
if (scatter_mode == "T")
{
bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
bsdf.throughput = tint * weight;
return;
}

float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
float F0 = mx_ior_to_f0(ior);
float F = mx_fresnel_schlick(NdotV, F0);

// Calculate compensation for multiple scattering.
// This should normally be done inside the closure
// but since vanilla OSL doesen't support this we
// add it here in shader code instead.
vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);

// Calculate throughput from directional albedo.
float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
bsdf.throughput = 1.0 - dirAlbedo * weight;

if (scatter_mode == "R")
{
bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
}
else
{
bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
}
}
38 changes: 38 additions & 0 deletions libraries/pbrlib/genosl/legacy/mx_generalized_schlick_bsdf.osl
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "../lib/mx_microfacet_specular.osl"

void mx_generalized_schlick_bsdf(float weight, color color0, color color90, float exponent, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
{
float avgF0 = dot(color0, color(1.0 / 3.0));
float ior = mx_f0_to_ior(avgF0);

if (scatter_mode == "T")
{
bsdf.response = weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
bsdf.throughput = weight;
return;
}

float NdotV = fabs(dot(N,-I));
color F = mx_fresnel_schlick(NdotV, color0, color90, exponent);

// Calculate compensation for multiple scattering.
// This should normally be done inside the closure
// but since vanilla OSL doesen't support this we
// add it here in shader code instead.
vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);

// Calculate throughput from directional albedo.
color dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, color0, color90) * comp;
float avgDirAlbedo = dot(dirAlbedo, color(1.0 / 3.0));
bsdf.throughput = 1.0 - avgDirAlbedo * weight;

// Calculate the reflection response, setting IOR to zero to disable internal Fresnel.
bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, 0);

if (scatter_mode == "RT")
{
bsdf.response += bsdf.throughput * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 1);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "lib/mx_microfacet_sheen.osl"
#include "../lib/mx_microfacet_sheen.osl"

// TODO: Vanilla OSL doesn't have a proper sheen closure,
// so use 'diffuse' scaled by sheen directional albedo for now.
Expand Down
6 changes: 6 additions & 0 deletions libraries/pbrlib/genosl/legacy/mx_subsurface_bsdf.osl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
{
// TODO: Subsurface closure is not supported by vanilla OSL.
bsdf.response = _color * weight * diffuse(N);
bsdf.throughput = color(0.0);
}
5 changes: 5 additions & 0 deletions libraries/pbrlib/genosl/legacy/mx_surface.osl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
{
float opacity_weight = clamp(opacity, 0.0, 1.0);
result = (bsdf.response + edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
}
5 changes: 3 additions & 2 deletions libraries/pbrlib/genosl/mx_anisotropic_vdf.osl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
void mx_anisotropic_vdf(vector absorption, vector scattering, float anisotropy, output VDF vdf)
{
// Not implemented in vanilla OSL
vdf = 0; // volume_henyey_greenstein(color(absorption), color(scattering), color(0.0), anisotropy);
// TODO: Need to remap parameters to match the new closure,
// or change the MaterialX spec to OSL parameterization.
vdf = 0;
}
31 changes: 5 additions & 26 deletions libraries/pbrlib/genosl/mx_dielectric_bsdf.osl
Original file line number Diff line number Diff line change
@@ -1,36 +1,15 @@
#include "lib/mx_microfacet_specular.osl"

void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
{
if (scatter_mode == "T")
if (scatter_mode == "R")
{
bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
bsdf.throughput = tint * weight;
return;
bsdf = weight * dielectric_bsdf(N, U, tint, color(0.0), roughness.x, roughness.y, ior, distribution);
}

float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
float F0 = mx_ior_to_f0(ior);
float F = mx_fresnel_schlick(NdotV, F0);

// Calculate compensation for multiple scattering.
// This should normally be done inside the closure
// but since vanilla OSL doesen't support this we
// add it here in shader code instead.
vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);

// Calculate throughput from directional albedo.
float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
bsdf.throughput = 1.0 - dirAlbedo * weight;

if (scatter_mode == "R")
else if (scatter_mode == "T")
{
bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
bsdf = weight * dielectric_bsdf(N, U, color(0.0), tint, roughness.x, roughness.y, ior, distribution);
}
else
{
bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
bsdf = weight * dielectric_bsdf(N, U, tint, tint, roughness.x, roughness.y, ior, distribution);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For refraction to occur this has to be paired with a medium closure. Is it expected that the user will have a medium node in the graph if they want refraction?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If refraction with absorption/scattering is requested then it is expected that the user sets a medium_vdf below the dielectric_bsdf, using the layer operator, in the MaterialX graph.

But it would be good if the dielectric_bsdf could support basic refraction without having the medium_vdf present. In that case it would use the same ior for both the reflection and transmission parts. This would ensure backwards compatibility as this is the current behavior before we add the medium_vdf to MaterialX, and it's the behavior used in MDL for example.

I'm imagining the following behavior could be used for the two dielectric BSDF's.

  • dielectric_bsdf If transmission is enabled but no medium is present use the one and only ior for both reflection and transmission.
  • generalized_schlick_bsdf If transmission is enabled but no medium is present use ior=1.0 for transmission and hence get straight transmission without any refraction.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good point. Do you think generalized_schlick_bsdf should always use 1.0? or try to guess the IOR from f0 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my mind it would be better to just have ior=1.0 and get no refraction for generalized_schlick_bsdf if no medium is present. But this is mainly because I find it so unintuitive that f0 will control how light is refracted. It seems very hard to control for an artist and especially since refraction has such a drastic effect on the look. But this is my personal preference and I'm open to other suggestions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should start with the current MaterialX behavior, where IOR is derived from F0 in generalized_schlick_bsdf, but discuss this further in the ASWF Slack channel for this topic. Ideally, I'd like to bring Naty Hoffman and Andre Mazzone from Lucasfilm/ILM into that conversation, since they've given that question additional thought in a production context.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Thanks for the input @jstone-lucasfilm.

}
}
39 changes: 8 additions & 31 deletions libraries/pbrlib/genosl/mx_generalized_schlick_bsdf.osl
Original file line number Diff line number Diff line change
@@ -1,38 +1,15 @@
#include "lib/mx_microfacet_specular.osl"

void mx_generalized_schlick_bsdf(float weight, color color0, color color90, float exponent, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
{
float avgF0 = dot(color0, color(1.0 / 3.0));
float ior = mx_f0_to_ior(avgF0);

if (scatter_mode == "T")
if (scatter_mode == "R")
{
bsdf.response = weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
bsdf.throughput = weight;
return;
bsdf = weight * generalized_schlick_bsdf(N, U, color(1.0), color(0.0), roughness.x, roughness.y, color0, color90, exponent, distribution);
}

float NdotV = fabs(dot(N,-I));
color F = mx_fresnel_schlick(NdotV, color0, color90, exponent);

// Calculate compensation for multiple scattering.
// This should normally be done inside the closure
// but since vanilla OSL doesen't support this we
// add it here in shader code instead.
vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);

// Calculate throughput from directional albedo.
color dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, color0, color90) * comp;
float avgDirAlbedo = dot(dirAlbedo, color(1.0 / 3.0));
bsdf.throughput = 1.0 - avgDirAlbedo * weight;

// Calculate the reflection response, setting IOR to zero to disable internal Fresnel.
bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, 0);

if (scatter_mode == "RT")
else if (scatter_mode == "T")
{
bsdf.response += bsdf.throughput * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 1);
bsdf = weight * generalized_schlick_bsdf(N, U, color(0.0), color(1.0), roughness.x, roughness.y, color0, color90, exponent, distribution);
}
else
{
bsdf = weight * generalized_schlick_bsdf(N, U, color(1.0), color(1.0), roughness.x, roughness.y, color0, color90, exponent, distribution);
}
}
3 changes: 1 addition & 2 deletions libraries/pbrlib/genosl/mx_subsurface_bsdf.osl
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
{
// TODO: Subsurface closure is not supported by vanilla OSL.
bsdf.response = _color * weight * diffuse(N);
bsdf.throughput = color(0.0);
bsdf = _color * weight * diffuse(N);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testrender will recognize the subsurface closure (even if it happens to use diffuse in the implementation right now). So it would be safe to use it.

}
2 changes: 1 addition & 1 deletion libraries/pbrlib/genosl/mx_surface.osl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
{
float opacity_weight = clamp(opacity, 0.0, 1.0);
result = (bsdf.response + edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
result = (bsdf + edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
}
1 change: 0 additions & 1 deletion libraries/pbrlib/genosl/mx_uniform_edf.inline

This file was deleted.

71 changes: 71 additions & 0 deletions libraries/pbrlib/genosl/pbrlib_genosl_impl.legacy
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?xml version="1.0"?>
<materialx version="1.38">

<!-- <oren_nayar_diffuse_bsdf> -->
<implementation name="IM_oren_nayar_diffuse_bsdf_genosl" nodedef="ND_oren_nayar_diffuse_bsdf" file="legacy/mx_oren_nayar_diffuse_bsdf.osl" function="mx_oren_nayar_diffuse_bsdf" target="genosl" />

<!-- <burley_diffuse_bsdf> -->
<implementation name="IM_burley_diffuse_bsdf_genosl" nodedef="ND_burley_diffuse_bsdf" file="legacy/mx_burley_diffuse_bsdf.osl" function="mx_burley_diffuse_bsdf" target="genosl" />

<!-- <translucent_bsdf> -->
<implementation name="IM_translucent_bsdf_genosl" nodedef="ND_translucent_bsdf" file="legacy/mx_translucent_bsdf.osl" function="mx_translucent_bsdf" target="genosl" />

<!-- <dielectric_bsdf> -->
<implementation name="IM_dielectric_bsdf_genosl" nodedef="ND_dielectric_bsdf" file="legacy/mx_dielectric_bsdf.osl" function="mx_dielectric_bsdf" target="genosl" />

<!-- <conductor_bsdf> -->
<implementation name="IM_conductor_bsdf_genosl" nodedef="ND_conductor_bsdf" file="legacy/mx_conductor_bsdf.osl" function="mx_conductor_bsdf" target="genosl" />

<!-- <generalized_schlick_bsdf> -->
<implementation name="IM_generalized_schlick_bsdf_genosl" nodedef="ND_generalized_schlick_bsdf" file="legacy/mx_generalized_schlick_bsdf.osl" function="mx_generalized_schlick_bsdf" target="genosl" />

<!-- <subsurface_bsdf> -->
<implementation name="IM_subsurface_bsdf_genosl" nodedef="ND_subsurface_bsdf" file="legacy/mx_subsurface_bsdf.osl" function="mx_subsurface_bsdf" target="genosl" />

<!-- <sheen_bsdf> -->
<implementation name="IM_sheen_bsdf_genosl" nodedef="ND_sheen_bsdf" file="legacy/mx_sheen_bsdf.osl" function="mx_sheen_bsdf" target="genosl" />

<!-- <anisotropic_vdf> -->
<implementation name="IM_anisotropic_vdf_genosl" nodedef="ND_anisotropic_vdf" file="legacy/mx_anisotropic_vdf.osl" function="mx_anisotropic_vdf" target="genosl" />

<!-- <thin_film_bsdf> -->
<implementation name="IM_thin_film_bsdf_genosl" nodedef="ND_thin_film_bsdf" target="genosl" />

<!-- <uniform_edf> -->
<implementation name="IM_uniform_edf_genosl" nodedef="ND_uniform_edf" sourcecode="{{color}} * emission()" target="genosl" />

<!-- <layer> -->
<implementation name="IM_layer_bsdf_genosl" nodedef="ND_layer_bsdf" target="genosl" />
<implementation name="IM_layer_vdf_genosl" nodedef="ND_layer_vdf" target="genosl" />

<!-- <mix> -->
<implementation name="IM_mix_bsdf_genosl" nodedef="ND_mix_bsdf" target="genosl" />
<implementation name="IM_mix_edf_genosl" nodedef="ND_mix_edf" target="genosl" />

<!-- <add> -->
<implementation name="IM_add_bsdf_genosl" nodedef="ND_add_bsdf" target="genosl" />
<implementation name="IM_add_edf_genosl" nodedef="ND_add_edf" target="genosl" />

<!-- <multiply> -->
<implementation name="IM_multiply_bsdfC_genosl" nodedef="ND_multiply_bsdfC" target="genosl" />
<implementation name="IM_multiply_bsdfF_genosl" nodedef="ND_multiply_bsdfF" target="genosl" />
<implementation name="IM_multiply_edfC_genosl" nodedef="ND_multiply_edfC" target="genosl" />
<implementation name="IM_multiply_edfF_genosl" nodedef="ND_multiply_edfF" target="genosl" />

<!-- <surface> -->
<implementation name="IM_surface_genosl" nodedef="ND_surface" file="legacy/mx_surface.osl" function="mx_surface" target="genosl" />

<!-- <displacement> -->
<implementation name="IM_displacement_float_genosl" nodedef="ND_displacement_float" file="mx_displacement_float.osl" function="mx_displacement_float" target="genosl" />
<implementation name="IM_displacement_vector3_genosl" nodedef="ND_displacement_vector3" file="mx_displacement_vector3.osl" function="mx_displacement_vector3" target="genosl" />

<!-- <roughness_anisotropy> -->
<implementation name="IM_roughness_anisotropy_genosl" nodedef="ND_roughness_anisotropy" file="mx_roughness_anisotropy.osl" function="mx_roughness_anisotropy" target="genosl" />

<!-- <roughness_dual> -->
<implementation name="IM_roughness_dual_genosl" nodedef="ND_roughness_dual" file="mx_roughness_dual.osl" function="mx_roughness_dual" target="genosl" />

<!-- <artistic_ior> -->
<implementation name="IM_artistic_ior_genosl" nodedef="ND_artistic_ior" file="mx_artistic_ior.osl" function="mx_artistic_ior" target="genosl" />

</materialx>
Loading