We discussed this a bit in the meeting. The core questions:
- How should we think about the mirroring assumption? Should it be taken literally as a symmetric "sandwich", with duplicated coat and fuzz, that would in theory be visible from the other side?
- The spec also offers a "convenient rough approximation" of that: assume the coat and fuzz are only on the viewed side, and flip accordingly. That avoids having to deal with double coat and fuzz, at the price of the material not being strictly physically consistent (the coat and fuzz of a physical material sit on one fixed side, not flipping according to view).
Worth noting up front: these two descriptions already coexist in the spec and don't quite agree. The main thin-walled section says the surface is "mirrored around the base … [and] appears identical viewed from either side", with the slabs "double-sided, with the top BSDF mirrored to the bottom". But the coat/fuzz derivation already says "the simplest approximation is to assume the coat and fuzz are ignored on the underside, and the surface always flipped so that incident rays enter top-down." So part of the clarification is simply to reconcile these into a single, consistent statement.
I think there are two issues at root here:
- We want a model that's useful for a range of effects, but also practically implementable. It would be nice to just write down the most general setup, but if no-one knows how to implement it, that's not workable.
- There are genuinely different use cases, and one structure probably doesn't cover them all. A thin-walled transmissive leaf may need to be coated on both sides, or only one. Even uncoated, the leaf albedo and roughness may need to be decoupled per side, or not, depending on what's being modeled. It's not ideal to guess and support only one of these.
So my thoughts based on that framing:
A fully general model is not OpenPBR's domain
A fully general model would be the most flexible: a thin sheet of volumetric material inside a dielectric host medium, bounded on each side by an arbitrary (not necessarily mirrored) stack of further such layers, with multiple thin-films too.
The current spec implies this mirrored, layered model is what one should actually compute. But that isn't really practically doable, because there isn't at present a viable theory for stacking such layers in a production shader. (There is a complete theory — it's just light transport — but not, I'd claim, a practical one.) Belcour's "atomic layering" seems the most promising direction, but AFAIK it isn't implemented in any production layering framework, and may still be missing things we'd need, or not be accurate enough.
It would be great if such a practical model eventually lands (in MaterialX, Substance, etc.) and lets us work towards supporting the fully general configuration once it's practical. But we can't rely on that here.
A related point I've raised elsewhere: a more flexible model wouldn't need the arbitrary distinction between "thin-walled transmission" (absorbing only, no scattering) and "thin-walled subsurface" (strongly scattering, no extinction control) — these would just be different regimes of one thin volumetric sheet. Some artists have already bumped up against this split and found it limiting. Clearly out of scope for now, but worth recording.
The thin-subsurface case, and the EGSR 2026 aging-leaf BSDF
A useful state-of-the-art data point, raised by Weizhen: Kruk, Noizet & Lucas, "A Biophysical Approach to Foliage Rendering: Time-Dependent BSDF for Aging Leaves" (EGSR 2026; co-authored by Mathieu Noizet, present in the meeting; code at https://github.com/laurent-lu/Time-Dependent-BSDF-for-Aging-Leaves-Rendering, implemented in Mitsuba 3). Their leaf BSDF is
$$f(\omega_i,\omega_o,\lambda) = f_s(\omega_i,\omega_o,\lambda) + \big(1 - E_s(\omega_i)\big) f_d(\lambda), \qquad f_d(\lambda) = \frac{\rho_d(\lambda)}{\pi} + \frac{\tau(\lambda)}{\pi},$$
i.e. a GGX specular interface $f_s$ (fixed roughness $0.3$) over a diffuse substrate that sums a Lambertian reflectance $\rho_d$ and a Lambertian transmittance $\tau$ (from the biophysical PROSPECT-D model), with the specular albedo-scaled onto the substrate through the $(1 - E_s)$ factor.
To be clear about what it is and isn't:
- It is essentially just our thin-subsurface mode — a dense scattering sheet (diffuse reflection + diffuse transmission), modelled fairly heuristically (though validated against reference data). It does not cover the clear thin-transmission case (e.g. a plain window pane), and it is not a general layered model — so it is not evidence about the "fully general model" above, nor about unifying the transmission/subsurface split.
- What it does make explicit, which we don't currently write down, is the albedo scaling of the specular reflection over the diffuse substrate. We should probably document this as a reasonable practical approximation for thin-subsurface (clearly labelled as an approximation).
- It ignores various physical effects, notably the darkening due to internal bounces within the sheet.
Open question — internal-bounce darkening. We do account for exactly this kind of darkening in the coat (via coat_darkening, from bounces between the coat and the base), so how do we justify ignoring it in the thin sheet? We needn't resolve the physics, but we should be explicit — say either that implementations may ignore the thin-sheet internal-bounce darkening, or that they must. Right now the spec says neither.
v1.3 — separately coloured transmission lobe. The leaf model needs independently coloured diffuse reflectance and transmittance ($\rho_d \neq \tau$ — a leaf reflects and transmits different colours). OpenPBR's current thin-subsurface ties both to one colour: writing $S$ for subsurface_color and $g$ for subsurface_scatter_anisotropy,
$$f^R_\mathrm{diffuse} = \tfrac{1}{2}S(1-g) f_{+}, \qquad f^T_\mathrm{diffuse} = \tfrac{1}{2}S(1+g) f_{-},$$
so only the energy split between reflection and transmission is controllable (via $g$) — their colour is shared. Decoupling these (e.g. an added diffuse-transmission colour) is worth doing for v1.3, within the existing thin-subsurface mode.
Proposal
What should we ask implementors to do in their thin-wall implementation, given the current SOTA and bearing in mind the possible artist requirements?
v1.1 / v1.2: specify view-direction flipping (symmetric appearance)
The key realization is that the mirrored sandwich and the view-flip produce the same (symmetric) appearance — the flip is just the cheap realization that avoids double-counting coat and fuzz. So the target has always been a symmetric look; "mirrored" is an impractical, double-counting rationalization of it, and the flip is the practical one. The two only diverge in that the literal mirror would apply coat/fuzz absorption twice in transmission, which is exactly what nobody wants.
Concretely, the "flip" is a statement about the layer structure, not the compositing math: we orient the whole stack toward the viewer — evaluating as if the surface is always hit from the coat/fuzz side, i.e. with the oriented normal $\mathbf{n}^{+} = \mathrm{sign}(\omega_o \cdot \mathbf{n})\mathbf{n}$ — so the coat and fuzz sit on the view side and a transmitted ray crosses them once rather than twice. It also makes explicit that the specular/coat interface is always present on the viewed side and cannot be dropped in thin-wall mode. (How the lobes are then combined is left to the implementation as usual: OpenPBR stays physical where it can — e.g. the coat genuinely darkens the base via internal bounces, coat_darkening — so simple albedo scaling is only one suggested approximation, not a requirement.)
So for v1.1 / v1.2 I propose we:
- stop presenting the "mirrored sandwich" (with duplicated coat/fuzz) as the thing to compute, and remove/replace that diagram;
- show the coat/fuzz on one side only, tied to the view direction, and state explicitly that the implementation flips the structure so incident rays always enter from the coat/fuzz side;
- specify that a ray transmitted through the sheet incurs exactly one coat/fuzz interaction, on the exit side.
Importantly, this is a clarification, not a look change: since no renderer actually implements the literal double coat/fuzz mirror, no conforming output changes. That's the justification for backporting it to 1.1 (and it should address the "look changes only in version revisions" concern).
Two caveats to state honestly:
- It's non-physical — the structure follows the view direction rather than the fixed geometry. But we've had to bend physics elsewhere for practicality.
- It complicates bidirectional transport, as noted by Weizhen: light-side paths would need to see the coat and fuzz on the transmitted side. Since bidirectional transport is fairly niche, I think we can get away with it, but we should note it.
One thing to decide explicitly: this makes the symmetric look the required default. That matches Arnold (which flips so the incident ray always hits the fuzz from the fuzz side). But it does not match every implementation — e.g. renderers that "set coat and fuzz to 0 on the back face" (Chris mentioned this) produce a one-sided look, which is actually the v1.3 sidedness behavior below. So we should acknowledge that standardizing on symmetric-flip makes those one-sided implementations non-conforming for the default, and confirm that symmetric is the default we want. I think it is — it's the natural choice for the common thin-glass / bubble / window case; the leaf-style one-sided case is handled next.
v1.3+: optional sidedness (single/double-sided)
A natural extension: allow the thin-wall to look different on each side by tying the coat/fuzz hemisphere to the normal instead of the view direction — the classic single-/double-sided distinction. From the back side (opposite to N), the implementation simply ignores the coat and fuzz in reflection, while still accounting for one coat/fuzz interaction on the exit side for transmission.
This is physically consistent up to the usual single-interaction / albedo-scaling approximation (we still ignore multiple bounces between the coat/fuzz and the sheet).
It needs a single switch, e.g. geometry_thin_walled_double_sided, defaulting to true (preserving the v1.1/v1.2 symmetric behavior). When false, the coat/fuzz live on the +N side only.
Notably, the aging-leaf BSDF above is operationally symmetric (a single $\rho_d/\tau$ pair, specular on both epidermes), which supports making symmetric the default — yet real leaves are physically sided (glossy/dark adaxial vs pale abaxial; the paper's own cross-section shows distinct upper and lower epidermis, and it cites PROSPECT extensions with per-layer optical properties). So this same paper both validates the symmetric default and names the case this sidedness extension is for.
v1.3+: fully decoupled look per side
More generally, an artist may want the two sides to differ substantially while still having thin-walled transmission — e.g. cheaply rendered thin-walled translucent leaves whose color and roughness differ on the underside. In principle this requires the fully general arbitrary layer stacking.
But I'd argue this is not really OpenPBR's job: assigning a different shader per side is exactly what the host's two-sided material binding already does (ubiquitous in DCCs, and — per Jonathan — the domain of USD/MaterialX rather than open_pbr_surface, which describes a single shading point). So the practical answer is: bind two OpenPBR shaders via the standard two-sided mechanism, and for a given view direction evaluate only the shader on the side being viewed. The sidedness switch above is then simply the single-shader special case of this — so we may not need a dedicated parameter at all.
The honest caveat here is transmission: with two decoupled shaders there is no well-defined interaction between the two sides' media, so the transmitted tint front→back generally won't equal back→front (it's non-reciprocal). For opaque or lightly-transmissive leaves that's acceptable, and it's what implementations effectively do today anyway — we'd just state that we deliberately don't attempt to reconcile the two transmissive media.
Scope note
This issue concerns thin-walled mode only. The related "Entering versus exiting" question — the different light transport for rays entering vs exiting a thick, non-thin-walled dielectric — is a separate topic (the thick-glass discussion from the same meeting) and should get its own issue; I'll cross-reference it.
We discussed this a bit in the meeting. The core questions:
Worth noting up front: these two descriptions already coexist in the spec and don't quite agree. The main thin-walled section says the surface is "mirrored around the base … [and] appears identical viewed from either side", with the slabs "double-sided, with the top BSDF mirrored to the bottom". But the coat/fuzz derivation already says "the simplest approximation is to assume the coat and fuzz are ignored on the underside, and the surface always flipped so that incident rays enter top-down." So part of the clarification is simply to reconcile these into a single, consistent statement.
I think there are two issues at root here:
So my thoughts based on that framing:
A fully general model is not OpenPBR's domain
A fully general model would be the most flexible: a thin sheet of volumetric material inside a dielectric host medium, bounded on each side by an arbitrary (not necessarily mirrored) stack of further such layers, with multiple thin-films too.
The current spec implies this mirrored, layered model is what one should actually compute. But that isn't really practically doable, because there isn't at present a viable theory for stacking such layers in a production shader. (There is a complete theory — it's just light transport — but not, I'd claim, a practical one.) Belcour's "atomic layering" seems the most promising direction, but AFAIK it isn't implemented in any production layering framework, and may still be missing things we'd need, or not be accurate enough.
It would be great if such a practical model eventually lands (in MaterialX, Substance, etc.) and lets us work towards supporting the fully general configuration once it's practical. But we can't rely on that here.
A related point I've raised elsewhere: a more flexible model wouldn't need the arbitrary distinction between "thin-walled transmission" (absorbing only, no scattering) and "thin-walled subsurface" (strongly scattering, no extinction control) — these would just be different regimes of one thin volumetric sheet. Some artists have already bumped up against this split and found it limiting. Clearly out of scope for now, but worth recording.
The thin-subsurface case, and the EGSR 2026 aging-leaf BSDF
A useful state-of-the-art data point, raised by Weizhen: Kruk, Noizet & Lucas, "A Biophysical Approach to Foliage Rendering: Time-Dependent BSDF for Aging Leaves" (EGSR 2026; co-authored by Mathieu Noizet, present in the meeting; code at https://github.com/laurent-lu/Time-Dependent-BSDF-for-Aging-Leaves-Rendering, implemented in Mitsuba 3). Their leaf BSDF is
i.e. a GGX specular interface$f_s$ (fixed roughness $0.3$ ) over a diffuse substrate that sums a Lambertian reflectance $\rho_d$ and a Lambertian transmittance $\tau$ (from the biophysical PROSPECT-D model), with the specular albedo-scaled onto the substrate through the $(1 - E_s)$ factor.
To be clear about what it is and isn't:
Open question — internal-bounce darkening. We do account for exactly this kind of darkening in the coat (via
coat_darkening, from bounces between the coat and the base), so how do we justify ignoring it in the thin sheet? We needn't resolve the physics, but we should be explicit — say either that implementations may ignore the thin-sheet internal-bounce darkening, or that they must. Right now the spec says neither.v1.3 — separately coloured transmission lobe. The leaf model needs independently coloured diffuse reflectance and transmittance ($\rho_d \neq \tau$ — a leaf reflects and transmits different colours). OpenPBR's current thin-subsurface ties both to one colour: writing $S$ for $g$ for
subsurface_colorandsubsurface_scatter_anisotropy,so only the energy split between reflection and transmission is controllable (via$g$ ) — their colour is shared. Decoupling these (e.g. an added diffuse-transmission colour) is worth doing for v1.3, within the existing thin-subsurface mode.
Proposal
What should we ask implementors to do in their thin-wall implementation, given the current SOTA and bearing in mind the possible artist requirements?
v1.1 / v1.2: specify view-direction flipping (symmetric appearance)
The key realization is that the mirrored sandwich and the view-flip produce the same (symmetric) appearance — the flip is just the cheap realization that avoids double-counting coat and fuzz. So the target has always been a symmetric look; "mirrored" is an impractical, double-counting rationalization of it, and the flip is the practical one. The two only diverge in that the literal mirror would apply coat/fuzz absorption twice in transmission, which is exactly what nobody wants.
Concretely, the "flip" is a statement about the layer structure, not the compositing math: we orient the whole stack toward the viewer — evaluating as if the surface is always hit from the coat/fuzz side, i.e. with the oriented normal$\mathbf{n}^{+} = \mathrm{sign}(\omega_o \cdot \mathbf{n})\mathbf{n}$ — so the coat and fuzz sit on the view side and a transmitted ray crosses them once rather than twice. It also makes explicit that the specular/coat interface is always present on the viewed side and cannot be dropped in thin-wall mode. (How the lobes are then combined is left to the implementation as usual: OpenPBR stays physical where it can — e.g. the coat genuinely darkens the base via internal bounces,
coat_darkening— so simple albedo scaling is only one suggested approximation, not a requirement.)So for v1.1 / v1.2 I propose we:
Importantly, this is a clarification, not a look change: since no renderer actually implements the literal double coat/fuzz mirror, no conforming output changes. That's the justification for backporting it to 1.1 (and it should address the "look changes only in version revisions" concern).
Two caveats to state honestly:
One thing to decide explicitly: this makes the symmetric look the required default. That matches Arnold (which flips so the incident ray always hits the fuzz from the fuzz side). But it does not match every implementation — e.g. renderers that "set coat and fuzz to 0 on the back face" (Chris mentioned this) produce a one-sided look, which is actually the v1.3 sidedness behavior below. So we should acknowledge that standardizing on symmetric-flip makes those one-sided implementations non-conforming for the default, and confirm that symmetric is the default we want. I think it is — it's the natural choice for the common thin-glass / bubble / window case; the leaf-style one-sided case is handled next.
v1.3+: optional sidedness (single/double-sided)
A natural extension: allow the thin-wall to look different on each side by tying the coat/fuzz hemisphere to the normal instead of the view direction — the classic single-/double-sided distinction. From the back side (opposite to
N), the implementation simply ignores the coat and fuzz in reflection, while still accounting for one coat/fuzz interaction on the exit side for transmission.This is physically consistent up to the usual single-interaction / albedo-scaling approximation (we still ignore multiple bounces between the coat/fuzz and the sheet).
It needs a single switch, e.g.
geometry_thin_walled_double_sided, defaulting totrue(preserving the v1.1/v1.2 symmetric behavior). Whenfalse, the coat/fuzz live on the+Nside only.Notably, the aging-leaf BSDF above is operationally symmetric (a single$\rho_d/\tau$ pair, specular on both epidermes), which supports making symmetric the default — yet real leaves are physically sided (glossy/dark adaxial vs pale abaxial; the paper's own cross-section shows distinct upper and lower epidermis, and it cites PROSPECT extensions with per-layer optical properties). So this same paper both validates the symmetric default and names the case this sidedness extension is for.
v1.3+: fully decoupled look per side
More generally, an artist may want the two sides to differ substantially while still having thin-walled transmission — e.g. cheaply rendered thin-walled translucent leaves whose color and roughness differ on the underside. In principle this requires the fully general arbitrary layer stacking.
But I'd argue this is not really OpenPBR's job: assigning a different shader per side is exactly what the host's two-sided material binding already does (ubiquitous in DCCs, and — per Jonathan — the domain of USD/MaterialX rather than
open_pbr_surface, which describes a single shading point). So the practical answer is: bind two OpenPBR shaders via the standard two-sided mechanism, and for a given view direction evaluate only the shader on the side being viewed. The sidedness switch above is then simply the single-shader special case of this — so we may not need a dedicated parameter at all.The honest caveat here is transmission: with two decoupled shaders there is no well-defined interaction between the two sides' media, so the transmitted tint front→back generally won't equal back→front (it's non-reciprocal). For opaque or lightly-transmissive leaves that's acceptable, and it's what implementations effectively do today anyway — we'd just state that we deliberately don't attempt to reconcile the two transmissive media.
Scope note
This issue concerns thin-walled mode only. The related "Entering versus exiting" question — the different light transport for rays entering vs exiting a thick, non-thin-walled dielectric — is a separate topic (the thick-glass discussion from the same meeting) and should get its own issue; I'll cross-reference it.