Jump to content

  • Log In with Google      Sign In   
  • Create Account

FREE SOFTWARE GIVEAWAY

We have 4 x Pro Licences (valued at $59 each) for 2d modular animation software Spriter to give away in this Thursday's GDNet Direct email newsletter.


Read more in this forum topic or make sure you're signed up (from the right-hand sidebar on the homepage) and read Thursday's newsletter to get in the running!


Blinn-Phong with Fresnel effect.


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
15 replies to this topic

#1 Suen   Members   -  Reputation: 160

Like
0Likes
Like

Posted 20 May 2012 - 12:26 PM

I might be confusing myself here with what the Fresnel effect is exactly supposed to do. From what I understand, the idea of using Fresnel effect is to make the amount of reflection dependant on the viewing angle. Basically the effect should be that at grazing angles we have a higher reflection. So the way I imagine it is that the wider the angle is, the more reflection we get. where a total reflection would happen at 90 degrees(?). Meanwhile if we have 0 degrees we would have no reflection at all and all light would be absorbed.

What I tried to do was to implement this through GLSL since I already had a working implementation of Blinn-Phong reflection model with Phong shading. Basically I wanted the specular effect to be dependent on this by multiplying it with the Fresnel term. What I did was to compute an approximation of the Fresnel term, widely known as Schlick approximation and then just use it in the final result. However that just gave me a mesh with no specular highlights instead. It was as if I was just performing diffuse and ambient reflection. I'm sure there is something I've understood completely wrong about this but I'm pretty much confused about it at the moment. I'll post the shaders:

Vertex shader

#version 330

layout(location = 0) in vec4 position;
layout(location = 1) in vec3 normal;

uniform mat4 modelToWorldMatrix;
uniform mat4 worldToViewMatrix;
uniform mat4 viewToClipMatrix;
uniform mat3 normalMatrix; //3x3 submatrix of the modelview matrix. No non-uniform scaling is used.

uniform vec4 worldLight;

smooth out vec3 eyeNormal;
smooth out vec3 lightDirection;
smooth out vec3 viewDirection;

void main()
{
	 vec4 viewPos = worldToViewMatrix*modelToWorldMatrix*position; //Vertex in eye space
	 gl_Position = viewToClipMatrix*viewPos; //Vertex shader has to generate vertex in clip-space

	 eyeNormal = normalize(normalMatrix*normal); //Transform normal to eye space
	 vec4 eyeWorldLight = worldToViewMatrix*worldLight; //Transform light position to eye space

	 lightDirection = normalize(eyeWorldLight.xyz-vec3(viewPos)); //Compute direction to light
	 viewDirection = normalize(-viewPos.xyz); //Compute direction to viewer
}

Fragment shader

#version 330

//Make a simplification here, assuming the light intensity is already
//combined together with the material properties of the object
uniform vec3 ambientConstant;
uniform vec3 diffuseConstant;
uniform vec3 specularConstant;
uniform float fZero; //Fresnel term at normal incidence

smooth in vec3 eyeNormal;
smooth in vec3 lightDirection;
smooth in vec3 viewDirection;

out vec4 outputColor;

void main()
{
	 //Interpolated normal, light direction and view direction
	 vec3 f_eyeNormal = normalize(eyeNormal);
	 vec3 f_lightDirection = normalize(lightDirection);
	 vec3 f_viewDirection = normalize(viewDirection);

	 vec3 halfWay = normalize(f_lightDirection+f_viewDirection); //Halfway vector

	 //Fresnel approximation
	 float base = 1-dot(f_viewDirection, halfWay);
	 float exp = pow(base, 5);
	 float fresnel = exp+fZero*(1.0-exp);

	 //Blinn-phong reflection model
	 vec3 ambientReflection = ambientConstant;
	 vec3 diffuseReflection = diffuseConstant*clamp(dot(f_lightDirection, f_eyeNormal), 0, 1);
	 vec3 specularReflection = specularConstant*max(0.0,pow(dot(f_eyeNormal, halfWay), 80));

	 //Final color of the fragment
	 outputColor.rgb =  ambientReflection + diffuseReflection + fresnel*specularReflection;
	 outputColor.a = 1.0;
}

fZero is calculated in the application as following:

GLfloat fZero = pow((1.0f-(1.0f/1.31f)), 2)/pow((1.0f+(1.0f/1.31f)), 2);


