specular_weight reinterpretation (as decoupled IOR)#247
Conversation
|
An alternative take could be to split the IOR used for fresnel from the IOR used for refraction (ray bending). This is the approach we took in Unreal's path tracer and it does have a number of advantages:
One natural reservation about this idea is -- how does this work with total internal reflection? The refraction equation for computing how the ray bends falls apart at a critical angle -- if this angle doesn't match the fresnel curve, one would think you would get a weird artifact if they don't line up. There is a property of the dielectric fresnel equations that isn't immediately obvious, but allows this to work: Here In plain english, what this means is that the Fresnel curve you get when entering the material, is the same as the compressed Fresnel curve you get when exiting the material. As long as you do the "compression" using the refraction IOR, you are free to use any fresnel curve you want, you only need a curve with the right F0 value and have it tend towards 1.0 at grazing angles. Past the grazing angle when leaving the material, you have TIR and just use 1.0 (full reflection). This also gives a recipe to replace the dielectric fresnel with the Schlick approximation if you want to (as has been documented in other talks like the Disney BSDF writeup or Natty Hoffman's talk on Fresnel). Some other details that might be relevant -- we use the albedo scaling approach for energy conservation. At first I was worried that decoupling the IORs would require an extra dimension in the albedo table. However in practice, the specular IOR has a minimal effect. So sticking to a 3D table with (cos_theta, roughness, refraction IOR) is sufficient for good results. I don't recall if the OpenPBR spec spells out the behavior of nested dielectrics -- but this can all be taken into account easily if you want. Again, you have a additional degrees of freedom over the behavior here, but the physically correct behavior can be kept. |
|
This decoupling of the ray bending and the reflection seems like a more aggressive "non-physical" modification than having If we did this, then If it's considered reasonable to have this non-physical behavior of |
|
It would be good to understand in more detail the decoupled IOR proposal. This is breaking physics, so it's not easy to reason about how it should behave in full detail. I take it the IOR ratio seen by light rays is made different according to the specific scatter configuration (which is non-physical, but mathematically possible). Given the interface with an exterior ("above") and interior ("below"), there are 4 (single) scatter modes to consider:
(There are more modes if you consider microfacet multiple scattering, but can ignore that for now. Or maybe we consider these modes to be macroscopic only, I'm not sure). The proposal I assume will have both the "from above" modes see (the same) modified IOR ratio, which potentially differs from the "from below" modes which both see the unmodified original IOR ratio. (The ratio seen from above will be the one we currently calculate for the whole interface, via the specular weight). In the case where the interior IOR is higher, light incident from below can undergo TIR. So the discrepancy I take it will be that light from below that undergoes TIR, may also correspond to a refraction from above (which is physically impossible)? I'm not sure what artifact this causes. In the discussion above about "stretching" the Fresnel curve, I'm unclear how this works, since the Fresnel coefficient will be determined by the IOR ratio seen by the mode. Maybe some diagrams could help.. I generally worry that by breaking the standard physical picture, we could make it quite tricky to understand how to actually implement this in a sensible way, given the rest of the complexity of the microfacet physics being accounted for (multiple scattering, nested dielectrics, shadow masking etc.). |
|
It might be helpful to do a demo of an implementation of this, I can try to have a setup of this ready for the next OpenPBR meeting so we can noodle the parameters interactively and show how it works. I am not sure I exactly understand your questions, but I do agree that its a bit subtle and I had similar reservations as you before I worked through it. Once you see the small changes required in code to make this work, you realize that this is actually a fairly minimal change. If you have an implementation that uses the Schlick approximation in a microfacet roughness model, you've actually already done the hard work. That might be the best place to approach the implementation. You can switch back to the real fresnel equations afterwards if you prefer them. In your microfacet code, you only need to deal with the ray bending IOR for all microfacet half-vector math. The only thing you need to mess with is the implementation of the Maybe a blog post working through the details would be helpful? |
Those would be excellent, thanks @fpsunflower! It would be good to make sure we examine the most general case, so arbitrary ambient IOR (i.e. assume medium tracking). Note also that the dielectric may be under the coat, which has an arbitrary IOR as well. (In nested dielectrics, I recall there is in general no "interior" and "exterior", there are just adjacent dielectrics with different IOR, and the Fresnel (for rays incident from either side) depends on the ratio. Now we are hacking this somehow, in some sense that needs to be clear). |
|
The discussion of nested behavior is largely orthogonal to all this. The Fresnel equations can be written in a few different ways. The most literal, straight from the textbook form looks like it needs two IORs (inside and outside). In reality, only the ratio between those is important like you said. Likewise for Snell's law for the ray bending. In my description above, when I used You are right that we should spell out exactly what the interaction with the coating should be. As long as you take the proper ratio for both quantities, you can preserve the physical behavior. You also gain additional flexibility if you want it, but we don't need to reinvent the wheel. Whatever language we already have in the spec could just be extended to cover both IOR ratios. Another area that will need to be updated is the section on dispersion. I have to admit I haven't implemented this part in a decoupled setting myself, but I suspect that again we could just view the IOR tweak that dispersion implies as a tweak to both the specular IOR and the bending IOR. |
|
Would it not work to simply have the reflection from above (i.e. from the exterior) compute the Fresnel factor using the modulated IOR, with everything else unmodified? That includes unmodified reflection from below (i.e. from the interior), so TIR is unaffected. Also, completely unmodified refraction directions and transmission coefficients (that breaks energy conservation technically, but it is the price for the artistic hack). This is a rather similar, relatively harmless simple hack, to the existing specular_color multiplier. It only changes that one Fresnel factor, except in a somewhat more physically acceptable way (maintaining the white at grazing). It only changes the reflection seen from outside (as should specular_color) but presumably that is what we (I.e. the artists) care about when tweaking specular_weight. For reference we said:
We would just amend that to say that the specular_weight functions via this same modulation, by changing the IOR ratio in that one Fresnel factor. (It is then further multiplied by specular_color). Everything else is the same and just uses the unmodified IOR. That way it avoids this quite confusing notion of there being 2 IOR ratios involved. We are simply calculating the reflection Fresnel factor in a modified way. Dispersion (refractive) for example is not affected, since we define that the only modification to light transport is that one Fresnel factor. (This is so simple to do, I will implement the spec change in the next commit, for discussion). ——— EDIT: There is the case where the interior IOR < exterior IOR to consider. In that case, the Fresnel reflection from above has TIR for sufficiently grazing angles. The critical angle will change as the specular_weight changes. I don’t see this causing any particular issue though. Note also the coat may have higher IOR than the interior. In that case we mentioned that implementations may want to invert the IOR ratio (in the specular dielectric BSDF) as a trick to prevent the TIR (essentially faking the effect of the refraction in the coat). This trick would still apply. |
|
The Disney BSDF notes @fpsunflower mentioned are from the 2015 Physically Based Shading course and linked from here: https://blog.selfshadow.com/publications/s2015-shading-course/#course_content Attached is the relevant part of the course notes. While it was a long time ago, I was the one at Disney who observed and implemented this scaling of the Fresnel curves, so feel free to let me know if you have any questions about this.
|
|
Last year, @portsmouth and I also discussed a similar concept in the context of polarization. Here's a recap of that, including diagrams illustrating the scaling. Like @fpsunflower mentioned, You can try it in an online Fresnel calculator like this one: https://www.lasercalculator.com/fresnel-reflection-and-transmission-calculator/ You can also see in a chart that swapping the IORs only compresses the curves into a smaller set of angles (nonlinearly with respect to angle). You can try it here: https://webs.optics.arizona.edu/gsmith/FresnelCalculator.html And below are example curves from that last link. |
|
In practice, the key is that, as @fpsunflower described, when going from high IOR to low IOR, you don't evaluate the Fresnel equations directly. You first calculate the refraction angle using Snell's Law and your bending IOR. If the refraction angle is invalid, you have TIR. If the refraction angle is valid, you evaluate the Fresnel equations as if you're hitting the surface from the outside, using the refraction angle and the reflectivity IOR. This ensures reciprocity as any path will have the same reflectivity in either direction by construction, so you can use any function for the reflectivity. In our case, we would want to use the Fresnel equations with an IOR derived from an F0 reflectivity that has been scaled by the |
|
Another proposal is (with a sub-optimal name perhaps, but just to distinguish from "decoupled IORs"). Proposal III: Decoupled R and T
This seems like a more direct implementation of the decoupling between the reflection and refraction. The advantage of this is that all the reflection (interior and exterior) goes completely to zero as the weight goes to zero, and there is no special case logic needed for the TIR mode. The TIR modes naturally go away (pushed to extreme grazing) as the specular weight The refraction direction and energy, seen from both sides, is explicitly untouched (as noted, this is not clear in the current Proposal II). In order to avoid energy gain, this does require that Also the issues about how to calculate multiple scattering are similar to the other proposals. In this proposal though the TIR experienced by the R and T modes doesn't align though. Possibly this leads to a visual artifact, as for example as the R modes go to zero, there are areas where the T modes also go to zero due to TIR, thus the ray is killed. The Proposal II was designed to ensure that energy can in principle be preserved. (Just to note, I think most likely the Proposal II makes more sense, but perhaps it was worth mentioning this possibility for discussion). |
|
The standard BSDF for a dielectric interface is described in "Microfacet Models for Refraction through Rough Surfaces" by Walter et. al 2007. This gives the reflection micro-BRDF as: And the refraction micro-BTDF as: We ought to consider how the proposal will alter these standard formulas. I take it the Fresnel factor in these formulas is replaced as described above. Perhaps this maintains energy conservation (and that reciprocity relation) after the modification? We should check. |
|
Yup, only the Fresnel term |
OK sure, so I interpret this as meaning that:
I worry about how negligible the inconsistent multiple scattering compensation really is, in a very rough case. Having this lack of energy preservation (for glass) either explicit in the spec, or present but not quantified, seems unpleasant to me. I'd prefer an approach where we instead modify the Fresnel terms for the BRDF/BTDF of individual microfacets, using the reciprocity formula. Then I think probably if you follow through the math, this produces the same single single scattering lobe in the macroscopic BRDF/BTDF, but in principle changes the multiple scattering compensation to perfectly preserve energy. That at least would be a fully unambiguous modification, which while using the unphysical Fresnel factors still perfectly preserves energy. (*) If there is no TIR, then we can apply the reciprocity relation to obtain |
|
On second thoughts, um, maybe it is totally fine that energy is not preserved, since the user is asking for that essentially.. Also given that, perhaps it is fine to simply state that this (reciprocity-based) modulation is only applied to the single scattering BRDF term (in the macroscopic BRDFs of entering/exiting rays). The BTDFs are unchanged. The multiple scattering compensation is unchanged. That is then clear and easy to implement. If One might worry that in the rough limit, the multiple scattering will possibly dominate, so the It's still unclear to me whether it is best to have the Fresnel of both entering and exiting rays altered. Altering both seems more sensible. If the camera is under an ocean viewing the refracted sky through the waves, do we want Also if we don't alter both, then as |
|
I should add that my experience with energy conservation of decoupled Fresnel was based on Turquin's approach where you just rescale the total albedo (of BRDF+BTDF) to 1.0. In that model the impact of how I haven't attempted to implement this in the Imageworks style energy conservation approach with an extra diffuse lobe for energy compensation. Honestly, with hindsight I feel like that approach is overkill. It doesn't match the ground truth any better than Turquin's simple approach and is way more expensive and confusing to implement. Like you said, if you do real multiple scattering (ala Heitz. et al) between your microfacets, you can have a fully correct result because you are only tweaking the balance on individual facets and the random walk has a weight of 1.0 by design (all the masking stuff cancels out with enough bounces). |
|
Just to be clear Chris, in your proposal did you intend that the BTDFs are unchanged, only the BRDFs change? Also do you change both BRDFs (for entering and exiting), or only for entering? About the multiple scattering, we could either say the Fresnel alteration happens at the microfacet level, so then in principle the (full, Heitz-style) multi-scatter BRDF will go to zero as specular weight goes to zero (requiring some alteration to the multi-scatter formulas). Or, as in my last comment, we could simply say that the Fresnel alteration happens to the final macro-BRDFs in the single-scatter term only, and the multi-scatter is defined to be the same (full, Heitz-style) result as one gets without any Fresnel alteration (this then requires no alteration to the multi-scatter implementation [*]). Which would you prefer? [*] Except the Turquin approach would need to use the albedo of the unmodified BRDF, which might be a bit of a gotcha. |
|
Both BRDF and BTDF are changed. Its the same I think it makes more sense to refer to the individual microfacets for the spec. Multi-scattering would do the "right thing" and single scatter is just the single-bounce specialization of that. |
I see, though that does mean that the
That sounds like a good approach, so the entire BRDF automatically goes to zero as
What about this? Would you have the Or have only the external reflections modified (if so, why?). |
I would change it for both. Its simpler and in glass some of what reads as "specular highlights" are actually multiply reflected highlights. For example if your goal was to get rid of specular ( |
|
I implemented the decoupling scheme in Arnold, with the following results (on a smooth glass shaderball with (Just to note, the left column is not actually how the currently shipped Arnold OpenPBR functions, since we simply had the transmission unaffected by
I committed a draft of the required language in the spec. It's a bit wordy, but I tried to be as clear as possible leaving nothing ambiguous (so e.g. it describes the logic for both entering and exiting cases, since TIR can occur for either due to nested dielectrics or the coat). @fpsunflower and @peterkutz it would be very helpful if you can proofread this, and suggest any improvements.
-------------------------------------- Some initial commentary:
|
I think I better understand the energy balance now. The formulas quoted above are misleading since the delta function is written there in "half-direction" measure, not solid angle measure. The solid angle measure versions are as quoted in PBRT (this is for a smooth interface, and/or at the microfacet level):
Integrating these over the hemispheres gives the reflection and transmission albedos: These do not sum to 1, since the radiance of the transmitted rays is boosted by the factor In order to satisfy Renderers would/should take this into account. (Our discussion of energy conservation in the spec should be altered to state this more correctly). In any case, the modification we do via The current Arnold approach in contrast is more incoherent, since altering the BRDF without altering the BTDF does not correspond to merely changing the Fresnel factor, and leads presumably to energy conservation/reciprocity violation. |
|
Nice analysis and investigation @portsmouth ! Based on all of the considerations, "Proposal II: Decoupled IORs" does seem like the best approach. It makes sense intuitively that this approach conserves energy. At each interaction either reflection or transmission occurs with complementary probabilities, so the path is always continued without modifying the throughput. And the scaling of the radiance is due to the density of light energy changing, not the actual amount of energy changing, and it only depends on the degree of refractive squeezing, not the proportion of light transmitted. It also makes sense that this approach would reduce darkening artifacts compared to the other approaches. Even for other approaches that do not modify TIR, they still modify non-total internal reflection, which can be arbitrarily close to 100% so which can still be quite visible. Avoiding almost-black artifacts seems ideal. I still need to more carefully consider some of the details you've written about, and I still need to review your updated wording in the spec itself, but at a high level everything sounds good. I haven't fully caught your proposal for the effect of the exterior IOR on this system yet, but I would assume that the specular weight is applied directly to the relative IOR of the exterior IOR and the original base specular IOR. In the Eclair implementation, we have a reflection/transmission-coefficient abstraction that is treated like a black box by the microfacet lobes, so it seems like it should be possible to modify the reflection/transmission coefficients returned by that abstraction without affecting anything else. Of course the microfacet multiple scattering compensation might need to be adjusted too, but I'll be curious to first see how this looks if leaving that unchanged. |
|
Maybe it's worth giving some very simplified C "pseudo" code (compiling, but out of context) for the dielectric Fresnel modification. Something like, given: // Standard dielectric Fresnel function, depending on:
// - mu_i = magnitude of angle cosine of incident ray (mu_i >= 0)
// - eta_ti = (IOR in hemisphere of transmitted light photons) / (IOR in hemisphere of incident light photons)
float DielectricFresnel(float mu_i, float eta_ti);The function to compute the modulated Fresnel due to // specular_weight (>= 0) modulates the Fresnel F0 linearly, without disturbing TIR/refraction
float DielectricFresnel(float mu_i, float eta_ti, float specular_weight=1.f)
{
if (specular_weight == 1.f)
return DielectricFresnel(mu_i, eta_ti);
// Compute modified IOR ratio
float F0 = sqr((eta_ti - 1.f)/(eta_ti + 1.f));
float eps = copysignf(min(1.f, sqrtf(clamp(specular_weight * F0, 0, 1)), eta_ti - 1.f);
float eta_ti_prime = (1.f + eps) / max(FLT_EPSILON, 1.f - eps);
if (eta_ti_prime >= 1.f) // (No TIR possible)
return DielectricFresnel(mu_i, eta_ti_prime);
// In the possible-TIR case, check for TIR and if not, use the "un-squeezed" Fresnel curve.
float mu2_t = 1.f - (1.f - sqr(mu_i))/sqr(eta_ti);
if (mu2_t <= 0.f)
return 1.f; // (TIR occurs)
return DielectricFresnel(sqrtf(mu2_t), 1.f/eta_ti_prime);
}That's easier to understand than the formulas perhaps. (Maybe there is a simpler/nicer way of implementing it than this, but this is the most straightforward way it seems). It would be easy to add a folder with some such example code snippets in it for reference from the spec. |
I expressed the formulas in terms of a generic exterior IOR The dielectric exterior IOR To deal with this, the spec suggests to use an effective where That's not really correct (instead one should ideally e.g. stochastically decide whether coat is present or not, not use a blend of IORs), but it seems a reasonable practical approach. |
|
For the MaterialX implementation of this, it doesn't seem possible without modifying the MaterialX spec, since the Fresnel function has to change. We can propose something for that. But in the interim, a possible approximation we can easily do in the MaterialX graph is to just ignore the IOR modulation in the dielectric BTDF. (Which is what the current Arnold implementation does, as noted). This will violate the proposed decoupling behavior in this PR, but is not very different in appearance (as shown above). I'd suggest we go with that in order to get this merged, and revisit the MaterialX side when possible. |
|
The simplified MaterialX form of the IOR decoupling is nothing but: 9853e4d |
peterkutz
left a comment
There was a problem hiding this comment.
I reviewed the spec changes in detail and everything looks good to me. The concepts are explained accurately and I didn't notice any errors in the mathematical symbols and formulas.
|
Someone noted that the current OpenPBR cannot produce the image on the left below, which is a rough coat on a smooth glass base in Standard Surface (right is OpenPBR). Of course this is expected in OpenPBR because if the coat is rough this (physically) should cause the refraction through it to also be correspondingly rough. In Standard Surface this physical behavior was optional, and off by default. But one motivation for doing this specular decoupling is that it allows for something similar to the non-physical look of the left image to be achieved in OpenPBR, without the coat, by making the glass rough but with a low IOR and high specular weight, so the refraction is fairly sharp (due to low refraction IOR) but the rough reflection is visible due to boosted effective reflection IOR. Of course it won't quite look the same since the refraction IOR will be low. (We should check). Another (physically correct) option will be to use the hazy specular functionality to blend between rough and smooth glass, though the refraction will also appear hazy. A partially present rough coat should also produce some amount of completely sharp refraction. (There doesn't seem to be any way to get completely sharp high-IOR refraction through rough glass without completely faking the light transport though. I hope that the perceived need for this look is a case of actually wanting something that is doable physically, like the haze look.). |
|
I think this is already implied in the proposed text, but I wonder if it would be worth explicitly clarifying that this technique is not just exploiting a special properties of the Fresnel equations but would actually work with any reflectance function. The reflectance is the same in both directions along a refracted path, so why do we evaluate it in the "entering" direction? The reason is that that lets us use any reflectance function defined over the entire hemisphere without any consideration of the angle of refraction. This could be the Fresnel equations with a different IOR, but it could also be any other function with a hemispherical domain and a [0, 1] range. The function doesn't have to have any other special properties. The light transport is reciprocal and energy conserving by construction since the reflectance always matches in both directions and the transmittance is always the complement of the reflectance. Total internal reflection and radiance scaling are characteristics of refraction itself that occur regardless of the shape of the reflectance function. |
I think the reflectance in the TIR hemisphere at least has to have the property that it smoothly transitions to the true Fresnel as |
|
Just to note, the pseudo-code I posted above was actually wrong.. The last line was return DielectricFresnel(sqrtf(mu2_t), 1.f/eta_ti);but should have been return DielectricFresnel(sqrtf(mu2_t), 1.f/eta_ti_prime);(I changed the earlier post, just in case someone copies the code). It didn't seem to change the result much (in the Arnold case), though it should have in principle as the internal reflections were not being suppressed only the external ones. A point worth noting is that low
The highlight is killed, but the overall brightness goes up a bit. It could be though that this is also an artifact of the setup in Arnold in which we currently have some double counting going on by default in glass, as the It would be good to have another team implement this in their renderer to verify it behaves acceptably well. |
AdrienHerubel
left a comment
There was a problem hiding this comment.
This change was well received and was formally approved, so let's merge it in the 1.2 branch.
ac355b7
into
AcademySoftwareFoundation:dev_1.2


























It was noted on Slack by Masuo that it seems a bit strange that the refraction changes when
specular_weightis varied. This happens because in the current spec, we say that thespecular_weightcontrols the IOR of the dielectric.For a glass object, this may be unintuitive:
specular_weight 1specular_weight 0.1The issue is that the name implies "specular presence", so in this case the artist probably expects the control to merely kill the reflection from the glass, not change the refraction as well. We do already have a control that does that, namely
specular_color, but it is unintuitive to use that control for dialing the dielectric reflectivity.So the proposal here is to have the
specular_weightsimply function as a multiplier to that existing color (so it becomes simply an overall weight factor for the whole lobe). This matches how thespecular_weightfunctioned in Standard Surface.The change to the spec amounts to a wording change only:
Note that this change requires: