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

Update behavior of specular_weight, specular_ior_level, coat_ior_level #157

Conversation

portsmouth
Copy link
Contributor

@portsmouth portsmouth commented Feb 9, 2024

This implements the changes proposed in

which we agreed to go ahead with in the recent meetings.

The specular_ior_level param is removed, and replaced by specular_weight which works in the same way. I incorporated the facility to increase the specular_weight to values >1, though with a soft-max at 1 -- retaining the facility we had before, though actually more flexible since it doesn't get capped at an arbitrary maximum value of 2 (it just gets capped by the requirement that the Fresnel factor cannot exceed 1). This seems reasonable and potentially convenient (though we may want to verify that it behaves OK in practice).

The specular_weight is now no longer involved in the Fresnel factor multiplier, only specular_color. We state that this is applied only to one specific scattering mode (the initial Fresnel reflection from above), and call it out explicitly as an unphysical convenience.

image

The coat_ior_level param is also removed, simplifying the coat discussion:

image

@portsmouth
Copy link
Contributor Author

Corresponding changes will be needed in the MaterialX graph (and our native implementations). I'll leave those for a separate PR though.

Copy link
Contributor

@virtualzavie virtualzavie left a comment

Choose a reason for hiding this comment

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

Nice.
With this new parametrisation, I believe we need to remove specular_weight from the metal section.

index.html Outdated Show resolved Hide resolved
index.html Outdated Show resolved Hide resolved
index.html Outdated Show resolved Hide resolved
@portsmouth
Copy link
Contributor Author

portsmouth commented Feb 20, 2024

Nice. With this new parametrisation, I believe we need to remove specular_weight from the metal section.

We could do, as it functions currently (for the metal) only as a multiplier of the specular_color edge tint, which is probably not very important to retain, as the specular_color covers the edge-tint perfectly well. Arguably also it's helpful to remove it since if doing e.g. a rusty metal with varying metalness, if the specular_weight is textured it's inconvenient for that to affect the metallic parts and dielectric parts in a different way.

This means we would remove the instances of specular_weight outlined here:

image

@portsmouth
Copy link
Contributor Author

portsmouth commented Feb 21, 2024

In bffd9e1 I changed as follows:

  • Do not allow specular_weight > 1. This seems preferable, as otherwise it will generally cause the Fresnel to saturate at 1 producing an unnatural/discontinuous looking band of white at grazing edges, which I think it is best to prevent. It is also simpler to just think of the specular_weight as a suppression factor of the reflectivity in $[0,1]$.

  • Have the metal Fresnel multiplied by an overall factor of specular_weight (again in $[0,1]$). This was important to stipulate, as otherwise (as Julien pointed out on Slack), there was no way to avoid the white band at metallic edges as Fresnel always saturates to 1 at grazing (even with black base and specular color), unless suppressed by the weight. In this case, the suppression is "unphysical" (though applied to the Schlick curve which itself is only loosely physically based).

A more physical scheme would amount to somehow lowering the IOR of the metal to 1, as in the dielectric case, producing a narrowing of the Fresnel highlight rather than a suppression, but this is not doable in the standard Schlick representation, so the linear scaling with the weight seems likely the best approach. (Though note that the $F_0$ does actually scale the same way with specular weight, for both metal and dielectric).

image

image

…penPBR into specular_weight_and_color_changes

# Conflicts:
#	index.html
Copy link
Contributor

@virtualzavie virtualzavie left a comment

Choose a reason for hiding this comment

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

While it is a good change overall, there are two propositions I'm not comfortable with:

  • specular_weight affecting the metal lobe, as I strongly suspect it will yield inconsistent behaviour.
  • specular_weight limited to 1, thus preventing from locally increasing specular reflection. I think a use case would be gemstones on a character outfit. There is also the problem of converting existing assets, given that a modulation up to 2 has been common in several major tools for about a decade.

