AcademySoftwareFoundation / OpenPBR

Specification and reference implementation for the OpenPBR Surface shading model
Apache License 2.0
459 stars 18 forks source link

Visual discontinuity at `transmission_depth` of zero #179

Closed jstone-lucasfilm closed 3 months ago

jstone-lucasfilm commented 5 months ago

I'm not yet sure of the cause of this visual discontinuity, whether it's in the MaterialX graph for OpenPBR or perhaps in the real-time approximation of the MaterialX Physically Based Shading nodes, so I wanted to report it here for discussion.

When transmission_depth is exactly zero, the transmission_color input has an intuitive visual effect in real-time OpenPBR renders, but this effect disappears entirely when transmission_depth is set to any non-zero value, creating a visual discontinuity as the artist moves the slider.

This issue can be seen most easily in the open_pbr_honey.mtlx example material, linked here: https://academysoftwarefoundation.github.io/MaterialX/?file=Materials/Examples/OpenPbr/open_pbr_honey.mtlx

If you open the Property Editor and set transmission_depth to exactly zero, then the honey color of the transmission component becomes visible, but it disappears again when transmission_depth is set to a value slightly above zero.

OpenPBR Honey (zero transmission depth): image

OpenPBR Honey (epsilon transmission depth) image

virtualzavie commented 5 months ago

I believe this is the intended behaviour, where transmission_depth == 0 is a special case where the transmission_color is used as a simple multiplier, whereas any other value is the depth at which the transmission_color happens, meaning the resulting appearance will depend on the depth.

Quoting the parameter description:

Controls the depth into the volume at which the transmission_color is realized; if zero, acts as a constant (on-surface) transmission tint.

jstone-lucasfilm commented 5 months ago

That's great additional context, thanks @virtualzavie.

Does this mean that renderers will require support for volumetric rendering techniques (i.e. absorption and scattering) in order to correctly handle examples such as open_pbr_honey.mtlx?

We can certainly add approximations of these techniques to our MaterialX GLSL/ESSL/MSL codebase, but this is a higher bar than Autodesk Standard Surface set for real-time renderers, since its corresponding transmission_depth term was simply ignored in non-volumetric renderers.

virtualzavie commented 5 months ago

My understanding of light transport in volumes is limited, but I think so. As long as only transmission_depth and transmission_color are involved, I think it's straightforward (no pun intended), but as soon as transmission_scatter is modified, volumetric effects come into play.

@portsmouth and @peterkutz can correct me if I'm wrong.

jstone-lucasfilm commented 5 months ago

This may end up becoming a feature request for the MaterialX codebase, since ultimately we need to add simple approximations of absorption and scattering in GLSL/ESSL/MSL, but I'll keep this GitHub Issue open to gather thoughts from Jamie and Peter as well.

portsmouth commented 5 months ago

Yes this discontinuity exists in Standard Surface as well, and was inherited into OpenPBR.

We say:

The transmission_depth is the distance traveled inside the medium by white light before its color becomes exactly transmission_color by Beer's law .... However when transmission_depth is zero, it is assumed that the interior medium is absent ... and transmission_color is used instead to non-physically tint the dielectric refraction Fresnel factor multiplicatively by a constant amount (ignoring the dielectric energy balance).

So zero transmission depth produces the tinting behavior, whereas as the depth $\epsilon$ is increased away from zero, the medium starts extremely dense (infinite in the $\epsilon \rightarrow 0$ limit..) and gets progressively less dense.

We noted early on that this is rather unintuitive, and suggested to replace the depth with a density (as is more traditional for volumes), e.g. in https://github.com/AcademySoftwareFoundation/OpenPBR/issues/71. But it seemed hard to get a consensus about this.

NB, in your honey example, if you implemented the volume you would find that for transmission_depth greater than zero but tiny (and transmission_color changed from the (1,1,1) default), the material appears dark inside (i.e. volume is almost fully absorptive), rather than clear.

Here for example is the GLSL implementation I used to set up the volume in my viewer:

void fill_transmission_medium(inout Volume internal_medium)
{
    // Set up the volumetric medium according to the "Translucent Base" section of the OpenPBR spec
    if (transmission_depth > 0.0)
    {
        vec3 mu_t = -log(transmission_color) / transmission_depth;
        vec3 mu_s = transmission_scatter / transmission_depth;
        vec3 mu_a = mu_t - mu_s;
        if (minComponent(mu_a) < 0.0)
            mu_a = mu_a - vec3(minComponent(mu_a));
        internal_medium.extinction = mu_a + mu_s;
        internal_medium.albedo = mu_s / (mu_a + mu_s);
        internal_medium.anisotropy = transmission_scatter_anisotropy;
    }
    else
        internal_medium.extinction = vec3(0.0);
}
portsmouth commented 5 months ago

The parametrization is also awkward because the transmission_color = (1,1,1) default kills the volume as well, unless changed from the default. So to get a volume to show up, you need to dial both the depth and the color away from the defaults...

portsmouth commented 4 months ago

In summary, the discontinuity you see in the viewer is due to a) inherent discontinuity in our parametrization (by design), and b) that the viewer has not implemented volumes. Is there more to discuss?

jstone-lucasfilm commented 3 months ago

I'd agree that this is resolved, thanks @portsmouth, and I'll mark the issue as closed.