Skip to content

Clarify the coat absorption behaviour for partial coverage #252

@virtualzavie

Description

@virtualzavie

While working on an implementation of OpenPBR, I’ve noticed an ambiguity in how coat_weight affects coat absorption.

In summary, the question is whether coat_weight should be treated as partial coverage of a full-thickness coat (applying full coat absorption first then interpolating, i.e. “exponent → lerp”) or as a variable thickness applied uniformly (interpolating the coat_color first then applying the exponential absorption, i.e. “lerp → exponent”).

This distinction affects the resulting colour, in particular hue, especially for mid-range values of coat_weight that are neither fully off (0) nor fully on (1). The current specification does not clarify the exact behaviour (an explicit equation would be welcome), and each interpretation has valid arguments in its favor. The wording ("Coverage weight of coat slab", "presence weight of the coat") suggests “exponent → lerp”, but both Adobe and Autodesk implementations I’ve tested give results consistent with “lerp → exponent” with a distinctive hue shift.

Fraction of coverage (exponent → lerp)

This interpretation treats coat_weight as a fraction of a fully dense, full-thickness coat layer. The model applies the absorption exponent to the coat_color first, then interpolates based on coat_weight between that absorption and the absence of absorption (i.e. white).

From an artist's perspective:

  • Because the absorption is fully computed first, then simply blended, it is easier to anticipate how adjusting coat_weight will affect the final colour.
  • There's a more direct link between the input coat_color and the result: the resulting hue remains closely tied to the given coat_color when using partial weights, making it easier to obtain a target result.
  • Since both reflection intensity and absorption scale consistently with coat_weight, the relationship between them can feel more intuitive.
  • It is not the behaviour they are used to in Subtance and Arnold.

Image

Fraction of thickness (lerp → exponent)

This interpretation treats coat_weight as partial thickness of the coat layer. The model interpolates based on coat_weight between an absence of absorption (i.e. white) and the given coat_color first, then applies the exponential absorption of that colour.

Partial weights lead to a “physically expected” hue shift. As you reduce thickness, the hue changes due to less absorption. For example the resulting appearance of a coat with an orange coat_color will shift toward pink or an eggshell tint at intermediate weights. This aligns with the behaviour I have observed in Substance and Arnold.

From an artist's perspective:

  • This hue shift can be unintuitive, and achieving a desired result can be more difficult since there is no direct link between the coat_color and the obtained result.
  • This hue shift can be desirable if the intent is to represent wear or brush strokes, as it more closely physically models such cases.
  • The coat reflection intensity decreases linearly with the coat_weight, but the absorption color shift does not.
  • In fact, if we treated the coat_weight as a shorthand for thickness, we should do so for coat reflectivity as well, which means it could only be "on" or "off".

Image

Current behaviour in existing implementations

Here are some comparisons. Note how despite the differences in the models, all three show this egg-shell hue shift with partial coverage.
Also note how the tinting effect drops more rapidly than the reflectivity when coat_weight reduces.

Coating effect in Substance with Adobe Standard Material:
Image

Coating effect in Arnold with Standard Surface:
Image

Coating effect in Arnold with OpenPBR:
Image

Conclusion

The fact that both Substance and Arnold implement a similar behaviour is a strong argument in favour of keeping and formalising that behaviour. However, what matters for artists is predictability and direct control, and it might be easier for them to manually add a missing hue shift than to correct an unexpected or unwanted one. Eitherway, the specification should be explicit about the expected effect.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions