Here's a linkdump for you to research ;) It is a pretty small change - you've just got to find the right normalization factor for your lighting model, which will look something like
I didn't have the slightest idea that Blinn-Phong was not energy conserving. I've read that the Phong-reflection model was not energy conserving but that's about as far as I got. I had no idea that a normalized Blinn-Phong model existed hahaha. But I think I will look into using it as soon as I get things working with the non-normalized one, it shouldn't be too hard to just replace it then with the normalized one.float norm = ( 80.0 + 2.0 ) / 2.0; float spec = mask * norm * max(0.0, pow(dot(f_eyeNormal, halfWay), 80));http://www.altdevblo...l-introduction/
Thanks, these are all some interesting articles. I just went through the first two ones. Not exactly related to this topic but the first one gave me a new perspective on how to think of the rendering equation
Backing up from the scalar/vector issue for a moment -- fZero is the "reflectance an normal incidence", or in other words, the minimum percent of reflectance that will occur at any angle. You're using a value of around 1.8%, which is physically correct for a material with an IOR of ~1.3, such as water or ice.
Regarding the specular mask, you said I don't need my constant anymore as this is what my fresnel value is supposed to account for instead. But again it is just a scalar value, should I just create a vec3 where all values in the vector correspond to the fresnel value?
Update: Read what you said wrong, you actually mentioned fZero and not the fresnel value itself. So did you something more like this:
This is your specular constant / specular mask -- You start with an IOR of 1.3, which you convert into an fZero of 0.018 (and then use Schlick's approximation to get another f value somewhere between 0.018 and 1.0), and then use this f value to mask out your reflections.
N.B. you should actually use this value to mix specular and diffuse, because any light that isn't reflected is instead refracted and diffused e.g.spec = norm * pow( nDotH, p ); diffuse = nDotL * albedo; combined = mix( diffuse, spec, fresnel );
For most materials (dielectrics, insulators), storing a single IOR/fZero value in a scalar value is enough, because the IOR of these materials is typically the same when lit by either red, green or blue light.
However, some materials (metals) do require you to store a vector or IOR/fZero values, because the IOR when measured with red wavelength light is different than when measured with blue wavelength light. If you take three IOR values (measured at the red, blue and green wavelengths) and then convert them into three fZero values, then the result is an RGB spec-mask / spec-constant / fZero value.
So, yes, your modified code where fZero is a vec3 is suitable for rendering any kind of materials, whereas if fZero was just a scalar, it's still useful, but only for certain kinds of non-metallic materials with IOR values that are stable over the visible light spectrum.
It's pretty much as you said, I just decided to go with ice material and thus choose it's IOR. There is not much reason for that choice in my implementation, I just wanted to get a working example and just choose some material at random. Thanks for the clarification btw, things makes more sense now! I actually have a quesion with regards to the mixing of the specular and diffuse reflection but will post it below instead as I feel it has more relation to what I will write there.
What you explained to me about why one would sometimes choose an RGB spec-mask over a scalar value actually sounds much like something I wanted to implement after having the fresnel effect working. I believe they referred to it as chromatic dispersion when I had a small look at it, basically that some materials are dependant on the wavelength light. It sounds like having an RGB spec-mask is more flexible, by allowing you to choose any material you like. Of course I assume the penalty here is that two more floating-point values are involved in the calculations, but for my simple purpose the trade-off is quite small anyway.
Yes, it sounds like it. If we assume that my above normalization code is correct (it's probably not - I just copied the first one I came across), we can see that it adds an extra "* 41" onto the brightness when using a power of 80 -- thats a lot of brightness that's missing from the original blinn-phong.
. I then multiplied the red channel by 1000 and the specular highlights appeared. Going by this, should I assume it's as you and Tsus said, that the values I had were so small that the specular highlights were barely visible?
Also, you're using the IOR value for water, which is not very reflective when viewed front-on (1.8%), and is only very reflective when viewed at a glancing angle. Do your (red) specular highlights get brighter/dimmer depending on whether they are front-on or glancing? If so, then yes, I would just assume your brightness levels aren't well calibrated (partly due to energy loss) and there's no real problem going on.
What is happening in my app is that if align myself with the direction of light the specular reflections look almost white (well it's actually really really light red to be more precise). If I view it at glancing angles instead the intensity of the red color increase more and more. I'll post some screenshots I took a while ago to give a better idea of it. Anyhow it seems at least that it is working properly(?) for glancing angles. But as have been mentioned, if viewed head-on I should not have much specular reflection (1.8%)...yet the only thing that change is the intensity of red color . The highlights are all there in the same size, I understood that the closer I (viewer) get aligned with the normal the less specular reflection I should have, and the more refraction should occur. Yet this is clearly not the case and I wondered if this is because I'm not mixing the specular and diffuse reflection together?
Update: Now that I think of it a little bit more I think I've just misunderstood it. Judging by the pictures that should be correct. Viewing it straight on should result in dimmer reflection or to be more specific then less intensity in red color in this case...and more reflection (increased intensity in red) at glancing angles. For whatever reason I thought that for the result to be correct my specular highlights had to become smaller and weaker until they almost completely disappear...which they do not in the picture. I would like a small correction here if possible, it would be appreciated. Also I might as well ask this here instead of doing it in another reply; in the code you posted above with regards to lerping the values my code would be something like this:
vec3 fresnel = vec3(fZero)-vec3(fZero)*exp+exp; vec3 diffuseReflection = diffuseConstant*clamp(dot(f_lightDirection, f_eyeNormal), 0, 1); vec3 specularReflection = ??? * max(0.0,pow(dot(f_eyeNormal, halfWay), 80)); vec3 combined = mix(diffuseReflection, specularReflection, fresnel);
What I'm slightly confused about here is that I need specularReflection to be a vec3 but just using the dot vector will return a scalar value and that won't allow me to use mix. As you mentioned and explained pretty well earlier my fZero acts as my specular mask, should I use it in place of '???' (vec3(fZero)) there? Is the usage of it there independent of the fresnel approximation that I use when using mix later on? Because if so I would be using fZero twice, once as a weight in the mix function and once when computing the specularReflection variable. Otherwise what am I missing there? I guess the problem here could also be because I've made the simplification of not using light intensities in my reflection model, assuming them to be combined with the constants. In that case I could of course just describe the variable specularReflection as:
vec3 = vec3(max(0.0,pow(dot(f_eyeNormal, halfWay), 80)));
Well I feel like I'm all over the place about such small thing so I'm not entirely sure. So I had to ask
Here are three pictures, from viewing it almost head-on to glancing angles. As you can see the intensity increases as the angle is increasing.