Sponsor:

#2 Yours3!f   Members   -  Reputation: 1397

Like
0Likes
Like

Posted 20 May 2012 - 03:48 PM

here's the relevant wiki article:
http://en.wikipedia....s_approximation
here's Schlick's original paper from 1994:
http://www.ics.uci.e...s/Schlick94.pdf
and if you're successful with implementing the Schlick method, then you may proceed to more advanced approximations:
http://en.wikipedia....esnel_equations

so for you I think the fresnel term should be:
//Fresnel approximation
float base = 1-dot(f_viewDirection, halfWay);
float exp = pow(base, 5);
float fresnel = fZero + (1 - fZero)*exp;

Edited by Yours3!f, 20 May 2012 - 04:10 PM.


#3 Suen   Members   -  Reputation: 160

Like
0Likes
Like

Posted 20 May 2012 - 04:24 PM

here's the relevant wiki article:
http://en.wikipedia....s_approximation
here's Schlick's original paper from 1994:
http://www.ics.uci.e...s/Schlick94.pdf
and if you're successful with implementing the Schlick method, then you may proceed to more advanced approximations:
http://en.wikipedia....esnel_equations

so for you I think the fresnel term should be:

//Fresnel approximation
float base = 1-dot(f_viewDirection, halfWay);
float exp = pow(base, 5);
float fresnel = fZero + (1 - fZero)*exp;


I've taken a look at the first article (as well as several others that mentioned it). Haven't checked the original paper yet though. Anyhow, the code you wrote is exactly the same as the one I posted, it's just that the one I've posted is re-written. Both of them give: f0 + exp - f0*exp

#4 Tsus   Members   -  Reputation: 1062

Like
0Likes
Like

Posted 20 May 2012 - 06:09 PM

Hi,

If I'm not mistaken, the angle in question should be between the normal and the light direction. (To be more precisely half of the angle between the negated incoming light and outgoing light direction, but if we work with perfect reflection it comes down to NdotL.)
float base = max(0, 1-dot(n, l)); // better do the max here. (According to the OptiX source code not a bad idea as there are cases which yield artifacts if omitted.)
float exp = pow(base, 5);
float fresnel = fZero + (1-fZero)*exp;

fZero a little shorter:
GLfloat fZero = pow( (1.0f-(1.0f/1.31f))/ (1.0f+(1.0f/1.31f)), 2);

Make sure, n and l are both normalized and in the same space. For debugging purposes you can set fZero to 0.

Hope it helps a little.
Cheers!

Edited by Tsus, 21 May 2012 - 02:21 AM.


#5 Suen   Members   -  Reputation: 160

Like
0Likes
Like

Posted 20 May 2012 - 10:17 PM

Hi,

If I'm not mistaken, the angle in question should be between the normal and the light direction. (To be more precisely half of the angle between the negated incoming light and outgoing light direction, but if we work with perfect reflection it comes down to NdotL.)

float base = max(0, 1-dot(n, l)); // better do the max here. (According to the OptiX source code not a bad idea as there are cases which yield artifacts if omitted.)
float exp = pow(base, 5);
float fresnel = fZero + (1-fZero)*exp;
If I’m not mistaken fZero is:
GLfloat fZero = pow( (1.0f-(1.0f/1.31f))/ (1.0f+(1.0f/1.31f)), 2);

Make sure, n and l are both normalized and in the same space (your normal is in view space, your light direction in world space). For debugging purposes you can set fZero to 0.

Hope it helps a little.
Cheers!


Hello, greatly appreciate the suggestion. I'm kind of curious whether it's view direction and the halfway vector or if it's the light and normal as you said. I've seen some people suggesting the halfway vector and view direction in their implementation but when I had a look at nvidias whitepaper that describes Fresnel reflection, the article describes it as you do, that you want the angle between the direction to the light and the surface normal. What exactly is the difference between the two?

Anyhow, I modified my fragment shader and tried the above but it gave unfortunately the same result. I also made sure that the interpolated surface normal and light direction are normalized before using them. Both should also be in the same space (view space) as can be seen in the vertex shader so there should be no problem there. Setting fZero to 0 did not make any difference. So...there is something else strange going on. I'll post the modified fragment shader for clarity.

The way you calculated fZero is almost identical to mine, except that I would guess mine is slightly more inefficient. Either way both give the same result so there should be no problem there as well.

#version 330

//Make a simplification here, assuming the light intensity is already
//combined together with the material properties of the object
uniform vec3 ambientConstant;
uniform vec3 diffuseConstant;
uniform vec3 specularConstant;
uniform float fZero; //Fresnel term at normal incidence

smooth in vec3 eyeNormal;
smooth in vec3 lightDirection;
smooth in vec3 viewDirection;

out vec4 outputColor;

void main()
{
	 //Interpolated normal, light direction and view direction
	 vec3 f_eyeNormal = normalize(eyeNormal);
	 vec3 f_lightDirection = normalize(lightDirection);
	 vec3 f_viewDirection = normalize(viewDirection);

	 vec3 halfWay = normalize(f_lightDirection+f_viewDirection); //Halfway vector

	 //Fresnel approximation
	 float base = max(0, 1-dot(f_eyeNormal, f_lightDirection));
	 float exp = pow(base, 5);
	 float fresnel = fZero + (1-fZero)*exp;
	
	 //Blinn-phong reflection model
	 vec3 ambientReflection = ambientConstant;
	 vec3 diffuseReflection = diffuseConstant*clamp(dot(f_lightDirection, f_eyeNormal), 0, 1);
	 vec3 specularReflection = specularConstant*max(0.0,pow(dot(f_eyeNormal, halfWay), 80));
	
	 //Final color of the fragment
	 outputColor.rgb =  ambientReflection + diffuseReflection + fresnel*specularReflection;
	 outputColor.a = 1.0;
}


#6 Hodgman   Moderators   -  Reputation: 31920

Like
1Likes
Like

Posted 20 May 2012 - 10:44 PM

Hello, greatly appreciate the suggestion. I'm kind of curious whether it's view direction and the halfway vector or if it's the light and normal as you said. I've seen some people suggesting the halfway vector and view direction in their implementation but when I had a look at nvidias whitepaper that describes Fresnel reflection, the article describes it as you do, that you want the angle between the direction to the light and the surface normal. What exactly is the difference between the two?

The input to Schlick's approximation is cos theta, where 2*theta is the angle between the incoming light and reflected light direction.
So 2*theta == angleDiff( in, out ), and we also know that out == reflect( in, N ), and thus angleDiff( in, N ) == angleDiff( out, N ) == theta.
in  N  out
  \o|o/  
   \|/	(o is theta)
So from in to out is 2*theta, and from in to N is theta and from N to out is theta. Or alternatively, dot(N,L) == dot(N,reflect(L,N) == cos theta.

Edited by Hodgman, 20 May 2012 - 10:47 PM.


#7 Tsus   Members   -  Reputation: 1062

Like
0Likes
Like

Posted 21 May 2012 - 02:20 AM

I have the vague feeling that the product of Fresnel and dot(halfVector, normal) is rather small and thus barely discernible. Maybe that’s the problem?
Could you output the fresnel term as red and the dot(halfVector, normal) as green? Does any yellow appear?

Frankly, I haven’t seen the fresnel term being applied to the specular light in the blinn phong model. It’s more common for blending between mirror-like reflections and refractions. Ashikhmin-Shirley does use it (among others) for weighting the specular light, though.

Best regards

#8 Yours3!f   Members   -  Reputation: 1397

Like
-1Likes
Like

Posted 21 May 2012 - 02:34 AM


here's the relevant wiki article:
http://en.wikipedia....s_approximation
here's Schlick's original paper from 1994:
http://www.ics.uci.e...s/Schlick94.pdf
and if you're successful with implementing the Schlick method, then you may proceed to more advanced approximations:
http://en.wikipedia....esnel_equations

so for you I think the fresnel term should be:

//Fresnel approximation
float base = 1-dot(f_viewDirection, halfWay);
float exp = pow(base, 5);
float fresnel = fZero + (1 - fZero)*exp;


I've taken a look at the first article (as well as several others that mentioned it). Haven't checked the original paper yet though. Anyhow, the code you wrote is exactly the same as the one I posted, it's just that the one I've posted is re-written. Both of them give: f0 + exp - f0*exp


oh, ok, then you just need to find out how to use the fresnel term, because then you calculated it right...
maybe try to add it:
outputColor.rgb =  ambientReflection + diffuseReflection + specularReflection + fresnel;

Edited by Yours3!f, 21 May 2012 - 02:45 AM.


#9 Hodgman   Moderators   -  Reputation: 31920

Like
0Likes
Like

Posted 21 May 2012 - 02:56 AM

have the vague feeling that the product of Fresnel and dot(halfVector, normal) is rather small and thus barely discernible. Maybe that’s the problem?
Could you output the fresnel term as red and the dot(halfVector, normal) as green? Does any yellow appear?

Trying some debug visualisations like this is a good idea to find your problematic variable.
N.B. that the original (non-normalized) blinn-phong model isn't energy conserving, and a lot of energy goes missing (isn't reflected) when you use high power values -- it could be that your pow-80 light source is simply too dim to see when combined with your 0.018 spec mask. Try outputting the specular result times a thousand into a channel to see if it's still missing/black. If you're going for physically correct lighting, you're going to have to drop blinn-phong in favour of normalized blinn-phong.

Also, on a side note, you don't need your specularConstant variable any more, because your fZero variable is acting as your spec mask as well - so you've currently got two spec mask values.

Frankly, I haven’t seen the fresnel term being applied to the specular light in the blinn phong model. It’s more common for blending between mirror-like reflections and refractions. Ashikhmin-Shirley does use it (among others) for weighting the specular light, though.

Seeing reflection=specular and refraction=diffuse, most physically based models should use fresnel to mix their diffuse/spec lighting - including normalized blinn-phong.

Edited by Hodgman, 21 May 2012 - 02:59 AM.


#10 Suen   Members   -  Reputation: 160

Like
0Likes
Like

Posted 21 May 2012 - 09:20 AM

First I'd like to thank all of you for the replies so far.

Hello, greatly appreciate the suggestion. I'm kind of curious whether it's view direction and the halfway vector or if it's the light and normal as you said. I've seen some people suggesting the halfway vector and view direction in their implementation but when I had a look at nvidias whitepaper that describes Fresnel reflection, the article describes it as you do, that you want the angle between the direction to the light and the surface normal. What exactly is the difference between the two?

The input to Schlick's approximation is cos theta, where 2*theta is the angle between the incoming light and reflected light direction.
So 2*theta == angleDiff( in, out ), and we also know that out == reflect( in, N ), and thus angleDiff( in, N ) == angleDiff( out, N ) == theta.
in  N  out
  \o|o/  
   \|/	(o is theta)
So from in to out is 2*theta, and from in to N is theta and from N to out is theta. Or alternatively, dot(N,L) == dot(N,reflect(L,N) == cos theta.


This makes sense and of course is how I understood it at first when I read through nvidias article. The formula I had at first where I used dot product between the halfway vector and view direction was taken from here and apparently the formula there is taken from GPU Gems 3. Either way it made me confused as to what the angle represents, the one between the incoming light and surface normal or the one between the halfway vector and view direction.

I have the vague feeling that the product of Fresnel and dot(halfVector, normal) is rather small and thus barely discernible. Maybe that’s the problem?
Could you output the fresnel term as red and the dot(halfVector, normal) as green? Does any yellow appear?

Frankly, I haven’t seen the fresnel term being applied to the specular light in the blinn phong model. It’s more common for blending between mirror-like reflections and refractions. Ashikhmin-Shirley does use it (among others) for weighting the specular light, though.

Best regards


It might indeed be the case that product of the Fresnal and dot product is so small that I get no result from it. I'll explain below to Hodgman's reply why. And forgive me, this is a really really stupid question but how would I output the fresnel term as red and the dot(halfVector, normal) as green? Both are scalar values to begin with, if I have to change them into red and green wouldn't I need a vec3? It's probably a stupid question...

have the vague feeling that the product of Fresnel and dot(halfVector, normal) is rather small and thus barely discernible. Maybe that’s the problem?
Could you output the fresnel term as red and the dot(halfVector, normal) as green? Does any yellow appear?

Trying some debug visualisations like this is a good idea to find your problematic variable.
N.B. that the original (non-normalized) blinn-phong model isn't energy conserving, and a lot of energy goes missing (isn't reflected) when you use high power values -- it could be that your pow-80 light source is simply too dim to see when combined with your 0.018 spec mask. Try outputting the specular result times a thousand into a channel to see if it's still missing/black. If you're going for physically correct lighting, you're going to have to drop blinn-phong in favour of normalized blinn-phong.

Also, on a side note, you don't need your specularConstant variable any more, because your fZero variable is acting as your spec mask as well - so you've currently got two spec mask values.

Frankly, I haven’t seen the fresnel term being applied to the specular light in the blinn phong model. It’s more common for blending between mirror-like reflections and refractions. Ashikhmin-Shirley does use it (among others) for weighting the specular light, though.

Seeing reflection=specular and refraction=diffuse, most physically based models should use fresnel to mix their diffuse/spec lighting - including normalized blinn-phong.


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.

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:

vec3 specularReflection = (vec3(fZero)-vec3(fZero)*exp+exp)*max(0.0,pow(dot(f_eyeNormal, halfWay), 80));

At least this is the approach I took when trying your suggestion above. I created a vec3(fresnel) and multiplied it by my pow-80. I then multiplied the red channel by 1000 and the specular highlights appeared. Going by this, if I implemented your suggestion correct, 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?

Edited by Suen, 21 May 2012 - 07:57 PM.


#11 Hodgman   Moderators   -  Reputation: 31920

Like
1Likes
Like

Posted 21 May 2012 - 10:50 PM

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.

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
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/
http://www.altdevblo...based-lighting/
http://www.altdevblo...icrofacet-brdf/
http://www.rorydrisc...ation-in-games/
http://www.thetenthp...de/archives/255

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:

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.
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.

. 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?

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.
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.

Edited by Hodgman, 21 May 2012 - 10:54 PM.


#12 Tsus   Members   -  Reputation: 1062

Like
0Likes
Like

Posted 22 May 2012 - 02:54 AM

Hi,

float norm = ( 80.0 + 2.0 ) / 2.0;

You remembered it better than I have. Posted Image I looked it up and you almost got the normalization of the specular part of the physically plausible Phong BRDF.
float normS = ( n + 2.0 ) / (2.0*Pi);  // here n is 80

This is the normalization of the specular part of the phyically plausible Blinn-Phong BRDF.
float normS = ( n + 8.0 ) / (8.0*Pi);  // here n is 80

Also note that in both models the diffuse part is normalized by:
float normD = 1.0 / Pi;

Oh, and the irradiance attentuates with the squared distance. (Just divide by the squared distance.)

Best regards

#13 Suen   Members   -  Reputation: 160

Like
0Likes
Like

Posted 22 May 2012 - 10:17 AM

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.

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
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/
http://www.altdevblo...based-lighting/
http://www.altdevblo...icrofacet-brdf/
http://www.rorydrisc...ation-in-games/
http://www.thetenthp...de/archives/255


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 Posted Image

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:

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.
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.


. 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?

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.
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.

Attached Thumbnails

  • Img1.PNG
  • Img2.PNG
  • Img3.PNG

Edited by Suen, 23 May 2012 - 07:17 AM.


#14 Suen   Members   -  Reputation: 160

Like
0Likes
Like

Posted 22 May 2012 - 10:18 AM

Hi,


float norm = ( 80.0 + 2.0 ) / 2.0;

You remembered it better than I have. I looked it up and you almost got the normalization of the specular part of the physically plausible Phong BRDF.
float normS = ( n + 2.0 ) / (2.0*Pi);  // here n is 80

This is the normalization of the specular part of the phyically plausible Blinn-Phong BRDF.
float normS = ( n + 8.0 ) / (8.0*Pi);  // here n is 80

Also note that in both models the diffuse part is normalized by:
float normD = 1.0 / Pi;

Oh, and the irradiance attentuates with the squared distance. (Just divide by the squared distance.)

Best regards


I better take a look at how these are derived first :D But this will be helpful. I totally forgot about the light attenuation, which I should have remembered since I'm using a point light source. Thanks for the reminder!

#15 Suen   Members   -  Reputation: 160

Like
0Likes
Like

Posted 23 May 2012 - 05:24 PM

While I've been playing around to solve the fresnel effect (which I'm still stuck at, hopefully an answer can come soon to my reply!) I thought I would give the light attenuation a try. Having tried it out I got some weird results (or perhaps that is just the way it is supposed to look like). I will post both pictures and code for the fragment shader below. I've removed things relating to my fresnel calculations as I wanted to just keep the code less heavy for the reader. Besides I don't want to mix this problem together with the actual problem I have (topic) but I felt it was a bit unnecessary to start a whole new thread about it. So simply put, I'm just doing a Blinn-Phong per-fragment calculation in this post and for now I've just added light attenuation to my diffuse reflection ONLY.

Fragment shader
#version 330

//Make a simplification here, assuming the light intensity is already
//combined together with the material properties of the object
uniform vec3 ambientConstant;
uniform vec3 diffuseConstant;
uniform vec3 specularConstant;

smooth in vec3 eyePos;
smooth in vec3 eyeNormal;
smooth in vec3 eyeLight;

out vec4 outputColor;

void main()
{
	 //Interpolated normal, light direction and view direction
	 vec3 f_eyeNormal = normalize(eyeNormal);
	 vec3 f_lightDirection = normalize(eyeLight-eyePos);
	 vec3 f_viewDirection = normalize(-eyePos);

	 vec3 halfWay = normalize(f_lightDirection+f_viewDirection); //Halfway vector

	 //Light attenuation
	 float lightDistance = length(eyeLight-eyePos);
	 float att = 1/(lightDistance*lightDistance);

	 //Blinn-phong reflection model
	 vec3 ambientReflection = ambientConstant;
	 vec3 diffuseReflection = diffuseConstant*clamp(dot(f_lightDirection, f_eyeNormal), 0, 1);
	 vec3 specularReflection = specularConstant*clamp(dot(f_lightDirection, f_eyeNormal), 0, 1)*vec3(pow(clamp(dot(f_eyeNormal, halfWay), 0, 1), 80));

	 //Final color of the fragment
	 outputColor.rgb =  ambientReflection + att*diffuseReflection + specularReflection;
	 outputColor.a = 1.0;
}

As seen in the code I compute the distance to the light and then compute the attenuation as the inverse squared distance. This is then multiplied with my diffuse reflection. If you look at the first image below my mesh is very dark. To be more precise, what it looks like to me is that the remaining value of my diffuse reflection, after having been divided by the inverse squared distance, is so small that basically the only thing contributing to the fragment color is my ambient reflection and of course the specular reflection. Thus the mesh seem to have the same color all over it. I thought this made sense at first because my mesh is located at (0, 0, -50) in the world and the starting position of my light is (0, 0, 50) so there's 100 difference in units (rough estimation, of course the vertex positions vary). But if you look at the second picture the position of the light is at (0, 0, -20). The distance is much less here, yet the color of the mesh remains exactly the same (the only change here is the movement of the specular highlights but that makes sense as the light is moving closer to the camera). Finally in the third image the position of the light is at (0, 0, -48.7) so the distance here is really really small. Only here is there a change on the color of the mesh and only on a small area of it.

I thought this behaviour was strange as I expected the mesh to get "lit" much earlier but clearly this wasn't the case. I thought it might be the method itself and tried some other methods (for example one were I just used the inverse distance instead of the inverse square distance) but none of them made any huge difference. Some would get lit sliiiiightly earlier (perhaps from (0, 0, 44) or (0, 0, 45)).

Is this really the way it is supposed to behave? I can't exactly find a fault in it, I just found it strange that there wouldn't be any significant change until the light is really really close. I assume I would have to use some constant to change the attenuation to my needs?

Update: Seems that using a light radius solved this problem, although I read that it's not physically correct or at least a good approximation of it. Either way opinions are still appreciated about this, or at least suggestion(s) of good light attenuation models to use.

Attached Thumbnails

  • lightAtt1.PNG
  • lightAtt2.PNG
  • lightAtt3.PNG

Edited by Suen, 23 May 2012 - 08:25 PM.


#16 Suen   Members   -  Reputation: 160

Like
0Likes
Like

Posted 24 May 2012 - 11:46 AM

Just a small update: having searched quite a bit I found some nice posts and websites explaining light attenuation a bit more in detail so I have a slightly better idea of what is going on. So..the post above can mostly be ignored (but anyone is of course welcome to contribute to it regardless). Other then that I'm still stuck where I left off, with the fresnel part (see my reply to Hodgman) so any help there is greatly appreciated

Edited by Suen, 24 May 2012 - 11:47 AM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS