# In[1

]:=

Load a normalmap, convert to floats and remap it to the 1, 1 range, resize to make it more manageable... normalMap ImageApply Normalize 0.5 &, Image ImageResize Import "C:\\Users\\angelo.pesce\\Desktop\\4.jpg" , 128 , "Real"

Out[1]=

2

normalmapFit.nb

In[2]:=

We'll need a mean to operate on sub blocks of an image, let's write a small helper function that does these operations in parallell blockImageMap img_, fn_, size_ : Parallelize Map fn, ImagePartition img, size , 2 Let's compute the average normal for the blocks, we could have done it wiht another ImageResize but it's also a test of blockImageMap... blockSize 16; averagedNormals blockImageMap normalMap, Normalize Apply Plus, Flatten ImageData ,1 &, blockSize ; Image averagedNormals averagedNormals Flatten averagedNormals, 1 ;

Out[5]=

normalmapFit.nb

3

In[7]:=

We're not going to use a four dimensional Phong BRDF, we'll cheat reduce Phong to a simple lobe around the normal, like a "powered" diffuse Let me motivate this a bit: let's say that we have a cone Phong which takes a cone of normals and a light direction. The light around the normal reflection direction around the cone will be a cone whose size is equal to the normal cone no matter where the light direction is, thus this cone Phong will have a constant lobe which just rotates as the light direction changes, so we can ignore the light direction and just construct a lobe around the normal s instead, which we do here. cosFn n_, exp_, a_, b_ : Max n. Cos b Sin a , Sin b Sin a , Cos a , 0. ^ exp Let's see how that looks like when we start averaging different lobes around a bunch of normals aggregateCosFn Compile nlist, _Real, 2 , exp, a, b , Mean Map n Max n. Cos b Sin a , Sin b Sin a , Cos a , 0. ^ exp, nlist ; someTestNormals 0., 0., 1. , 0., 1., 0. , 0., 0.7071067811865475, 0.7071067811865475 ; someTestData Flatten Table a, b, aggregateCosFn someTestNormals, 5., a, b , a, 0., Pi, 0.01 , b, Pi 2, Pi 2, 0.01 , 1 ; AbsoluteTiming testPlot SphericalPlot3D aggregateCosFn someTestNormals, 5., a, b , a, Pi 2., Pi 2. , b, 0., Pi , PlotRange Full
Out[10]=

0.6400640, Null
CompiledFunction::cfsa : Argument a at position 3 should be a machine size real number.

4

normalmapFit.nb

Out[11]=

normalmapFit.nb

5

In[12]:=

Ok, now let's say that we are fine with a normal that is the average of the normals in the budle, we won't cosider that as a parameter... we want to fit a single lobe that fits well around the ones in the bundle... testNormal Normalize Plus someTestNormals cosFn Compile n,_Real,1 ,exp,a,b , Max n. Cos b Sin a ,Sin b Sin a ,Cos a ,0. ^exp testFit FindFit someTestData, mul cosFn testNormal, N exp , a, b , mul, exp , a, b testFitPlot SphericalPlot3D mul cosFn testNormal, exp, a, b . testFit, a, Pi 2., Pi 2. , b, 0., Pi , PlotRange Full ; Show testPlot, testFitPlot 0., 0.707107, 0.707107 mul 0.437587, exp 1.3201

Out[12]= Out[13]=

Out[15]=

In[16]:=

Same as before, but let's visualize the actual aggregated lobes for all the blocks in our image. baseExponent 15. ; We assume that the "original" Phong exponent is a constant, no gloss map. That also motivates the fact that we don't fit the normals but just use the average later on All these graphs take quite some time to compute... aggregateGraphs blockImageMap normalMap, SphericalPlot3D aggregateCosFn Flatten ImageData , 1 , baseExponent, a, b , a, Pi 2., Pi 2. , b, 0., Pi , PlotRange Full & , blockSize ; aggregateGraphs Grid

6

normalmapFit.nb

Out[17]=

normalmapFit.nb

7

Out[17]=

8

normalmapFit.nb

In[18]:=

Now let's do it "for real" and not only on our test case... This will sample the aggretageCosFn for all the blocks in the image bigTableStep 0.1; bigTable Flatten blockImageMap normalMap, Flatten Table a, b, aggregateCosFn Flatten ImageData , 1 , baseExponent, a, b a, 0., Pi, bigTableStep , b, Pi 2, Pi 2, bigTableStep , 1 & , blockSize ,1 ;

,

normalmapFit.nb

