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

Initial refraction approximation in GLSL #918

Merged
merged 3 commits into from
Apr 25, 2022
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
3 changes: 2 additions & 1 deletion javascript/MaterialXView/source/viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,8 @@ export class Material
u_envRadiance: { value: radianceTexture },
u_envRadianceMips: { value: Math.trunc(Math.log2(Math.max(radianceTexture.image.width, radianceTexture.image.height))) + 1 },
u_envRadianceSamples: { value: 16 },
u_envIrradiance: { value: irradianceTexture }
u_envIrradiance: { value: irradianceTexture },
u_refractionEnv: { value: true }
});

// Create Three JS Material
Expand Down
7 changes: 5 additions & 2 deletions libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distributio

// Compute the half vector and incoming light direction.
vec3 H = mx_ggx_importance_sample_NDF(Xi, alpha);
vec3 L = -reflect(V, H);
vec3 L = fd.refraction ? mx_refraction_solid_sphere(-V, H, fd.ior.x) : -reflect(V, H);
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a note for the future that we need to split this switch in two later to support thin-walled materials. We then need to have separate control of transmission and refraction on/off, since we can have transmission without refraction in that case, but still transmission with a roughness.


// Compute dot products for this sample.
float NdotH = clamp(H.z, M_FLOAT_EPS, 1.0);
Expand All @@ -54,13 +54,16 @@ vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distributio
// Compute the geometric term.
float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);

// Compute the combined FG term, which is inverted for refraction.
vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : F * G;

// Add the radiance contribution of this sample.
// From https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
// incidentLight = sampleColor * NdotL
// microfacetSpecular = D * F * G / (4 * NdotL * NdotV)
// pdf = D * NdotH / (4 * VdotH)
// radiance = incidentLight * microfacetSpecular / pdf
radiance += sampleColor * F * G * VdotH / (NdotV * NdotH);
radiance += sampleColor * FG * VdotH / (NdotV * NdotH);
}

// Normalize and return the final radiance.
Expand Down
20 changes: 19 additions & 1 deletion libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ struct FresnelData
// Thin film
float tf_thickness;
float tf_ior;

// Refraction
bool refraction;
};

// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
Expand Down Expand Up @@ -217,6 +220,13 @@ float mx_ior_to_f0(float ior)
return mx_square((ior - 1.0) / (ior + 1.0));
}

// Convert normal-incidence reflectivity to real-valued index of refraction.
float mx_f0_to_ior(float F0)
{
float sqrtF0 = sqrt(F0);
return (1.0 + sqrtF0) / (1.0 - sqrtF0);
}

// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
float mx_fresnel_dielectric(float cosTheta, float ior)
{
Expand Down Expand Up @@ -387,7 +397,7 @@ vec3 mx_fresnel_airy(float cosTheta, vec3 ior, vec3 extinction, float tf_thickne

FresnelData mx_init_fresnel_data(int model)
{
return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0);
return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
}

FresnelData mx_init_fresnel_dielectric(float ior)
Expand Down Expand Up @@ -462,6 +472,14 @@ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
}
}

// Compute the refraction of a ray through a solid sphere.
vec3 mx_refraction_solid_sphere(vec3 R, vec3 N, float ior)
{
R = refract(R, N, 1.0 / ior);
vec3 N1 = normalize(R * dot(R, N) - N * 0.5);
return refract(R, N1, ior);
}

vec2 mx_latlong_projection(vec3 dir)
{
float latitude = -asin(dir.y) * M_PI_INV + 0.5;
Expand Down
30 changes: 18 additions & 12 deletions libraries/pbrlib/genglsl/mx_dielectric_bsdf.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, floa
}
else
{
fd = mx_init_fresnel_dielectric(ior);
fd = mx_init_fresnel_dielectric(ior);
}
vec3 F = mx_compute_fresnel(VdotH, fd);
float D = mx_ggx_NDF(Ht, safeAlpha);
Expand All @@ -44,14 +44,7 @@ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, floa

void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (scatter_mode == 1)
{
bsdf.response = tint * weight;
bsdf.throughput = bsdf.response;
return;
}

if (weight < M_FLOAT_EPS)
if (weight < M_FLOAT_EPS || scatter_mode == 0)
{
return;
}
Expand All @@ -61,20 +54,29 @@ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior,

FresnelData fd;
if (bsdf.thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
}
else
{
fd = mx_init_fresnel_dielectric(ior);

}
vec3 F = mx_compute_fresnel(NdotV, fd);

vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);

float F0 = mx_ior_to_f0(ior);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
bsdf.throughput = 1.0 - dirAlbedo * weight;

bsdf.response = (scatter_mode == 2) ? tint * weight * bsdf.throughput : vec3(0.0);
// For now, we approximate the appearance of dielectric transmission as
// glossy environment map refraction, ignoring any scene geometry that
// might be visible through the surface.
fd.refraction = true;
vec3 Li = $refractionEnv ? mx_environment_radiance(N, V, X, safeAlpha, distribution, fd) : $refractionColor;
Copy link
Contributor

Choose a reason for hiding this comment

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

