MSBRDF for LTC Area Lights

We try and load the “roughness only” LTC tables generated by the LTC generator with a light-dependent MSBRDF term:

	L((Subscript[ω, o]))=(∫_Ω)L((Subscript[ω, i]))  (Subscript[f, ms])( (Subscript[ω, o]),(Subscript[ω, i]),α) ((Subscript[ω, i]) · n) d(Subscript[ω, i])
	L((Subscript[ω, o]))=(∫_Ω)L((Subscript[ω, i]))  (((1-E((Subscript[ω, o]),α))(1-E((Subscript[ω, i]),α)))/(π-(Subscript[E, avg])(α))) ((Subscript[ω, i]) · n) d(Subscript[ω, i])
	L((Subscript[ω, o]))=((1-E((Subscript[ω, o]),α))/(π-(Subscript[E, avg])(α)))(∫_Ω)L((Subscript[ω, i]))  (1-E((Subscript[ω, i]),α) ((Subscript[ω, i]) · n) d(Subscript[ω, i])
	L((Subscript[ω, o]))=(Subscript[L, i]) ·((1-E((Subscript[ω, o]),α))/(π-(Subscript[E, avg])(α)))(∫_Ω) (1-E((Subscript[ω, i]),α) ((Subscript[ω, i]) · n) d(Subscript[ω, i])
	L((Subscript[ω, o]))≃(OverTilde[L])((Subscript[ω, o]))=(Subscript[L, i]) ·((1-E((Subscript[ω, o]),α))/(π-(Subscript[E, avg])(α)))(∫_Ω) D((Subscript[ω, i]),α) d(Subscript[ω, i])
	(OverTilde[L])((Subscript[ω, o]))=(Subscript[L, i]) ·((1-E((Subscript[ω, o]),α))/(π-(Subscript[E, avg])(α)))(∫_(Subscript[Ω, 0])) (Subscript[D, 0])((Subscript[ω, 0]),α) d(Subscript[ω, 0])

Here, (Subscript[D, 0])((Subscript[ω, 0]),α) represents the clamped cosine distribution and (Subscript[Ω, 0]) the transformed solid angle.
Since the MSBRDF is very low-frequency and isotropic due to the fact it only depends on the light direction and roughne, the resulting transform matrix (Subscript[Ω, 0]) = (M^(-1)) Ω is very sparse and contains only diagonal terms:

	(M^(-1)) =(({{1, 0, 0}, {0, 1, 0}, {0, 0, (Subscript[m, 33])(α)}}))

The idea is to read back the (Subscript[m, 33]) and magnitude values for each roughness and either export it to a CSV table or find a simple fitting...

LTC Tables Loading

The roughness-only LTC tables have been exported as raw double values vectors { roughness, magnitude, m00, m20, m02, m22 }

LoadLTC=Function[{filename},
s=OpenRead[filename,BinaryFormat->True];
count=BinaryRead[s,"Integer32"];

temp=Table[BinaryRead[s,{ 
 "Real64"
, "Real64"
, "Real64", "Real64", "Real64", "Real64"}
],{i,1,count}];

Close[s];
l=Length[temp];
temp
];

ltcGGX=LoadLTC["D:\\Workspaces\\GodComplex\\Tools\\LTCTableGenerator\\MS_GGX.double"];
ltcOren=LoadLTC["D:\\Workspaces\\GodComplex\\Tools\\LTCTableGenerator\\MS_OrenNayar.double"];

Remap into nice tables

count=Length[ltcGGX];
(*taGGXmag=Table[{ ((i-1)/(count-1)), ltcGGX[[i]][[2]] }, {i,1,count}];
taGGXm33=Table[{ ((i-1)/(count-1)), ltcGGX[[i]][[6]] }, {i,1,count}];*)
taGGXm33=Table[{ ltcGGX[[i]][[1]], ltcGGX[[i]][[6]] }, {i,1,count}];
taGGXmag=Table[{ ltcGGX[[i]][[1]], ltcGGX[[i]][[2]] }, {i,1,count}];
taGGX=Table[{ ltcGGX[[i]][[1]] , ltcGGX[[i]][[6]], ltcGGX[[i]][[2]]}, {i,1,count}];
ListLinePlot[taGGXmag]
ListLinePlot[taGGXm33]

count=Length[ltcOren];
(*taOrenmag=Table[{ ((i-1)/(count-1)), ltcOren[[i]][[2]] }, {i,1,count}];
taOrenm33=Table[{ ((i-1)/(count-1)), ltcOren[[i]][[6]] }, {i,1,count}];*)
taOrenm33=Table[{ ltcOren[[i]][[1]], ltcOren[[i]][[6]] }, {i,1,count}];
taOrenmag=Table[{ ltcOren[[i]][[1]], ltcOren[[i]][[2]] }, {i,1,count}];
taOren=Table[{ ltcOren[[i]][[1]], ltcOren[[i]][[6]], ltcOren[[i]][[2]] }, {i,1,count}];
ListLinePlot[taOrenmag]
ListLinePlot[taOrenm33]

Exporting Nice Tables as CSV for the blog

SetDirectory["D:\\Workspaces\\GodComplex\\Tests\\TestMSBRDF\\Tables"]; 
Export["MSLTC_GGX_64.csv", taGGX,"CSV"]
Export["MSLTC_Orennayar_64.csv", taOren,"CSV"]

Attempt Fitting

Remap into “perceptual roughness” tables

Here, the roughness varies linearly and is called the “perceptual roughness”  α'=(Sqrt[α])

count=Length[ltcGGX];
taGGXm33=Table[{ ((i-1)/(count-1)), ltcGGX[[i]][[6]] }, {i,1,count}];
taGGXmag=Table[{ ((i-1)/(count-1)), ltcGGX[[i]][[2]] }, {i,1,count}];
taOrenm33=Table[{ ((i-1)/(count-1)), ltcOren[[i]][[6]] }, {i,1,count}];
taOrenmag=Table[{ ((i-1)/(count-1)), ltcOren[[i]][[2]] }, {i,1,count}];

taGGXm33=Table[{ (Sqrt[ltcGGX[[i]][[1]]]), ltcGGX[[i]][[6]] }, {i,1,count}];
taGGXmag=Table[{ (Sqrt[ltcGGX[[i]][[1]]]), ltcGGX[[i]][[2]] }, {i,1,count}];
taOrenm33=Table[{ (Sqrt[ltcOren[[i]][[1]]]), ltcOren[[i]][[6]] }, {i,1,count}];
taOrenmag=Table[{ (Sqrt[ltcOren[[i]][[1]]]), ltcOren[[i]][[2]] }, {i,1,count}];

ListLinePlot[taGGXmag]
ListLinePlot[taGGXm33,PlotRange->Full]
ListLinePlot[taOrenmag]
ListLinePlot[taOrenm33]

fittingMethod="Automatic";

model=0*a+b*x+c*(x^2)+d*(x^3)+e*(x^4);
data=taGGXmag;
fit=FindFit[data,model,{{a,1},{b,1},{c,1},{d,0},e},x,Method->fittingMethod]
fGGXmag=Function[{x},Evaluate[model/.fit]];
Show[
ListPlot[data,PlotRange->Full,PlotLabel->"GGX",AxesLabel->{"(Sqrt[α])","magnitude"}]
,Plot[fGGXmag[x],{x,0,1},PlotStyle->Red,PlotRange->Full]
]

a->0.`,b->0.09791431592022058`,c->-1.4272852958071647`,d->7.768226052923069`,e->-4.475643330561517`}

model=a+b*x+c*(x^2)+d*(x^3)+0*e*(x^4)+0*f*(x^5);
data=Take[taOrenmag,-50];
fit=FindFit[data,{model,{c>0}},{{a,1},{b,1},{c,1},{d,0},e,f},x,Method->fittingMethod]
fOrenmag=Function[{x},Evaluate[model/.fit]]
Show[
ListPlot[data,PlotRange->Full,PlotLabel->"Oren-Nayar",AxesLabel->{"(Sqrt[α])","magnitude"}]
,Plot[fOrenmag[x],{x,0,1},PlotStyle->Red,PlotRange->Full]
]

{a->0.2559021427001363`,b->-2.7944531451217687`,c->8.298513748611509`,d->-4.662218902663712`,e->0.001`,f->0.001`}

model=((a+0*b*x+1*c*(x^2))/(d+0*e*x+f*(x^2)));
data=Take[taGGXm33,-59];
fit=FindFit[data,model,{{a,1},{b,1},{c,1},{d,0},e,f},x,Method->fittingMethod]
fGGXm33=Function[{x},Evaluate[model/.fit]];
Show[
ListPlot[data,PlotRange->{{0,1},{0,20}},PlotLabel->"GGX",AxesLabel->{"(Sqrt[α])","(Subscript[m, 33])"}]
,Plot[fGGXm33[x],{x,0,1},PlotStyle->Red,PlotRange->Full]
]
taGGXm33[[1]]

{a->21.342911007109528`,b->1.`,c->93.01620773161568`,d->0.4814723396401988`,e->1.`,f->143.39682026031764`}

model=((a+b*x+c*(x^2))/(d+e*x+f*(x^2)));
data=taOrenm33;
fit=FindFit[data,model,{{a,1},{b,1},{c,1},{d,0},e,f},x,Method->fittingMethod]
fOrenm33=Function[{x},Evaluate[model/.fit]]
Show[
ListPlot[data,PlotRange->{{0,1},{0,1}},PlotLabel->"Oren-Nayar",AxesLabel->{"(Sqrt[α])","(Subscript[m, 33])"}]
,Plot[fOrenm33[x],{x,0,1},PlotStyle->Red]
]

Function[{x},((0.005048402287040277` -0.014928238738260345` x+0.027930492034822143` (x^2))/(0.008420878564907999` -0.022349996757079705` x+0.034343673853208634` (x^2)))]