Re: HLMS PBR BRDF
Posted: Fri Dec 13, 2019 1:14 am
@xrgo
I'm comparing the BRDF from Filament you ported and it is actually not very different from ours!!!
Mathematically, VdotH is the same as LdotH (because H is the halfway vector and sits in the exactly in the middle between V and L)
Filament squares roughness, which we don't (I suspect this is THE main difference):
We perform R term:
They perform:
The only difference is their last multiplication by PI in the divisor which we don't. But we perform it later (continue reading).
We perform G term:
They perform:
The only difference is their last multiplication by PI in the divisor which they don't. But Filament does this in the R term. R and G get multiplied together, so this is essentially the same thing.
Basically:
(R / PI) * G = R * (G / PI)
Schlick (fresnel) is exactly the same code, so no need to compare.
Another subtle difference:
We do:
They do:
At no point your modified code uses "pixelData.specular.xyz * lightSpecular", so that's different. But you use lightDiffuse instead. They're extremely likely to be the same value but not necessarily.
Your code doesn't use pixelData.specular.xyz which is wrong (this is user/material data; could be set to 1,1,1 which is the default)
We perform more fancy stuff with diffuse, but it causes some subtle differences, nothing ultra major.
Therefore the main differences between the BRDF you implemented from filament and our current default are:
I believe this is the major difference between Filament's and our BRDF, unless pixelData.specular.xyz * lightSpecular gives a hugely different value from just multiplying against lightDiffuse
Cheers
I'm comparing the BRDF from Filament you ported and it is actually not very different from ours!!!
Mathematically, VdotH is the same as LdotH (because H is the halfway vector and sits in the exactly in the middle between V and L)
Filament squares roughness, which we don't (I suspect this is THE main difference):
Code: Select all
float roughness = pixelData.roughness * pixelData.roughness;Code: Select all
float f = ( NdotH * sqR - NdotH ) * NdotH + 1.0;
float R = sqR / (f * f + 1e-6f);Code: Select all
float a2 = a * a; //note: a2 = sqR.
float f = (NoH * a2 - NoH) * NoH + 1.0;
return a2 / (PI * f * f);We perform G term:
Code: Select all
float Lambda_GGXV = NdotL * sqrt( (-pixelData.NdotV * sqR + pixelData.NdotV) * pixelData.NdotV + sqR );
float Lambda_GGXL = pixelData.NdotV * sqrt( (-NdotL * sqR + NdotL) * NdotL + sqR );
float G = 0.5 / (( Lambda_GGXV + Lambda_GGXL + 1e-6f ) * 3.141592654);Code: Select all
float a2 = a * a;
float GGXV = NoL * sqrt((-NoV * a2 + NoV) * NoV + a2);
float GGXL = NoV * sqrt((-NoL * a2 + NoL) * NoL + a2);
return 0.5 / (GGXV + GGXL);Basically:
(R / PI) * G = R * (G / PI)
Schlick (fresnel) is exactly the same code, so no need to compare.
Another subtle difference:
We do:
Code: Select all
float3 Rs = ( fresnelS * (R * G) ) * pixelData.specular.xyz * lightSpecular;
Code: Select all
vec3 Fr = (D * V) * F;Your code doesn't use pixelData.specular.xyz which is wrong (this is user/material data; could be set to 1,1,1 which is the default)
We perform more fancy stuff with diffuse, but it causes some subtle differences, nothing ultra major.
Therefore the main differences between the BRDF you implemented from filament and our current default are:
- Roughness is squared (I suspect this is THE major cause of the difference)
- You use lightDiffuse, don't use lightSpecular nor pixelData.specular.xyz
- Ogre does some more fancy stuff with diffuse (I seriously doubt this is the reason)
Code: Select all
float sqR = pixelData.roughness * pixelData.roughness;
sqR = sqR * sqR;Cheers