Out of curiosity why is there an option to replace the refracted environment with a uniform color? We don't have a corresponding control for environment reflections. Or is this just for debugging for now?

Copy link
Member Author

Choose a reason for hiding this comment

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

That option was primarily added for our GLSL/OSL reference renders, since OSL testrender refracts the constant-color background instead of the distant environment.

Copy link
Contributor

Choose a reason for hiding this comment

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

Aha! Maybe we should fix this so the environment is visible in both reflection and transmission, for both GLSL and OSL test suite rendering. Lets do that in a separate PR though.

bsdf.response = Li * tint * weight;
}

void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
Expand All @@ -90,14 +92,18 @@ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec

FresnelData fd;
if (bsdf.thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
}
else
{
fd = mx_init_fresnel_dielectric(ior);

}
vec3 F = mx_compute_fresnel(NdotV, fd);

vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);

float F0 = mx_ior_to_f0(ior);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
Expand Down
19 changes: 10 additions & 9 deletions libraries/pbrlib/genglsl/mx_generalized_schlick_bsdf.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,7 @@ void mx_generalized_schlick_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlus

void mx_generalized_schlick_bsdf_transmission(vec3 V, float weight, vec3 color0, vec3 color90, float exponent, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (scatter_mode == 1)
{
bsdf.response = color0 * weight;
bsdf.throughput = bsdf.response;
return;
}

if (weight < M_FLOAT_EPS)
if (weight < M_FLOAT_EPS || scatter_mode == 0)
{
return;
}
Expand All @@ -56,12 +49,20 @@ void mx_generalized_schlick_bsdf_transmission(vec3 V, float weight, vec3 color0,

vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);

vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, color0, color90) * comp;
float avgDirAlbedo = dot(dirAlbedo, vec3(1.0 / 3.0));
bsdf.throughput = vec3(1.0 - avgDirAlbedo * weight);

bsdf.response = (scatter_mode == 2) ? color0 * weight * bsdf.throughput : vec3(0.0);
// For now, we approximate the appearance of Schlick transmission as
// glossy environment map refraction, ignoring any scene geometry that
// might be visible through the surface.
float avgF0 = dot(color0, vec3(1.0 / 3.0));
fd.refraction = true;
fd.ior.x = mx_f0_to_ior(avgF0);
vec3 Li = $refractionEnv ? mx_environment_radiance(N, V, X, safeAlpha, distribution, fd) : $refractionColor;
bsdf.response = Li * color0 * weight;
}

