energy conserving shader

   9629   7   2
User Avatar
Member
186 posts
Joined: Jan. 2006
Offline
How would an energy conserving shader be approached in vex/vops etc? I have an idea involving using the reflected light vop to get the amount of incoming light then doing some math to distribute that amount among the shading components (diffuse, reflection, transmission, perhaps sss).

However, I seem to be out of luck when it comes to making this shader work in PBR. How do I measure light intensity when all I have is a bsdf? Any ideas?
User Avatar
Member
941 posts
Joined: July 2005
Offline
anamous
How would an energy conserving shader be approached in vex/vops etc? I have an idea involving using the reflected light vop to get the amount of incoming light then doing some math to distribute that amount among the shading components (diffuse, reflection, transmission, perhaps sss).

Hey anamous,

I think the bsdf's are already energy-conserving (on their own, that is). So it may be just a matter of distributing the weights among all the bsdf's you end up using in your composition (again, assuming that each bsdf, on its own, is energy-conserving). Here's a sketch of what I mean:
surface blah ( float k1=1,k2=1,k3=1; ) {
bsdf F1 = specular() * k1;
bsdf F2 = phong() * k2;
bsdf F3 = lambert() * k3;
F = (F1+F2+F3) / (k1+k2+k3);
}

Is that kind'a what you're after?
Edited by - May 2, 2008 02:57:11
Mario Marengo
Senior Developer at Folks VFX [folksvfx.com] in Toronto, Canada.
User Avatar
Member
186 posts
Joined: Jan. 2006
Offline
Hey Mario

maybe I'm thinking too complicated, and I'm glad to accept corrections. AFAIK energy conservation means that the different components are weighted with regards to their actual run time values - this is my naive understanding. the more specular reflection, the less diffuse reflection, for example. the idea being the simulation of the conservation of the photon energy hitting the sampled point.

this means that the kx constants that are used as multipliers in you code snippet would have to be variant, and depending on a procedure (get direct reflection, get direct transmission, get scattering, and finally get indirect reflection) and using them in that order, each dictating the multiplier for the next one. the most important factor is the amount of incoming energy that these components get to share. so if the surface is a full mirror, the direct reflection component uses almost all of the incoming energy, leaving little to nothing as multipliers for the other components.

and that's where I'm stuck right now: being restricted to manipulating BSDF's means that

1. I can't determine the amount of incoming energy (I could use reflectedlight to get a general estimate) but more importantly

2. I can't determine the amount of energy that a certain BSDF (a phong() for example) actually computes (reflects, transmits, etc) at the current sample.

both things being critical factors for energy conservation.

now I know this is all far away from what we all like about Mantra (quick and dirty tricks), and it certainly feels funny to think about rendering in these terms, but it is physics and after all the engine is called “physically based” . in practical terms, energy conservation means less of the “waaay overbright” metallic and glass objects that normally have to be painstakingly balanced against the more diffuse objects in a scene (mostly by tuning down reflections).

the bigger picture being of course that if SESI could manage to let us peek at runtime into the energy level a certain BSDF (or even *gasp* manipulate it), PBR would suddenly jump ahead of the whole pack of physically based renderers. we discussed something similar over at odforce.

BTW, how was your spectral mantra talk? :wink:

cheers,
Abdelkareem
User Avatar
Member
941 posts
Joined: July 2005
Offline
Hey anamous,

anamous
this means that the kx constants that are used as multipliers in you code snippet would have to be variant

Hmmm…. you lost me a little bit with the “variant weights” comment. As I understand it, energy conservation means that the bsdf shouldn't distribute any more or less energy than the incident amount (well, possibly less if there is absorption, and possibly more if you include interference, I think), but over the entire scattering volume, not in a particular viewing direction (i.e: energy distribution/conservation is completely defined by the bsdf, and only sampled in a particular viewing direction). And I guess what I'm saying is that I think that the built-in bsdf's are already taking care of that. So, as I see it, the energy conservation problem is only a result of applying more than one bsdf to the same incident energy: so adding 3 bsdf's, for example, will result in 3X the energy output, but a constant (not varying) set of scaling factors is all that's needed to restore it to unity, no? (assuming each bsdf itself is conserving, which again, I think they are).

anamous
and depending on a procedure (get direct reflection, get direct transmission, get scattering, and finally get indirect reflection) and using them in that order, each dictating the multiplier for the next one. the most important factor is the amount of incoming energy that these components get to share. so if the surface is a full mirror, the direct reflection component uses almost all of the incoming energy, leaving little to nothing as multipliers for the other components.

I think that direct transmission and direct reflection would have come from different sources to begin with, and so wouldn't be distributing the same energy, so I don't see why they should have to share any limits on their outputs (outisde of those calculated by their respective brdf/btdf's that is). Ditto for their indirect cousins, including scattering. Again, I think that it is only when you're applying more than one bsdf to the same energy source that you need to worry about wrecking conservation (like when adding lambert and phong which are essentially operating on the same incident energy). And then it is only a problem because we're splitting what should really be a single brdf into separate components (diffuse and phong).

I get the feeling that what you want to do is have a shader that smoothly goes all the way from flat diffuse to a perfect mirror in a physically plausible way. Let's talk about just reflectance for now (leave out transmission and scattering and whatever else). I think that for something like that to work you'd first need to restrict the user parameterization to a single surface quality like, say, “glossiness” with e.g, an internal mapping of glossiness to bsdf and all the “correct” or “expected” (as well as energy-preserving) mixtures in between.
In that case you could use a single unified brdf… but we can't write our own, as you well know… so we have to balance up to three bsdf's (which are the result of redistributing the same input energy) in such a way that the final scaling is still unity (for the combined scattering volumes not for a particular viewing direction).

Maybe you could try using lambert as the controlling bsdf and fill the energy gap with one of the specular models – all the way to perfect mirror (i.e: specular()) at glossiness=1. So, again assuming each built-in bsdf is already energy-conserving, the bsdf weights, based on a single user input of “glossiness”, might go something like:


Glossiness(g) | Lambert | Phong | PhongExponent | Specular
—————|————|———–|——————|———–
0 to 1 | 1-g | g*(g<1) | 400*exp(6*g-6) | (g>=1)



And of course I'm just guessing at the exponent: replace 400 for whatever you like as the smallest dot size, and replace 6 to change the rate at which it “shrinks”.

All other possibilities that come to mind right now for doing something more “correct” would involve writing a custom brdf (like an ashikhmin that includes the diffuse part, or his D-BRDF thing which is also pretty cool, etc), so yeah… the above hack is all I can think of at the moment (for the reflectance case anyway)

Andrew? Mark?


anamous
BTW, how was your spectral mantra talk? :wink:
Heh. I think it went well, thanks. At the very least, the slides might be useful to some people… I hope – SESI will be posting the stuff up sometime soon I think.

Cheers!
Mario Marengo
Senior Developer at Folks VFX [folksvfx.com] in Toronto, Canada.
User Avatar
Member
186 posts
Joined: Jan. 2006
Offline
thanks Mario

you're completely right about the distinction between “reflection in all directions” and “reflection towards eye position”, that had slipped beyond me.

I was thinking that since most reflecting materials utilize a fresnel coeff, maybe I could just primitively use that as a multiplier for the different components. so if there's a base lambert and a specular reflection on top, I would use kr as multiplier for the specular and (1-kr) as multiplier for the Lambert BSDF.

now, in the case of 3 or more components (specially involving transmission and sss), things get hairy indeed, but there surely is a way to break down that magical (1-kr) into useful fractions for the different components.

or maybe I'm on a completely wrong train of thought

cheers,
Abdelkareem
User Avatar
Member
941 posts
Joined: July 2005
Offline
anamous
I was thinking that since most reflecting materials utilize a fresnel coeff, maybe I could just primitively use that as a multiplier for the different components. so if there's a base lambert and a specular reflection on top, I would use kr as multiplier for the specular and (1-kr) as multiplier for the Lambert BSDF.

Ahhhhh…. my friend Fresnel…

Yeah. Things definitely get more complicated when Fresnel enters the picture. To quote Ashikhmin: “Another, perhaps more important limitation of the Lambertian diffuse term is that it must be set to zero to ensure energy conservation in the presence of a Fresnel-weighted term.”

To balance the two he uses the relationship:
diffusebrdf = C * Rd*(1-R(k1))*(1-R(k2))
where R(k) is the total hemispherical reflectance of the specular term in both the incident (k1) and outgoing (k2) directions, Rd is the diffuse albedo of the surface, and C is a normalization constant computed such that for Rd=1 the total incident and reflected energies are the same.

@Andrew: would you say albedo(bsdf) returns the “total hemispherical reflectance” of the given bsdf?

Anyway, he then goes on to compute C using the particular flavour of phong and fresnel that he's writing about. In our case, using the built-in fresnel, I guess weighing diffuse by (1-kr) – or just kt – is probably not a bad approximation, though I have no idea how successfully it may or may not conserve energy.

Transmission, I think, shouldn't be forced to share weights with lambert's (1-kr) because it's from a different source – true, it is also weighted by kt=1-kr), but its contribution is additional to whatever is going on at the surface with phong/lambert.

For sss, at least the portion of it that is not directly reflected at P but comes from a far field, again I think shouldn't be forced to share lambert's (1-kr) portion, for the same reasons as transmission. In reality, sss *should* take care of both what we consider to be lambert diffuse as well as the subsurface component (using its own set of fresnel terms along the way, actually). So, in a perfect world, I see sss as replacing lambert, not adding to it… but some of the models may have limitations that require a tiny bit of lambert to be added to the mix.

In short, when Fresnel comes into the picture, I personally have no clear idea of the “correct” way of doing things I've tried several different things over time, and some of them look pretty convincing, but I'm in no way qualified to say whether any of it is physically valid, or even plausible. Fresnel and I have been fighting for a while now… and I always loose! :shock:

Cheers.
Mario Marengo
Senior Developer at Folks VFX [folksvfx.com] in Toronto, Canada.
User Avatar
Member
186 posts
Joined: Jan. 2006
Offline
fresnel weighting is definitely better than simply combining the two components - see attached sample. But I'm also interested in what exactly albedo() means.

also, what exactly happens when I sum two BSDFs? is the result of each calculation added or something else? interesting to know

BTW your spectral Mantra talk is fantastic, thanks for sharing, very much appreciated!

cheers,
Abdelkareem

Attachments:
fresnel_nonfresnel.jpg (59.4 KB)

User Avatar
Member
941 posts
Joined: July 2005
Offline
anamous
fresnel weighting is definitely better than simply combining the two components - see attached sample.
Agreed – that's kind'a what I use too. Unfortunately it doesn't answer you initial question: is it energy conserving? – my guess is that it's probably close to being energy conserving but not guaranteed.

anamous
also, what exactly happens when I sum two BSDFs? is the result of each calculation added or something else? interesting to know
Yeah, the results of two distributions get added – at least that's how I think of it.

anamous
BTW your spectral Mantra talk is fantastic, thanks for sharing, very much appreciated!
Thanks!
Mario Marengo
Senior Developer at Folks VFX [folksvfx.com] in Toronto, Canada.
  • Quick Links