## October 31, 2010

### Python Implementation of Spherical Harmonics Stratified Sampling

Filed under: Uncategorized — admin @ 7:32 pm

Its Sunday afternoon and its time to write some code, this is pretty much lifted verbatim from Robin Green’s 2003 Paper: Spherical Harmonic Lighting the Gritty Details

So for the next trick is to put this as a large table into a shader so I can bake out a set of 9 cooefficients (4 bands) for Shadowed Diffuse Transfer, these I will store per sample in a point cloud to be looked up during a shading stage.

Then after that I can implement the Image Based Lights, then I can do really quick Image Based lighting using Spherical Harmonics

Lots of fun!

I made a big mess of this but luckily Markus Kransler was able to fix it up:

Here is the amended code:

```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 #!/usr/bin/env python   class SHSample(): sph=(0.0,0.0) vec=(0.0,0.0,0.0) coeff={} pass   def P(l,m,x): import math #Associated Legendre Polynomial P(l,m,x) at x pmm = 1.0 if m > 0: somx2=math.sqrt(1.0-(x*x)) fact = 1.0 for i in xrange(1,m+1,1): pmm *= (-fact)*somx2 fact += 2.0 if l == m: return pmm   pmmp1 = x * ((2.0*m)+1.0)*pmm   if l == m+1: return pmmp1   plm = 0.0   for ll in xrange(m+2,l+1,1): plm = ((2.0*ll - 1.0) * x * pmmp1 - (ll + m - 1.0) * pmm) / (ll - m); pmm = pmmp1 pmmp1 = plm   return plm   def K(l,m): import math temp = float((((2.0*l)+1.0)*math.factorial(l-m))/(4.0*math.pi*math.factorial(l+m))) return math.sqrt(temp)   def SH(l,m,theta,phi): import math sqrt2 = math.sqrt(2.0) if m==0: return K(l,0)*P(l,0,math.cos(theta)) elif m > 0: return sqrt2*K(l,m)*math.cos(m*phi)*P(l,m,math.cos(theta)) else: return sqrt2*K(l,-m)*math.sin(-m*phi)*P(l,-m,math.cos(theta))     def setupSamples(sqrtNumSamples=64,numBands=4): import random,math counter = 0 oneOverN = 1.0/float(sqrtNumSamples) samples = [SHSample() for i in range(sqrtNumSamples*sqrtNumSamples)]   for i in range(sqrtNumSamples): for j in range(sqrtNumSamples): x = (i+ random.random())*oneOverN theta = 2.0*math.acos(math.sqrt(1-x))   y = (j+ random.random())*oneOverN phi = 2.0*math.pi*y   samples[counter].sph=(theta,phi)   vec = (math.sin(theta)*math.cos(phi),\ math.sin(theta)*math.sin(phi),\ math.cos(theta)) samples[counter].vec = vec   tmpDict = {} for l in range(numBands): for m in xrange(-l,l+1,1): index = l*(l+1)+m sh= SH(l,m,theta,phi) tmpDict[index]=sh samples[counter].coeff=tmpDict counter +=1 return samples     for i in setupSamples(): print i.coeff```

## October 19, 2010

### Some papers to read and implement

Filed under: Uncategorized — admin @ 12:51 pm

## October 14, 2010

### Point Cloud in Nuke using “PositionToPoints”, 3delight Rendman Shader Language and cortex-vfx

Filed under: Uncategorized — Tags: , , , , — admin @ 1:51 pm

Here is the end result:

First things first I need a model to work with

#### Model

```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 surface bakeColourAndPosition( uniform float diffuseAmount = 1; varying color surfaceColour = color(0.18,0.18,0.18); varying color opacityColour = color(0.99,0.99,0.99); uniform string bakeFile="/tmp/out.bake"; ) { varying normal Nn = normalize(N); Ci = diffuse(Nn)*surfaceColour*diffuseAmount*Cs; Oi = opacityColour*Os; varying point Pworld = transform("current","world",P); bake(concat(bakeFile,"Position"),s,t,Pworld); bake(concat(bakeFile,"Colour"),s,t,Ci); Ci *= Oi; }```

