He’s right there, totally correct Fresnel is currently impossible in Cycles. Not just the Fresnel node itself - no matter what shader you build or OSL script you write, it will always be an approximation. That can and most likely will be fixed in the actual Cycles code, but it’s not as easy as it sounds.
I know you said that you could write pages about the reasons why that is, could you do it in a few paragraphs? Just curious, but don’t want to take up too much of your time!
El_diablo; I do agree that such a thing is not only a weakness of FOSS, but also something that the Blender devs. do not have a lot of power over.
If you look at the facts, it’s pretty much a given that the vast majority of an open source community will contribute far less in terms of financial investment than people in a community that is expected to pay for a license (since for most people anyway, the idea of Open Source software is allowing them to create something with no money).
The Blender Foundation is fortunate to at least have a few hundred thousand Dollars/Euros to spend on an annual basis, compare that to the larger software corporations which has up to hundreds of times more to spend on developer acquisition, marketing, and R&D. Autodesk for instance has so much money they can just buy out the boutique software houses with innovative 3D concepts and transfer the code to Max or Maya. In the end it boils down to a game of resources, so an open source organization has to be able to foster a thriving volunteer development community if they want any chance of their product being comparable with commercial solutions (or otherwise they will almost always lose).
Am I right to assume that this could be a side effect of the focus on shading flexibility for users?
I ask because I know that the output from the fresnal node (whether used in a raw form or strung through ramps and curves), can be used for pretty much anything while a lot of engines with physically correct fresnal do not allow quite as much user control.
Open Source, FFS… join the cloud. Everyone is also free to donate, to do their best as human can… yup, am dreaming again.
Damn reality, there’s some dumb shit floating in some heads… where used to be a brain.
There are module owners for the different areas of Blender exactly because of that.
There is one huge difference between commercial/non-open source projects and Blender. Blender is more open about the roadmap, even though it only has a few full time developers. If they are off, you can immediately see it. If a commercial project is off, you usually don’t see it, because they usually don’t mention their research projects. Making this kind of comparison is not really fair.
If you want to support Blender, there is the development fund and if you want them to develop new things within a project, you can subscribe to the Blender Cloud. There is your wallet.
Even if you pay for software licenses, you don’t have a huge say in the development. You may be able to vote for some features here and there, but there is still no guarantee that it will be implemented.
Okay, I’ll try:
The fresnel equations basically describe the effect that when light hits a surface, a part if it is reflected and a part is transmitted (Funfact: Technically, the light is also transmitted into metals, just like with glass. However, they are so absorptive that basically no light is left after a few atom diameters). The fresnel equations describe which percentage is reflected (the rest is transmitted).
There are four different fresnel equations, since they depend on the material (dielectric or conductive) and the polarization of the light (Funfact 2: That’s why polarization filters for photography reduce reflections - One polarization is reflected stronger than the other because of the different fresnel, and you can filter that out). Since renderers generally ignore polarization, the average of the two orientations is used, which leaves us with two equations.
Now, whenever you see IOR, you can assume that the dielectric one is used, for a simple reason: Metallic objects actually have two indices of refraction (technically, their IOR is a complex number, which is expressed as two regular numbers), and they also depend on the color (wavelength) of the light. This data for metals is usually called NK-data or something like that. Cycles doesn’t support that at all yet, but there’s no complex reason for that, it’s not harder than the dielectric one (you just have six values, N and K for red, green and blue, instead of one).
Now, a bit of background on the glossy (and glass) shader: They’re based on microfacet theory, which allows to model how not-perfectly-smooth surfaces reflect light. That works by assuming that the object is actually a perfect mirror, but is not smooth, but made up of tiny facets (therefore “microfacet”). On each microfacet, the light is reflected like on a perfect mirror. The facets are not calculated individually, but modeled with a statistical approach - some function essentially says how likely each facet orientation is. These function are the Beckmann, GGX or Aniso models that you can select. Generally, the higher you set the roughness, the more likely facets with a large tilt become.
Now, for a renderer to work, a shader must be able to fulfill two tasks: First, for a given incoming and outgoing direction of light, it must be able to calculate the fraction of light that is reflected between the two (for example, this is always the same for a diffuse shader). That is used by light sampling code. Second, for a given incoming direction, it must be able to randomly pick an outgoing direction (technical detail: for low noise, the probability of picking some direction should be proportional to the fraction reflected) and tell the reflected fraction.
Cycles (just as OSL, because that’s what Cycles’ design is based on) implements that as follows: First, for some incoming ray, the shader is executed (being an OSL script and/or a node graph). As a result, that produces a list of closures, which are individual small programs that are specified in the Cycles code, with a weight for every closure. Then, when one of the two functions described above is needed, the closure programs are used to execute the task.
For example, say you have a wood-textured desk with some glossy effect on top, mixing the textured diffuse and the glossy with a fresnel node. The first step will lookup the texture (resulting in some yellowish color), evaluate the fresnel, and then return two closures: A Diffuse BSDF with some yellowish color, and a Glossy BSDF. The individual weights depend on the fresnel result.
However, let’s consider what happens on a physical level: Your wood is covered with some clear coating. When light hits the coating, some of it is reflected (producing the highlights), while some of it is transmitted and reaches the wood (producing the yellowish diffuse component). Therefore, it is correct to combine the two as (Glossy Reflection)Fresnel + (Diffuse Reflection)(1-Fresnel), since the contribution of the glossy shader is just the light that’s reflected, not reaching the wood, which in turn is exactly modeled by the fresnel equation (as described above).
So, where’s the problem? Here it comes: If your coating is not perfectly smooth, the glossy shader will assume it’s made out of microfacets. Therefore, the fresnel equation should be applied to the facet that the light reflects off, which will give a different result than the smooth surface since the facet normal is different.
However, to find the microfacet normal, we need to know the incoming and outgoing direction (then, the microfacet normal is what’s known as the half-angle vector). However, remember when I said that the shader is executed once, producing a list of closures that is potentially used for many outgoing angles? That’s the problem: When computing the fresnel node, we can’t know the outgoing direction and therefore the microfacet normal yet. Also, this means that anything that happens in the shader itself can’t do this correctly, no amount of curvemap nodes and fancy processing can give the correct result.
So, how do other engines do it correctly? Well, they apply Fresnel inside the closure, since there the outgoing direction is known at the time where it is relevant. In fact, that’s why most papers show the microfacet model as “DFG/(4piw_i)”, while Cycles implements it as “DG/(4pi*w_i)” (notice the missing F aka fresnel)?
So, why doesn’t Cycles do that? Well, in the table example, that wouldn’t be enough - not only the glossy node depends on the fresnel, the diffuse one does as well - the more light is reflected, the less light is transmitted down to the wood. At that point, you’d need the diffuse closure to have a separate roughness input so that it can calculate its weight itself - but what for even more complex networks?
The result is: You can’t have Cycles’ flexibility of building any node graph that you want and still have correct fresnel.
But what if you don’t need that flexibility, you just want your wooden table? Well, the magic word there is Ubershader: “Simply” put both diffuse and glossy together with some other stuff into a single closure. In fact, that allows you to do some other magic, such as implementing correct multiple internal bounces etc., as well, but that’s really another topic.
So, when does Cycles get an Ubershader? Well, maybe soon(ish), once I got the code fully working
wow, totally interesting read Lukas, not only that, you surely know how to create a great cliffhanger!, thanks
Super interesting ! thx Lukas
there are some nodes set up for the N K model
and working on some materials but not all I guess
is cycles going to ever get something like this N K model for Fresnel or not even possible !
hope to see this ubershader soon
some one made a nodes set up for ubershader
any idea if this can work better with Fresnel ?
thanks
happy cl
Ah, fascinating read Lukas ! So, if I understand correctly using individual bsdfs like we do now is bound to never have a correct fresnel ?
Thanks for the background info, totally interesting and it helps explain some of the issues I have seen with shading, especially at glancing edges.
Much appreciated!
@anaho: I am still surprised about this Embree myth. We do use Embree code and algorithms for a fact, already for years. (BVH building, traversal and intersection). The only “strange policy” we have is that we actually want a good and future proof code base, rather than plugging in more external dependencies and having duplicate data in RAM etc.
Lukas putting down knowledge again - always great to read!
Thank you for sharing!
Lukas; So I see that the only real way is another multi-closure node like what we already have with the glass shader?
Come to think of it, do you think it would ever be possible for Cycles to automatically generate a multi-closure shader by way of folding together parts of all of the shader node part of a node tree, because I think such a thing could create a notable benefit in rendering performance along with the convergence rate (and maybe open the door for possibilities such as the correct fresnal equation)?
If it is possible, than that would be the icing on the cake when it comes to the recent improvements on the constant folding optimizations.
Quoted for agreement
@lukasstockner97, so the solution is an ubershader, out of curiosity does it follow the Disney one?
How this recent paper* connects to the Fresnel problem? An issue we encounter is also the energy loss when doing frosted glass. Has this been taken into account from Cycles devs? Does the Ubershader somewhat solve the energy loss, or this multiple scattering approach needs to be implemented for that?
I remember Ton also tweeted about it back in November.
Glad to know someone acually read what I posted ^^
@RickyBlender: Metallic (instead of dielectric) Fresnel is totally possible, in fact, the OSL templates even contain such a node.
@Hadriscus: Yep, that sums it up nicely afaics. Correct fresnel is possible, but only “inside” a node.
@Ace Dragon: The glass node essentially behaves just like a “Glossy BSDF + Refraction BSDF mixed by Fresnel” shader group since it also emits two closures. For correct fresnel, it really needs to be one closure in the code.
@marcoG_ita: Currently I’m thinking that two Ubershaders would be the best solution: One artist-oriented shader (most likely the Disney BSDF, yes) and one physically 100% correct one based on exactly the paper you linked
To be more precise, I’ve designed the second one it for five modes for now:
- Glossy, Glass and Diffuse, which work just like the current ones (no fresnel included etc.), but with energy conservation because they support multiple scattering.
- Metallic, which behaves like the new Glossy, but with NK parameters (for realistic materials) instead of a color parameter.
- Coated, which models a diffuse surface covered with a clear coating. Currently, the parameters are the color of the diffuse base, the IOR of the coating and the roughness of the coating. The model is really accurate - not only multiple bounces in the coating itself, but also TIR on the way back from diffuse to the outside etc. are modeled.
This model could be extended by some more parameters like an absorption color of the coating, or by more possible base materials (e.g. coating on rough metal), but at some points it starts to be overkill, so we’ll have to find a balance there.
very interesting
Can you give any idea when we might be able use these new ubershader nodes in cycles.
thanks
happy bl
Thanks, this sounds really great! This will be another big step for Cycles.