9

In[20]:=

On Assert Assert Length averagedNormals Length bigTable And this will do all the fits... mulExpFit Parallelize MapIndexed FindFit 1, mul cosFn averagedNormals mul, exp , a, b &, bigTable mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul mul 0.618805, exp 8.89502 , mul 0.532327, exp 7.89826 , mul 0.340919, exp 4.43919 , mul 0.239627, exp 2.56283 , mul 0.641131, exp 9.08297 , mul 0.551036, exp 7.94867 , mul 0.244188, exp 2.90802 , mul 0.221055, exp 2.5569 , mul 0.52463, exp 8.39447 , mul 0.432999, exp 6.17545 , mul 0.275543, exp 3.27375 , mul 0.222615, exp 2.83855 , mul 0.297381, exp 3.65126 , mul 0.380706, exp 5.5091 , mul 0.294879, exp 3.78966 , mul 0.213934, exp 2.2836 , mul 0.577994, exp 8.49755 , mul 0.367468, exp 4.02667 , mul 0.383339, exp 4.77278 , mul 0.428866, exp 6.55542 , mul 0.805292, exp 12.181 , mul 0.837642, exp 12.5788 , mul 0.857893, exp 12.9482 , mul 0.752328, exp 11.1784 , mul 0.792982, exp 11.9456 , mul 0.788424, exp 11.5308 , mul 0.807078, exp 11.8358 , mul 0.821676, exp 11.8566 , mul 0.907429, exp 13.9459 , mul 0.880625, exp 13.787 , mul 0.880505, exp 13.8098 , mul 0.867259, exp 13.9305 , mul 0.598644, exp 0.477471, exp 0.276318, exp 0.203344, exp 0.654242, exp 0.507081, exp 0.303832, exp 0.213842, exp 0.566446, exp 0.635317, exp 0.37205, exp 0.205707, exp 0.453345, exp 0.474217, exp 0.269861, exp 0.236978, exp 0.336856, exp 0.33009, exp 0.407135, exp 0.430458, exp 0.773268, exp 0.864366, exp 0.818741, exp 0.819131, exp 0.802381, exp 0.808138, exp 0.799556, exp 0.813524, exp 0.879497, exp 0.872389, exp 0.834215, exp 0.895466, exp

First

2

, N exp , a, b ,

Out[22]=

8.51412 , 6.5503 , 3.27376 , 2.24956 , 9.53887 , 6.8086 , 4.30447 , 2.6156 , 8.5677 , 9.14319 , 5.46512 , 2.32635 , 6.01124 , 8.23652 , 3.16145 , 2.93404 , 4.13611 , 3.96973 , 5.56801 , 5.99862 , 11.6552 , 13.018 , 12.3182 , 12.5493 , 12.0445 , 11.5349 , 11.4176 , 12.0381 , 13.5577 , 13.6939 , 13.3251 , 14.0431

In[23]:=

fitGraphs Parallelize MapIndexed SphericalPlot3D mul cosFn averagedNormals First 2 , exp, a, b . 1, a, Pi 2., Pi 2. , b, 0., Pi , PlotRange Full, MeshStyle None &, mulExpFit Let's still display graphs as a grid, but as fitGraphs is a list we have to convert it to a table first... With sideLength Sqrt Length fitGraphs , Table Show fitGraphs j i sideLength , aggregateGraphs i 1, j , i, 0, sideLength 1 , j, sideLength Grid

;

10

normalmapFit.nb

Out[24]=

normalmapFit.nb

11

Out[24]=

12

normalmapFit.nb

In[37]:=

normalLengths blockImageMap normalMap, Norm Apply Plus, Flatten ImageData ,1 Image normalLengths normalLengths Flatten normalLengths, 1 ;

blockSize ^ 2 &, blockSize ;

Out[38]=

normalmapFit.nb

13

Let's follow some of the ideas here http: developer.nvidia.com content mipmapping normal maps is there a relation between averaged vector lengths and the phong exponent? We interleave the elements of normalLengths with the exponents from the fit solutions, then group everything two elements at a time, then plot... lengthAndExponent Partition Riffle normalLengths, Map sol baseExponent exp . sol , mulExpFit ListPlot lengthAndExponent

,2 ;

6

5

4
Out[53]=

3

2

1

0.86
In[54]:= Out[54]=

0.88

0.90

0.92

0.94

0.96

0.98

linearLengthExponentFit 34.3097 33.9931 x

Fit lengthAndExponent, 1, x , x