This shader will produce two text “bakefile” files in the /tmp directory

Note: The texture coordinates are ignored, only using the 3rd,4th and 5th values

1. one named out.bakeColour with colour information
2. one named out.bakePosition with position information

#### Output Bakefiles

Due to the SIMD nature of shaders the line count of each of the bake files is the same so they should contain line for line the same information about the Position and Colour Respectively.

They are in ASCII format so they are easy enough to parse with Python.

here is an example of thier content:

```1 2 3 4 5 6 7 8 9 out.bakePositionmh 3 0 1 -0.07415867 0.17987273 -0.05079475 0 1 -0.073529155 0.1800126 -0.051191031 0 1 -0.07289961 0.18015243 -0.051587344 0 1 -0.072270096 0.18029229 -0.051983685 0 1 -0.07164058 0.18043211 -0.052379965 0 1 -0.07101102 0.18057197 -0.052776248 0 1 -0.07038155 0.1807118 -0.053172619```

#### Creating Position and Colour EXR files using cortex-vfx

```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #!/usr/bin/env python   import sys,os,math   IECoreInstallPath = "/usr/lib/python2.6/site-packages"   if IECoreInstallPath not in sys.path: sys.path.append(IECoreInstallPath)   from IECore import *   bakeFolder = "/tmp"   colorBakeFileLocation = os.path.sep.join([bakeFolder,"out.bakeColour"])   positionBakeFileLocation = os.path.sep.join([bakeFolder,"out.bakePosition"])         def parseBakeFile(bakeFileLocation): data = [] counter = 0 bakeFile = open(bakeFileLocation,"r") for line in bakeFile.readlines(): counter +=1 if counter > 2: stuff = line.strip().split(" ") if len(stuff) > 2: data.append((float(stuff[2]),float(stuff[3]),float(stuff[4]))) print "Completed parsing %d lines of file %s" % (len(data),bakeFileLocation) bakeFile.close() return data   colourData = parseBakeFile(colorBakeFileLocation)   positionData = parseBakeFile(positionBakeFileLocation)   if len(colourData) == len(positionData): squareSize = int(math.sqrt(len(positionData))) +1 print "Square Size: %d, Excess Pixels : %d" % (squareSize,squareSize*squareSize - len(colourData)) width = squareSize height = squareSize x = FloatVectorData( width * height ) y = FloatVectorData( width * height ) z = FloatVectorData( width * height )   r = FloatVectorData( width * height ) g = FloatVectorData( width * height ) b = FloatVectorData( width * height )   for i in range(len(colourData)): r[i]=colourData[i][0] g[i]=colourData[i][1] b[i]=colourData[i][2] x[i]=positionData[i][0] y[i]=positionData[i][1] z[i]=positionData[i][2]   boxColour = Box2i( V2i( 0, 0 ), V2i( width-1, height-1 ) ) boxPosition = Box2i( V2i( 0, 0 ), V2i( width-1, height-1 ) )   imageColour = ImagePrimitive( boxColour, boxColour ) imagePosition = ImagePrimitive( boxPosition, boxPosition )   imagePosition["R"]= PrimitiveVariable( PrimitiveVariable.Interpolation.Vertex, x) imagePosition["G"]= PrimitiveVariable( PrimitiveVariable.Interpolation.Vertex, y) imagePosition["B"]= PrimitiveVariable( PrimitiveVariable.Interpolation.Vertex, z)   imageColour["R"]= PrimitiveVariable( PrimitiveVariable.Interpolation.Vertex, r) imageColour["G"]= PrimitiveVariable( PrimitiveVariable.Interpolation.Vertex, g) imageColour["B"]= PrimitiveVariable( PrimitiveVariable.Interpolation.Vertex, b)   writePosition = Writer.create( imagePosition, "/tmp/outPosition.exr" ) writeColour = Writer.create( imageColour, "/tmp/outColour.exr" ) writePosition.write() writeColour.write()```

#### Using Nuke to read the Position and Colour Data

File > Script Command > PositionToPoints

If you werent able to create your own pair of EXRs you can download the pair here in .tar.bz2 format

So then you just need to connect them up to the input nodes for the PositionToPoints 3d node as follows: