Summary
The reference MaterialX graph for open_pbr_surface does not distinguish between external and internal reflection when applying specular_color, and as a side effect it tints the two kinds of internal reflection inconsistently:
- partial internal reflection (below the critical angle) is tinted by
specular_color;
- total internal reflection (TIR) (above the critical angle) is not tinted by
specular_color.
This came up in the working-group discussion around specular_color / back-face behavior; filing it so it can be resolved in the spec and reflected in the reference graph (and noted for the ASWF/OpenUSD normative shading-model docs).
What the graph does
The dielectric base is built from two independent single-mode lobes:
| Node |
scatter_mode |
tint |
dielectric_reflection (+ _tf thin-film variant) |
"R" |
specular_color |
dielectric_transmission |
"T" |
if_transmission_tint (white; becomes transmission_color only when transmission_depth == 0) |
dielectric_base = layer(
top = dielectric_reflection_tf_mix # the R lobe, tinted by specular_color
base = mix(dielectric_transmission(+ volume), opaque_base, transmission_weight)
)
specular_color is wired straight into tint of a single dielectric_bsdf with scatter_mode="R". That node reflects on whichever side the interface is hit (the dielectric_bsdf decides entering-vs-exiting internally from the ior), and tint multiplies the whole lobe. There is no backfacing node, geompropvalue facing test, or any view-direction conditional in the graph — so the tint applies uniformly to reflections from both hemispheres.
Verified in both:
- the OpenPBR reference graph (
reference/open_pbr_surface.mtlx), and
- the shipped MaterialX library (
libraries/bxdf/open_pbr_surface.mtlx, MaterialX 1.39.5).
The inconsistency
Because TIR is modeled inside the transmission lobe (scatter_mode="T") rather than the reflection lobe:
- partial internal reflection → produced by the
R lobe → tinted by specular_color;
- TIR → produced by the
T lobe → never tinted by specular_color.
So whether an internal reflection is tinted depends on whether the ray is above or below the critical angle — which was never intended to be an observable distinction of specular_color. There is effectively no single, well-defined behavior for "internal reflection."
Spec ambiguity
The graph matches neither interpretation cleanly:
- Literal current spec —
specular_color tints the BRDF (light reflected back into the same hemisphere, from upper or lower incidence), transmission unaffected. Under this reading TIR reflections should also be tinted, but they are not (they live in the T lobe).
- "External-only" intent (raised in the working group) —
specular_color should tint only the external reflection. Under this reading partial internal reflections should not be tinted, but they are (the R lobe).
Root cause / MaterialX limitation
With the current MaterialX node set, a single dielectric_bsdf(scatter_mode="R") does not expose entering-vs-exiting, so the tint cannot be gated on facing. Cleanly implementing an external-only (or a consistent internal) behavior would require either additional MaterialX functionality or a graph restructuring.
Proposed next steps
- Decide the intended semantics: does
specular_color tint (a) external reflection only, (b) all reflections including internal (both partial and TIR), or (c) something else?
- Clarify the spec text accordingly (the current wording is being read inconsistently).
- Once decided, correct the reference MaterialX graph — and, if it needs facing/entering-exiting information that MaterialX cannot currently express, raise the corresponding MaterialX feature request.
- Note the current graph↔spec difference for the ASWF/OpenUSD normative shading-model documentation.
Summary
The reference MaterialX graph for
open_pbr_surfacedoes not distinguish between external and internal reflection when applyingspecular_color, and as a side effect it tints the two kinds of internal reflection inconsistently:specular_color;specular_color.This came up in the working-group discussion around
specular_color/ back-face behavior; filing it so it can be resolved in the spec and reflected in the reference graph (and noted for the ASWF/OpenUSD normative shading-model docs).What the graph does
The dielectric base is built from two independent single-mode lobes:
scatter_modetintdielectric_reflection(+_tfthin-film variant)"R"specular_colordielectric_transmission"T"if_transmission_tint(white; becomestransmission_coloronly whentransmission_depth == 0)specular_coloris wired straight intotintof a singledielectric_bsdfwithscatter_mode="R". That node reflects on whichever side the interface is hit (thedielectric_bsdfdecides entering-vs-exiting internally from theior), andtintmultiplies the whole lobe. There is nobackfacingnode,geompropvaluefacing test, or any view-direction conditional in the graph — so the tint applies uniformly to reflections from both hemispheres.Verified in both:
reference/open_pbr_surface.mtlx), andlibraries/bxdf/open_pbr_surface.mtlx, MaterialX 1.39.5).The inconsistency
Because TIR is modeled inside the transmission lobe (
scatter_mode="T") rather than the reflection lobe:Rlobe → tinted byspecular_color;Tlobe → never tinted byspecular_color.So whether an internal reflection is tinted depends on whether the ray is above or below the critical angle — which was never intended to be an observable distinction of
specular_color. There is effectively no single, well-defined behavior for "internal reflection."Spec ambiguity
The graph matches neither interpretation cleanly:
specular_colortints the BRDF (light reflected back into the same hemisphere, from upper or lower incidence), transmission unaffected. Under this reading TIR reflections should also be tinted, but they are not (they live in theTlobe).specular_colorshould tint only the external reflection. Under this reading partial internal reflections should not be tinted, but they are (theRlobe).Root cause / MaterialX limitation
With the current MaterialX node set, a single
dielectric_bsdf(scatter_mode="R")does not expose entering-vs-exiting, so the tint cannot be gated on facing. Cleanly implementing an external-only (or a consistent internal) behavior would require either additional MaterialX functionality or a graph restructuring.Proposed next steps
specular_colortint (a) external reflection only, (b) all reflections including internal (both partial and TIR), or (c) something else?