index.html Outdated Show resolved Hide resolved
index.html Outdated Show resolved Hide resolved
\end{equation}
This formulation has the useful property that it reduces to the regular Schlick reflectivity at the default values of $\mathtt{specular\_weight}$ and $\mathtt{specular\_color}$. Note that the edge cannot be brighter than the standard Schlick term, but this is generally true in real metals. We consider this a benefit of this parametrization, as it makes it impossible to produce physically implausible metals with excessively bright edges.
The final metallic Fresnel term we employ is then given by an overall multiplication by **`specular_weight`**, ensuring that entire metallic lobe is suppressed as the weight goes to zero:
Copy link
Contributor

@virtualzavie virtualzavie Apr 9, 2024

Choose a reason for hiding this comment

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

I'm concerned about the implications of this description.
Suppose an artist makes a material with a combination of dielectric and metal. Then after the fact, they want to tune the specular reflection with a uniform specular_weight. They are not going to observe the same behaviour on the dielectric and metal parts, which I think could be questionable.

I should also note, given the recent discussions on the metal-rough model, that there is a bit of inconsistency in how the metal layer is parametrised: other layers have a weight that acts as a coverage parameter. One could argue metalness is conceptually close to that role.

More discussion is needed here, both short and longer term.

Copy link
Contributor Author

@portsmouth portsmouth Apr 9, 2024

Choose a reason for hiding this comment

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

Suppose an artist makes a material with a combination of dielectric and metal. Then after the fact, they way to tune the specular reflection with a uniform specular_weight. They are not going to observe the same behaviour on the dielectric and metal parts, which I think could be questionable.

I think actually it's more sensible now than it was before, since now the specular_weight directly dials the normal-incidence reflectance (i.e. F0) of both the dielectric and the metal.

