void ComputeBRDFIntegralImportanceSampling( System.IO.FileInfo _TableFileName0, System.IO.FileInfo _TableFileName1, int _TableSize ) {
const int SAMPLES_COUNT = 1024;
double[,] Table0 = new double[_TableSize,_TableSize];
double[,] Table1 = new double[_TableSize,_TableSize];
WMath.Hammersley QRNG = new WMath.Hammersley();
double[,] Sequence = QRNG.BuildSequence( SAMPLES_COUNT, 2 );
float3 View = new float3();
float3 Light = new float3();
float3 Half = new float3();
for ( int Y=0; Y < _TableSize; Y++ ) {
double Roughness = Math.Max( 0.01f, (float) Y / (_TableSize-1) );
//Roughness = Math.Pow( 1.0 - Roughness, 4.0 );
double r2 = Roughness*Roughness;
for ( int X=0; X < _TableSize; X++ ) {
float CosThetaView = (float) (1+X) / _TableSize;
float SinThetaView = (float) Math.Sqrt( 1.0f - CosThetaView*CosThetaView );
View.x = SinThetaView;
View.y = CosThetaView;
View.z = 0.0f;
double SumA = 0.0;
double SumB = 0.0;
for ( int SampleIndex=0; SampleIndex < SAMPLES_COUNT; SampleIndex++ ) {
double X0 = Sequence[SampleIndex,0];
double X1 = Sequence[SampleIndex,1];
double PhiH = 2.0 * Math.PI * X0;
// WARD
double ThetaH = Math.Atan( -r2 * Math.Log( 1.0 - X1 ) );
double CosThetaH = Math.Cos( ThetaH );
double SinThetaH = Math.Sin( ThetaH );
// // GGX
// double a = r2;
// double CosThetaH = Math.Sqrt( (1.0 - X1) / (1.0 + (a*a - 1.0) * X1 ) );
// double SinThetaH = Math.Sqrt( 1.0f - CosThetaH * CosThetaH );
double CosPhiH = Math.Cos( PhiH );
double SinPhiH = Math.Sin( PhiH );
Half.x = (float) (SinPhiH * SinThetaH);
Half.y = (float) CosThetaH;
Half.z = (float) (CosPhiH * SinThetaH);
Light = 2.0f * View.Dot( Half ) * Half - View; // Light is on the other size of the Half vector...
// Intuitively, we should have the same result if we sampled around the reflected view direction
// float3 ReflectedView = 2.0f * View.Dot( float3.UnitY ) * float3.UnitY - View;
// float3 OrthoY = ReflectedView.Cross( float3.UnitZ ).Normalized;
// float3 OrthoX = float3.UnitZ;
// Light = Half.x * OrthoX + Half.y * ReflectedView + Half.z * OrthoY;
//
// Half = (View + Light).Normalized;
if ( Light.y <= 0 )
continue;
double HoN = Half.y;
double HoN2 = HoN*HoN;
double HoV = Half.Dot( View );
// float HoV = Half.x * View.x + Half.y * View.y; // We know that Z=0 here...
double HoL = Half.Dot( Light );
double NoL = Light.y;
double NoV = View.y;
// Apply sampling weight for correct distribution
double SampleWeight = 2.0 / (1.0 + View.y / Light.y);
double BRDF = SampleWeight;
// Try with Unreal's GGX & Smith G term to see if we get the same thing
//
// // GGX NDF
// // double alpha = r2;
// // double alpha2 = alpha*alpha;
// // double D = alpha2 / (Math.PI * Math.Pow( HoN2*(alpha2 - 1.0) + 1.0, 2.0 ));
//
// // Smith masking/shadowing
// double k = (Roughness + 1)*(Roughness + 1) / 8.0;
// double Gl = NoL / (NoL * (1-k) + k);
// double Gv = NoV / (NoV * (1-k) + k);
// double G = Gl * Gv;
//
// //double BRDF = G / (4.0 * View.y);
// //double BRDF = G * HoV / (HoN * NoV);
// double BRDF = NoL * GSmith( Roughness, NoV, NoL ) * 4.0f * HoV / HoN;
// Compute Fresnel terms
double Schlick = 1.0 - HoV;
double Schlick5 = Schlick * Schlick;
Schlick5 *= Schlick5 * Schlick;
double FresnelA = 1.0f - Schlick5;
double FresnelB = Schlick5;
//FresnelA = FresnelB = 1.0;
SumA += FresnelA * BRDF;
SumB += FresnelB * BRDF;
}
// SumA *= 1.0 / (SAMPLES_COUNT * Math.PI * r2);
// SumB *= 1.0 / (SAMPLES_COUNT * Math.PI * r2);
SumA /= SAMPLES_COUNT;
SumB /= SAMPLES_COUNT;
// For few samples, the sum goes over 1 because we have poor solid angle sampling resolution...
// SumA = Math.Min( 1.0, SumA );
// SumB = Math.Min( 1.0, SumB );
Table0[X,Y] = SumA;
Table1[X,Y] = SumB;
}
}
// Write table 0
using ( System.IO.FileStream S = _TableFileName0.Create() )
using ( System.IO.BinaryWriter W = new System.IO.BinaryWriter( S ) )
for ( int Y=0; Y < _TableSize; Y++ ) {
for ( int X=0; X < _TableSize; X++ )
W.Write( Table0[X,Y] );
}
// Write table 1
using ( System.IO.FileStream S = _TableFileName1.Create() )
using ( System.IO.BinaryWriter W = new System.IO.BinaryWriter( S ) )
for ( int Y=0; Y < _TableSize; Y++ ) {
for ( int X=0; X < _TableSize; X++ )
W.Write( Table1[X,Y] );
}
}