void mx_generalized_schlick_bsdf_indirect(vec3 V, float weight, vec3 color0, vec3 color90, float exponent, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<materialx version="1.38" colorspace="lin_rec709">
<standard_surface name="SR_glass_tinted" type="surfaceshader">
<input name="base" type="float" value="0" />
<input name="specular" type="float" value="1" />
<input name="specular_color" type="color3" value="1, 1, 1" />
<input name="specular_roughness" type="float" value="0.15" />
<input name="specular_IOR" type="float" value="1.54" />
<input name="transmission" type="float" value="1" />
<input name="transmission_color" type="color3" value="0.2, 0.1, 1" />
</standard_surface>
<surfacematerial name="GlassTinted" type="material">
<input name="surfaceshader" type="surfaceshader" nodename="SR_glass_tinted" />
</surfacematerial>
</materialx>
121 changes: 21 additions & 100 deletions resources/Materials/TestSuite/pbrlib/bsdf/dielectric.mtlx
Original file line number Diff line number Diff line change
@@ -1,122 +1,43 @@
<?xml version="1.0"?>
<materialx version="1.38">
<!-- Test dielectric_bsdf in various scatter modes -->
<nodegraph name="dielectric_bsdf_R">
<dielectric_bsdf name="bsdf1" type="BSDF">
<nodegraph name="dielectric_bsdf">
<dielectric_bsdf name="dielectric_R" type="BSDF">
<input name="weight" type="float" value="1.0" />
<input name="tint" type="color3" value="0.7, 0.7, 0.7" />
<input name="ior" type="float" value="1.7" />
<input name="scatter_mode" type="string" value="R" />
</dielectric_bsdf>
<surface name="surface1" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="bsdf1" />
<surface name="surface_R" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="dielectric_R" />
</surface>
<output name="out" type="surfaceshader" nodename="surface1" />
</nodegraph>
<nodegraph name="dielectric_bsdf_T">
<dielectric_bsdf name="bsdf1" type="BSDF">
<output name="R_out" type="surfaceshader" nodename="surface_R" />

<dielectric_bsdf name="dielectric_T" type="BSDF">
<input name="weight" type="float" value="1.0" />
<input name="tint" type="color3" value="0.7, 0.7, 0.7" />
<input name="ior" type="float" value="1.7" />
<input name="scatter_mode" type="string" value="T" />
</dielectric_bsdf>
<surface name="surface1" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="bsdf1" />
<surface name="surface_T" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="dielectric_T" />
</surface>
<output name="out" type="surfaceshader" nodename="surface1" />
</nodegraph>
<nodegraph name="dielectric_bsdf_RT">
<dielectric_bsdf name="bsdf1" type="BSDF">
<output name="T_out" type="surfaceshader" nodename="surface_T" />

<dielectric_bsdf name="dielectric_RT" type="BSDF">
<input name="weight" type="float" value="1.0" />
<input name="tint" type="color3" value="0.7, 0.7, 0.7" />
<input name="ior" type="float" value="1.7" />
<input name="scatter_mode" type="string" value="RT" />
</dielectric_bsdf>
<surface name="surface1" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="bsdf1" />
</surface>
<output name="out" type="surfaceshader" nodename="surface1" />
</nodegraph>
<nodegraph name="dielectric_bsdf_layeredRT">
<dielectric_bsdf name="bsdf1" type="BSDF">
<input name="weight" type="float" value="1.0" />
<input name="tint" type="color3" value="0.7, 0.7, 0.7" />
<input name="ior" type="float" value="1.7" />
<input name="scatter_mode" type="string" value="R" />
</dielectric_bsdf>
<dielectric_bsdf name="bsdf2" type="BSDF">
<input name="weight" type="float" value="1.0" />
<input name="tint" type="color3" value="0.7, 0.7, 0.7" />
<input name="ior" type="float" value="1.7" />
<input name="scatter_mode" type="string" value="T" />
</dielectric_bsdf>
<layer name="layer1" type="BSDF">
<input name="top" type="BSDF" nodename="bsdf1" />
<input name="base" type="BSDF" nodename="bsdf2" />
</layer>
<surface name="surface1" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="layer1" />
<surface name="surface_RT" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="dielectric_RT" />
</surface>
<output name="out" type="surfaceshader" nodename="surface1" />
</nodegraph>
<output name="RT_out" type="surfaceshader" nodename="surface_RT" />

<!-- Test generalized_schlick_bsdf in various scatter modes -->
<nodegraph name="generalized_schlick_bsdf_R">
<generalized_schlick_bsdf name="bsdf1" type="BSDF">
<input name="weight" type="float" value="1.0" />
<input name="color0" type="color3" value="0.7, 0.7, 0.7" />
<input name="color90" type="color3" value="1.0, 1.0, 1.0" />
<input name="scatter_mode" type="string" value="R" />
</generalized_schlick_bsdf>
<surface name="surface1" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="bsdf1" />
</surface>
<output name="out" type="surfaceshader" nodename="surface1" />
</nodegraph>
<nodegraph name="generalized_schlick_bsdf_T">
<generalized_schlick_bsdf name="bsdf1" type="BSDF">
<input name="weight" type="float" value="1.0" />
<input name="color0" type="color3" value="0.7, 0.7, 0.7" />
<input name="color90" type="color3" value="1.0, 1.0, 1.0" />
<input name="scatter_mode" type="string" value="T" />
</generalized_schlick_bsdf>
<surface name="surface1" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="bsdf1" />
</surface>
<output name="out" type="surfaceshader" nodename="surface1" />
</nodegraph>
<nodegraph name="generalized_schlick_bsdf_RT">
<generalized_schlick_bsdf name="bsdf1" type="BSDF">
<input name="weight" type="float" value="1.0" />
<input name="color0" type="color3" value="0.7, 0.7, 0.7" />
<input name="color90" type="color3" value="1.0, 1.0, 1.0" />
<input name="scatter_mode" type="string" value="RT" />
</generalized_schlick_bsdf>
<surface name="surface1" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="bsdf1" />
</surface>
<output name="out" type="surfaceshader" nodename="surface1" />
</nodegraph>
<nodegraph name="generalized_schlick_bsdf_layeredRT">
<generalized_schlick_bsdf name="bsdf1" type="BSDF">
<input name="weight" type="float" value="1.0" />
<input name="color0" type="color3" value="0.7, 0.7, 0.7" />
<input name="color90" type="color3" value="1.0, 1.0, 1.0" />
<input name="scatter_mode" type="string" value="R" />
</generalized_schlick_bsdf>
<generalized_schlick_bsdf name="bsdf2" type="BSDF">
<input name="weight" type="float" value="1.0" />
<input name="color0" type="color3" value="1.0, 1.0, 1.0" />
<input name="color90" type="color3" value="1.0, 1.0, 1.0" />
<input name="scatter_mode" type="string" value="T" />
</generalized_schlick_bsdf>
<layer name="layer1" type="BSDF">
<input name="top" type="BSDF" nodename="bsdf1" />
<input name="base" type="BSDF" nodename="bsdf2" />
<layer name="layer_RT" type="BSDF">
<input name="top" type="BSDF" nodename="dielectric_R" />
<input name="base" type="BSDF" nodename="dielectric_T" />
</layer>
<surface name="surface1" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="layer1" />
<surface name="surface_layer_RT" type="surfaceshader">
<input name="bsdf" type="BSDF" nodename="layer_RT" />
</surface>
<output name="out" type="surfaceshader" nodename="surface1" />
<output name="layer_RT_out" type="surfaceshader" nodename="surface_layer_RT" />
</nodegraph>
</materialx>
Loading