void ComputeBRDFIntegral( System.IO.FileInfo _TableFileName0, System.IO.FileInfo _TableFileName1, int _TableSize ) {
const int SAMPLES_COUNT_THETA = 128;
const int SAMPLES_COUNT_PHI = 2*SAMPLES_COUNT_THETA;
const double dTheta = 0.5 * Math.PI / SAMPLES_COUNT_THETA;
const double dPhi = 2.0 * Math.PI / SAMPLES_COUNT_PHI;
double[,] Table0 = new double[_TableSize,_TableSize];
double[,] Table1 = new double[_TableSize,_TableSize];
// float Theta = 0.5 * _UV.x * PI;
// float3 ToLight = float3( sin( Theta ), 0, cos( Theta ) );
// float3 ToView = float3( -sin( Theta ), 0, cos( Theta ) );
//
// float Albedo = 0.0;
// const int THETA_COUNT = 64; // * $alwaysOne; // warning X4008: floating point division by zero
// const float dTheta = HALFPI / THETA_COUNT;
// const float dPhi = TWOPI / THETA_COUNT;
// for ( int i=0; i < THETA_COUNT; i++ )
// {
// Theta = HALFPI * (0.5 + i) / THETA_COUNT;
// for ( int j=0; j < THETA_COUNT; j++ )
// {
// float Phi = PI * j / THETA_COUNT;
//
// ToView = float3( sin( Theta ) * cos( Phi ), sin( Theta ) * sin( Phi ), cos( Theta ) );
//
// float3 Half = normalize( ToLight + ToView );
//
// // "True" and expensive evaluation of the Ward BRDF
// float alpha = Roughness;
//
// float CosDelta = Half.z; // dot( Half, _wsNormal );
// float delta = acos( CosDelta );
// float CosThetaL = ToLight.z;
// float SinThetaL = sqrt( 1.0 - CosThetaL*CosThetaL );
// float PhiL = atan2( ToLight.y, ToLight.x );
// float CosThetaV = ToView.z;
// float SinThetaV = sqrt( 1.0 - CosThetaV*CosThetaV );
// float PhiV = atan2( ToView.y, ToView.x );
//
// float BRDF = 1.0 / square(alpha) * exp( -square( tan( delta ) / alpha ) ) * 2.0 * (1.0 + CosThetaL*CosThetaV + SinThetaL*SinThetaV*cos( PhiV - PhiL )) / pow4( CosThetaL + CosThetaV );
//
// Albedo += BRDF * cos( Theta ) * sin( Theta ) * dTheta * dPhi;
// }
// }
//
// Albedo *= INVPI; // Since we forgot that in the main loop
float3 View = new float3();
float3 Light = new float3();
for ( int Y=0; Y < _TableSize; Y++ ) {
float Roughness = Math.Max( 0.01f, (float) Y / (_TableSize-1) );
float r2 = Roughness*Roughness;
for ( int X=0; X < _TableSize; X++ ) {
// double CosThetaV = (double) (1+X) / _TableSize;
// double SinThetaV = Math.Sqrt( 1.0 - CosThetaV*CosThetaV );
double ThetaV = 0.5 * Math.PI * X / _TableSize;
double CosThetaV = Math.Cos( ThetaV );
double SinThetaV = Math.Sin( ThetaV );
View.x = (float) SinThetaV;
View.y = (float) CosThetaV;
View.z = 0.0f;
double SumA = 0.0;
double SumB = 0.0;
for ( int Theta=0; Theta < SAMPLES_COUNT_THETA; Theta++ ) {
double fTheta = 0.5 * Math.PI * (0.5 + Theta) / SAMPLES_COUNT_THETA;
double CosThetaL = Math.Cos( fTheta );
double SinThetaL = Math.Sin( fTheta );
// Compute solid angle
double SolidAngle = SinThetaL * dTheta * dPhi;
double ProjectedSolidAngle = CosThetaL * SolidAngle; // (N.L) sin(Theta).dTheta.dPhi
for ( int Phi=0; Phi < SAMPLES_COUNT_PHI; Phi++ ) {
double fPhi = Math.PI * Phi / SAMPLES_COUNT_PHI;
double CosPhiLight = Math.Cos( fPhi );
double SinPhiLight = Math.Sin( fPhi );
Light.x = (float) (SinPhiLight * SinThetaL);
Light.y = (float) CosThetaL;
Light.z = (float) (CosPhiLight * SinThetaL);
// // Transform into "reflected-view-centered space"
// Light = Light.x * OrthoX + Light.y * ReflectedView + Light.z * OrthoZ;
// if ( Light.y < 0.0f )
// continue;
// ProjectedSolidAngle = dPhi * dTheta * Light.y * Math.Sqrt( 1.0 - Light.y*Light.y );
float3 H_unorm = View + Light;
float3 H = H_unorm.Normalized;
// // Compute normal distribution function
// float H_unorm_dot_N = H_unorm.y;
// float H_unorm_dot_N2 = H_unorm_dot_N * H_unorm_dot_N;
// float H_unorm_dot_N4 = H_unorm_dot_N2 * H_unorm_dot_N2;
//
// double BRDF = Math.Exp( -(H_unorm.x*H_unorm.x + H_unorm.z*H_unorm.z) / (r2 * H_unorm_dot_N2) ) * H_unorm.Dot( H_unorm ) / H_unorm_dot_N4;
// Expensive Ward
double PhiL = Math.Atan2( Light.z, Light.x );
double PhiV = Math.Atan2( View.z, View.x );
double tanDelta = Math.Tan( Math.Acos( H.y ) );
double BRDF = Math.Exp( -tanDelta*tanDelta / r2 ) * 2.0 * (1.0 + CosThetaL*CosThetaV + SinThetaL*SinThetaV*Math.Cos( PhiV - PhiL )) / Math.Pow( CosThetaL + CosThetaV, 4.0 );
// Try with Unreal's GGX & Smith G term to see if we get the same thing
// double alpha = r2;
// double alpha2 = alpha*alpha;
// double D = alpha2 / (Math.PI * Math.Pow( HoN2*(alpha2 - 1.0) + 1.0, 2.0 ));
//
// double k = (Roughness + 1)*(Roughness + 1) / 8.0;
//
// HoN = H_norm.y;
// double HoL = H_norm.Dot( Light );
// double HoV = H_norm.Dot( View );
// double Gl = HoL / (HoL * (1-k) + k);
// double Gv = HoV / (HoV * (1-k) + k);
// double G = Gl * Gv;
//
// double BRDF = D * G / (4.0 * Light.y * View.y);
// //double BRDF = D * G * H_norm.Dot( View ) / Math.Max( 1e-6, HoN * View.y);
// Expensive Ward with angles
// double PhiL = Math.Atan2( Light.z, Light.x );
// double PhiV = 0.0;//Math.Atan2( View.z, View.x );
// double tanDelta = Math.Tan( Math.Acos( H_norm.y ) );
// double BRDF = Math.Exp( -tanDelta*tanDelta / r2 ) * 2.0 * (1.0 + CosThetaLight*CosThetaView + SinThetaLight*SinThetaView*Math.Cos( PhiV - PhiL )) / Math.Pow( CosThetaLight + CosThetaView, 4.0 );
//
// SumA += BRDF * ProjectedSolidAngle;
// SumB += BRDF * ProjectedSolidAngle;
// Compute Fresnel terms
double VoH = View.x * H.x + View.y * H.y;
double Schlick = 1.0 - VoH;
double Schlick5 = Schlick * Schlick;
Schlick5 *= Schlick5 * Schlick;
double FresnelA = 1.0 - Schlick5;
double FresnelB = Schlick5;
//FresnelA = FresnelB = 1.0;
SumA += FresnelA * BRDF * ProjectedSolidAngle;
SumB += FresnelB * BRDF * ProjectedSolidAngle;
}
}
SumA /= Math.PI * r2;
SumB /= Math.PI * r2;
// // 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] );
}
}