Before, we had it only affecting the F82-tint color of the metal, but not the F0. I found that more inconsistent, since the F0 is the dominant effect. (Also, it didn't allow the metal lobe to be zeroed, as you noticed, so we have to have the weight multiply the entire lobe instead, which is more consistent with the dielectric F0 anyway).

The specular_weight multiplies the metallic lobe though, rather than operating via the IOR, so the effect is of course slightly different (suppression at grazing angles, rather than "tightening of the white highlight"). But we don't have an IOR control for the metal, so it's not possible to make them match to that extent.

Copy link
Contributor Author

@portsmouth portsmouth Apr 9, 2024

Choose a reason for hiding this comment

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

I should also note, given the recent discussions on the metal-rough model, that there is a bit of inconsistency in how the metal layer is parametrised: other layers have a weight that acts as a coverage parameter. One could argue metalness is conceptually close to that role.

No, the metal is a component of the base, mixed with the "non-metallic base", according to the base_metalness. So base_metalness operates as a mix weight, not a coverage weight. Similarly for transmission_weight and subsurface_weight.

A coverage weight only makes sense for a layer which can optionally "cover" another, i.e. the coat and fuzz layers.

Note that we also decided against trying to interpret specular_weight as the coverage weight of the diffuse base with dielectric (since the dielectric is not really functioning as a layer, it is really the base substrate in which the scattering media of the transmission/subsurface/glossy-diffuse slabs are embedded). Instead, specular_weight operates by modulating the IOR of the unified base dielectric.

Copy link
Contributor Author

@portsmouth portsmouth Apr 9, 2024

Choose a reason for hiding this comment

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

Then after the fact, they way to tune the specular reflection with a uniform specular_weight. They are not going to observe the same behaviour on the dielectric and metal parts, which I think could be questionable.

I see what you mean though, if they have a textured metalness, then the single specular_weight affects both the metal and dielectric parts (now in roughly the same way -- by multiplying the F0), which maybe they don't want. They'd have to modulate the weight by the metalness map.

This goes back to our discussions (on Slack and in #140) about having a separate optional parametrization for metal. If you really want this use case of e.g. metal and non-metal bits on the same object, you have to deal with blending of various maps to get that look, and then get issues in the transition regions because you are trying to represent a 50/50 mix of metal and dielectric with blended maps driving both the metal and dielectric BRDFs, instead of independent maps for the two BRDFs.

A solution is something like the proposed additional metal params in #140 (comment). But that adds a fair bit of complexity to our model. My compromise approach is to accept that these issues are real, but treat this as a limitation of our model that we traded off for ease of use and simplicity. I would even propose to say something in the spec about textured metalness being problematic, and that we really intended metalness to be more like a binary selector over the entire surface, or very smoothly varying, and not something which is allowed to have harsh transitions (though I know that in reality, people certainly use metalness to represent areas of metal/dielectric "coat" or "substrate" living in a background of dielectric/metal).

For better quality results, people can always do more elaborate things like use MaterialX to add layers of dielectric stuff on top of OpenPBR metal, say. (To model rust more correctly for example, physically it should be a layer of semi-opaque rust on top of a substrate metal -- which is just not representable in OpenPBR since our only layers are an absorbing dielectric coat and fuzz).

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I agree this is an improvement over the previous proposition, and I don't see a better alternative.
My concern is whether this could become a subject of complain, and if it does, can we still make a better proposal in a future without being locked by this decision.

That being said, if in the future we decide to go toward separate metal parameters, then this one parameter would likely be overridden for metal anyway, so I guess we're safe.

Co-authored-by: Julien Guertault <9511025+virtualzavie@users.noreply.github.com>
Signed-off-by: Jamie Portsmouth <jamports@mac.com>
@portsmouth
Copy link
Contributor Author

portsmouth commented Apr 9, 2024

specular_weight limited to 1, thus preventing from locally increasing specular reflection. I think a use case would be gemstones on a character outfit. There is also the problem of converting existing assets, given that a modulation up to 2 has been common in several major tools for about a decade.

We've discussed this a fair bit before. I think it would be fine to allow specular_weight to exceed 1 (make it a soft-max at 1, say). We would just need to specify appropriate clamping logic (for both metal and dielectric Fresnel) since the resulting F0 cannot be allowed to exceed 1.

I find the proposed use-cases a bit unconvincing though, as if you're doing gemstones on a costume you could just set the IOR to that of the gemstones, and lower the specular_weight accordingly for the non-gem bits. Or just paint the actual IOR, in a tool where you are not restricted to [0,1] maps.

Also, above I noted:

In the latest commit, I removed that.. [specular_weight > 1] (for both dielectric and metal). As I think it generally means that the Fresnel will at some angle near grazing end up saturating to 1 with a C1 discontinuity, which I would expect to look a bit unnatural (as real Fresnel doesn't saturate like this, except when TIR happens).

Which is true, e.g. if at specular_weight = 1 the F0=0.75, then at specular_weight=1.333 the IOR will saturate to infinity (to achieve F0=1, according to Equation 26 below, assuming we added a clamp so that the thing inside the square root is $\le 1$). So then varying specular_weight > 1.333 does nothing, the Fresnel curve is just maxed at 1. That seems a bit weird.

image

@jstone-lucasfilm
Copy link
Member

Once we're happy with these proposed changes to the specification, I'd recommend that we include the matching updates to the reference implementation in this same pull request. This should allow us to validate the new user controls before merging, and will also insure that our specification and reference implementation remain in sync as we approach a 1.0 release.

index.html Outdated

The Fresnel factor $F(\omega_i, h)$ is determined by the index of refraction of the reflecting material, and its form differs depending on whether the material is a dielectric or conductor [#Walter2007]. Its parametrization in each case is described in the Dielectric base section and Metal section. The _masking-shadowing function_ $G(\omega_i, \omega_o)$ accounts for the probability that the input and output directions are occluded by the microsurface. It is usually derived using the Smith model which determines $G$ given the NDF, and for the GGX NDF equation [GGX] the masking-shadowing function then has a well-known form [#Heitz2014b].
The Fresnel factor $F(\omega_i, h)$ is determined by the complex index of refraction of the reflecting material of each microfacet, and its form differs depending on whether the material is a dielectric or conductor [#Walter2007]. Its parametrization in each case is described in the Dielectric base section and Metal section. The _masking-shadowing function_ $G(\omega_i, \omega_o)$ accounts for the probability that the input and output directions are occluded by the microsurface. It is usually derived using the Smith model which determines $G$ given the NDF, and for the GGX NDF equation [GGX] the masking-shadowing function then has a well-known form [#Heitz2014b].
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is "masking-shadowing function" emphasized, but the other factors are not?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed up in e6a15d3

index.html Outdated

The Fresnel factor $F(\omega_i, h)$ is determined by the index of refraction of the reflecting material, and its form differs depending on whether the material is a dielectric or conductor [#Walter2007]. Its parametrization in each case is described in the Dielectric base section and Metal section. The _masking-shadowing function_ $G(\omega_i, \omega_o)$ accounts for the probability that the input and output directions are occluded by the microsurface. It is usually derived using the Smith model which determines $G$ given the NDF, and for the GGX NDF equation [GGX] the masking-shadowing function then has a well-known form [#Heitz2014b].
The Fresnel factor $F(\omega_i, h)$ is determined by the complex index of refraction of the reflecting material of each microfacet, and its form differs depending on whether the material is a dielectric or conductor [#Walter2007]. Its parametrization in each case is described in the Dielectric base section and Metal section. The _masking-shadowing function_ $G(\omega_i, \omega_o)$ accounts for the probability that the input and output directions are occluded by the microsurface. It is usually derived using the Smith model which determines $G$ given the NDF, and for the GGX NDF equation [GGX] the masking-shadowing function then has a well-known form [#Heitz2014b].
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: have F, G and D in 3 separate paragraphs.

Suggested change
The Fresnel factor $F(\omega_i, h)$ is determined by the complex index of refraction of the reflecting material of each microfacet, and its form differs depending on whether the material is a dielectric or conductor [#Walter2007]. Its parametrization in each case is described in the Dielectric base section and Metal section. The _masking-shadowing function_ $G(\omega_i, \omega_o)$ accounts for the probability that the input and output directions are occluded by the microsurface. It is usually derived using the Smith model which determines $G$ given the NDF, and for the GGX NDF equation [GGX] the masking-shadowing function then has a well-known form [#Heitz2014b].
The Fresnel factor $F(\omega_i, h)$ is determined by the complex index of refraction of the reflecting material of each microfacet, and its form differs depending on whether the material is a dielectric or conductor [#Walter2007]. Its parametrization in each case is described in the Dielectric base section and Metal section.
The _masking-shadowing function_ $G(\omega_i, \omega_o)$ accounts for the probability that the input and output directions are occluded by the microsurface. It is usually derived using the Smith model which determines $G$ given the NDF, and for the GGX NDF equation [GGX] the masking-shadowing function then has a well-known form [#Heitz2014b].

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in ec47ee0.

index.html Outdated

- The specular lobe shape is controlled by the roughness properties of the surface, parametrized by **`specular_roughness`**, **`specular_anisotropy`**, and **`specular_rotation`** (see the section on the [Microfacet model](index.html#model/microfacetmodel) NDF).
- The **`specular_ior`** parameter controls the index of refraction (IOR) of the dielectric. The **`specular_weight`** parameter provides a convenient, texturable linear $[0, 1]$ multiplier of the dielectric reflectivity at normal incidence via modulation of this reference IOR. When **`specular_weight`** is $0$, the specular reflection disappears entirely, as the IOR of the dielectric is then equal to that of the surrounding medium.
Copy link
Contributor

Choose a reason for hiding this comment

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

Note to ourselves: this is where we want to briefly define the behaviour above 1 (how we prevent non sensical values would probably be better explained with equation modulated_ior).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added changes to explicitly allow specular_weight > 1 in 4361177 and cceeb42

image

Copy link
Contributor

Choose a reason for hiding this comment

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

Great, thank you.

index.html Outdated
Comment on lines 527 to 528
**`specular_weight`** | Weight | `float` | $ [0, 1] $ | | $ 1 $ | Scalar multiplier to the dielectric Fresnel factor
**`specular_weight`** | Weight | `float` | $ [0, 1] $ | | $ 1 $ | Modulates the dielectric reflectivity at normal incidence
**`specular_color`** | Color | `color3` | $ [0, 1]^3 $ | | $ (1, 1, 1) $ | Tints the dielectric Fresnel factor
Copy link
Contributor

Choose a reason for hiding this comment

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

Bit of nitpicking: there's an inconsistency in that specular_weight and specular_color are described by verbs when other parameters are described by nouns.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't find it a problem personally.. They are just descriptions, with slightly different phrasing (it's a scalar multiplier, versus it modulates.. etc. Both work).

index.html Outdated
where the range of $\xi_c$ is clamped according to $\xi_c \in [0, \mathrm{min}(1, 1/(2 F_c)]$. Inserting this modulated IOR ratio $\eta^\prime_c$ in the coat Fresnel factor then produces the desired reflectivity modulation.

If there is a fractional $\mathtt{coat\_weight}$ $\mathtt{C}$, then the surrounding IOR of the base dielectric or metal varies statistically across the surface depending on whether the coat is locally present (the fuzz layer can be assumed to have the ambient IOR $n_\mathrm{ambient}$). A reasonable approximation of this is to take the surrounding effective coat IOR to be a linear blend [^lerp] between the ambient and modulated coat IORs according to the coat weight:
The IOR of the coat medium $V_\mathrm{coat}$, controlled by the **`coat_ior`** parameter, will alter the Fresnel factor of both the coat top interface and the underlying metal or dielectric. If there is a fractional $\mathtt{coat\_weight}$ $\mathtt{C}$, then the surrounding IOR of the base dielectric or metal varies statistically across the surface depending on whether the coat is locally present (the fuzz layer can be assumed to have the ambient IOR $n_\mathrm{ambient}$). A reasonable approximation of this is to take the surrounding effective coat IOR to be a linear blend [^lerp] between the ambient and coat IORs according to the coat weight:
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpicking: do we know how far the proposed approximation is from ground truth? I'm wondering if "reasonable approximation" is a fair description, or if we might want to use a different adjective like "acceptable".

Copy link
Contributor Author

@portsmouth portsmouth Apr 26, 2024

Choose a reason for hiding this comment

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

Actually, the stated approximation is very bad indeed.. Thanks for drawing attention to this.

For example, consider if the ambient IOR is 1, the coat_ior = 2, and specular_ior = 1.5. Then coat weight = 0.5 would (according to this approximation) lead to no base specular reflection (since the "effective" coat IOR is 1.5, same as the base dielectric), i.e. $\eta_s = 1$. But obviously in reality, there is a pretty strong specular reflection (from both the coat boundary $\eta_\mathrm{ca}=2$, and specular boundary $\eta_\mathrm{sa}=1.5$).

Really the integrator should statistically mix between the coated and uncoated configurations. While still approximate, a clearly better formulation is to take:

$$ \eta_s = \mathrm{lerp}(n_\mathrm{specular}/n_\mathrm{ambient}, n_\mathrm{specular}/n_\mathrm{coat}, \mathtt{C}) $$

This produces $\eta_s = 0.5 * (2.0 + 1.5) = 1.75$, which is a lot better.

I've added that in a1c4d90:

image

Copy link
Contributor

@virtualzavie virtualzavie left a comment

Choose a reason for hiding this comment

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

The current state of the PR looks good to me.

@portsmouth
Copy link
Contributor Author

portsmouth commented Apr 27, 2024

In 0e9560a I updated the MaterialX graph to remove specular_ior_level and coat_ior_level, and implemented the various changes required to be consistent with the updates to the spec of this PR.

I verified that this compiles in the MaterialX viewer, and seems to work correctly.

@jstone-lucasfilm
Copy link
Member

This looks great, thanks @portsmouth, and my only suggestion is to double-check that @AntonPalmqvist's example materials render correctly using this new parameterization.

@jstone-lucasfilm
Copy link
Member

@portsmouth I've looked at each of the example materials, and the only unusual result I see is that open_pbr_honey.mtlx seems to have lost its transmission color, and now looks more like glass. Let's consider this an issue to address in a future change, though, and I think it's fine to move forward with the current pull request.

@jstone-lucasfilm jstone-lucasfilm changed the title Changes to description of specular_weight, specular_ior_level, coat_ior_level Update behavior of specular_weight, specular_ior_level, coat_ior_level Apr 28, 2024
@jstone-lucasfilm jstone-lucasfilm merged commit 0f3eea8 into AcademySoftwareFoundation:main Apr 28, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants