Fresnel equation

Started by
14 comments, last by Chris_F 11 years, 1 month ago

Out of curiocity I wanted to compare Schlick's approximation to the real thing, so I had my hand at implementing it, though, admittedly, I'm not very good with the maths... sleep.png so I may have made a mistake, or more.

float CosTheta = dot(N, L);
float SinTheta = sqrt(1 - CosTheta * CosTheta);
float temp = n1 * (SinTheta / n2);
temp = n2 * sqrt(1 - temp * temp);
temp = (n1 * CosTheta - temp) / (n1 * CosTheta + temp);
float fresnel = temp * temp;

I'm getting a pretty noticable difference between this and Schlick's.

Advertisement

You implemented the formula for s-polarized light. But you want to implement the formula for non-polarized light. Which is simply R = (Rs + Rp) / 2.

You implemented the formula for s-polarized light. But you want to implement the formula for non-polarized light. Which is simply R = (Rs + Rp) / 2.

Doh. Second try?


float CosTheta = dot(N, L);
float SinTheta = sqrt(1 - CosTheta * CosTheta);

float t1 = n1 * (SinTheta / n2);
t1 = sqrt(1 - t1 * t1);
float t2 = (n1 * CosTheta - n2 * t1) / (n1 * CosTheta + n2 * t1);
float Rs = t2 * t2;
t2 = (n2 * CosTheta - n1 * t1) / (n2 * CosTheta + n1 * t1);
float Rp = t2 * t2;
float R = (Rs + Rp) / 2;

I'd reduce unnecessary calculations even more. Also your nominators were subtracting the values instead of adding them:


float cosThetaIncidence = dot(N, L);
float sinThetaIncidence = sqrt(1 - cosThetaIncidence * cosThetaIncidence);

float sinThetaTransmittance = n1 * (sinThetaIncidence / n2);
float cosThetaTransmittance = sqrt(1 - sinThetaTransmittance * sinThetaTransmittance);

float n1CosThetaTransmittance = n1 * cosThetaTransmittance;
float n2CosThetaTransmittance = n2 * cosThetaTransmittance;
float n1CosThetaIncidence = n1 * cosThetaIncidence;
float n2CosThetaIncidence = n2 * cosThetaIncidence;

float sPolarizedSqrt = (n1CosThetaIncidence - n2CosThetaTransmittance) / (n1CosThetaIncidence + n2CosThetaTransmittance);
float sPolarized = sPolarizedSqrt * sPolarizedSqrt;

float pPolarizedSqrt = (n2CosThetaIncidence - n1CosThetaTransmittance) / (n2CosThetaIncidence + n1CosThetaTransmittance);
float pPolarized = pPolarizedSqrt * pPolarizedSqrt;

float fresnel = (sPolarized + pPolarized) / 2;

I'd reduce unnecessary calculations even more. Also your nominators were subtracting the values instead of adding them:


float CosTheta = dot(N, L);
float SinTheta = sqrt(1 - CosTheta * CosTheta);

float t1 = n1 * (SinTheta / n2);
t1 = sqrt(1 - t1 * t1);

float n1CosThetaTransmittance = n1 * t1;
float n2CosThetaTransmittance = n2 * t1;
float n1CosThetaIncidence = n1 * CosTheta;
float n2CosThetaIncidence = n2 * CosTheta;

float t2 = (n1CosThetaIncidence - n2CosThetaTransmittance) / (n1CosThetaIncidence + n2CosThetaTransmittance);
float Rs = t2 * t2;
t2 = (n2CosThetaIncidence - n1CosThetaTransmittance) / (n2CosThetaIncidence + n1CosThetaTransmittance);
float Rp = t2 * t2;
float R = (Rs + Rp) / 2;

Could do, but it's unlikely to make a difference as shader compilers are pretty sly.

Edit: For me, it seems as though this full implementation is about 24% more instructions than Schlick's method.

For me, it seems as though this full implementation is about 24% more instructions than Schlick's method.


Only 24% more? Isn't Schlick Fresnel ks + (1 - ks) * pow(1 - dot(L, H), 5), which would be about 9 instructions? The full fresnel equation would be about 29 instructions.

Also, why is it dot(N, L) in your code? It should be the microfacets normal and not the normal of the macro surface.

For me, it seems as though this full implementation is about 24% more instructions than Schlick's method.


Only 24% more? Isn't Schlick Fresnel ks + (1 - ks) * pow(1 - dot(L, H), 5), which would be about 9 instructions? The full fresnel equation would be about 29 instructions.

Also, why is it dot(N, L) in your code? It should be the microfacets normal and not the normal of the macro surface.

I included the instruction cost of calculating f0 with pow((n2-n1) / (n2+n1) , 2.0), and I used N.L because I was testing this with normalized Blinn-Phong, not a microfaceted BRDF.


I was testing this with normalized Blinn-Phong, not a microfaceted BRDF


Blinn-Phong is the microfacet version of Phong. Blinn-Phong describes the percentage of microfacets oriented towards the halfway vector, cause these are the only microfacets reflecting light into the eye. So if you are extending your already partially existing microfacet BRDF by a fresnel term, you should use the microfacets normal, aka your halfway vector.

OK, one last question, since as I said, I am not very familiar with the math yet. I use sqrt(1-cos^2(theta)) to get sin(theta). Is this correct? It seems to work, but as far as I know, sqrt(1-cos^2(theta)) is actually only equal to abs(sin(theta)). Assuming this works, is this method preferable to taking the length of the cross product?

abs(sin(theta)) is fine in this case. I'd use your method instead of the cross product, since the calculation of the length of the cross product requires a square root as well, so your method is faster.

This topic is closed to new replies.

Advertisement