## Saturday, 6 November 2010

### Jon's Teapot

After a lunch time design meeting (in the Ship) Luiz decided that the name Jon's coding blog was crap, so after much discussion it was decided to call this blog "Jon's Teapot". The main motivation for this is the fact that most of my demos include some form of Utah Teapot, and it is also an oblique reference to Russell's teapot as well.

The image above was created using Renderman Python using a simple Fractional Brownian motion (fBM) displacement shader

displacement fbmDisp
(
float Km=0.1;
float Layers=6;
output varying float Freq=1;
)
{

/* init the shader values */
vector NN=normalize(N);
float i;
float mag=0;
for(i=0; i<Layers; i+=1)
{
mag+=(float noise(Pt*Freq)-0.5)*2/Freq;
Freq*=2;
}
mag /=length(vtransform("object",NN));
P=P+mag*NN*Km;
N=calculatenormal(P);

}



The basic scene was created using a Python for Renderman script below, this script is a two stage script which generates shadow maps by moving the camera to the position of each of the lights and rendering a Z depth map which the special shadowspot shader uses to calculate the occlusion.

Once these maps are generated the final scene is rendered using these maps.

#!/usr/bin/python
# for bash we need to add the following to our .bashrc
# export PYTHONPATH=$PYTHONPATH:$RMANTREE/bin
import getpass
import time,random,math
# import the python renderman library
import prman

# Modified from Renderman Examples in The renderman Companion
# AimZ(): rotate the world so the direction vector points in
# positive z by rotating about the y axis, then x. The cosine
# of each rotation is given by components of the normalized
# direction vector.  Before the y rotation the direction vector
# might be in negative z, but not afterward.

def AimZ(ri,direction) :
if (direction[0]==0 and direction[1]==0 and direction[2]==0) :
return
#
# The initial rotation about the y axis is given by the projection of
# the direction vector onto the x,z plane: the x and z components
# of the direction.

xzlen = math.sqrt(direction[0]*direction[0]+direction[2]*direction[2])
if (xzlen == 0) :
if(direction[1] <0) :
yrot = 0
else :
yrot =180

#  yrot = (direction[1] < 0) ? 180 : 0
else :
yrot = 180*math.acos(direction[2]/xzlen)/math.pi;

# The second rotation, about the x axis, is given by the projection on
# the y,z plane of the y-rotated direction vector: the original y
# component, and the rotated x,z vector from above.

yzlen = math.sqrt(direction[1]*direction[1]+xzlen*xzlen)
xrot = 180*math.acos(xzlen/yzlen)/math.pi  # yzlen should never be 0

if (direction[1] > 0) :
ri.Rotate(xrot, 1.0, 0.0, 0.0)
else :
ri.Rotate(-xrot, 1.0, 0.0, 0.0)
#The last rotation declared gets performed first
if (direction[0] > 0) :
ri.Rotate(-yrot, 0.0, 1.0, 0.0)
else :
ri.Rotate(yrot, 0.0, 1.0, 0.0)

#
print "Rendering Shadow pass %s.z" %(Name)
ri.Begin("__render")
ri.Display(Name+".z", "zfile", "z")
ri.Clipping(1,10)
# Specify PAL resolution 1:1 pixel Aspect ratio
ri.Format(512,512,1)
# now set the projection to perspective
ri.Projection(ri.PERSPECTIVE,{ri.FOV:coneAngle*(360/math.pi)})
#now move to light position
# create a vector for the Spotlight to and from values

# to do this we subtract each of thelist elements using a lambda function
# this is the same as doing the code below I will leave it to you as to which you
#direction =[To[0]-From[0],To[1]-From[1],To[2]-From[2]]

direction = map(lambda x,y : x-y , To,From)
AimZ(ri,direction)
ri.Translate(-From[0],-From[1],-From[2])
# now draw the Scene
ri.WorldBegin()
SceneFunc(ri)
ri.WorldEnd()
ri.End()

def Scene(ri) :
random.seed(25)
ri.TransformBegin()
ri.AttributeBegin()
ri.Color([1,1,1])
ri.Translate( 0,-1.0,0)
ri.Rotate(-90,1,0,0)
ri.Rotate(36,0,0,1)
ri.Scale(0.4,0.4,0.4)
ri.Surface("plastic")
ri.Displacement("fbmDisp",{"float Km":[0.2]})

ri.Geometry("teapot")
ri.AttributeEnd()
ri.TransformEnd()
face=[-0.1,-1,-3, 0.1,-1,-3,-0.1,-1,3, 0.1,-1,3]
plank=-5.0

ri.AttributeBegin()
while (plank <=5.0) :
ri.TransformBegin()
ri.Color([random.uniform(0.35,0.4),random.uniform(0.1,0.025),0])
c0=[random.uniform(-10,10),random.uniform(-10,10),random.uniform(-10,10)]
c1=[random.uniform(-10,10),random.uniform(-10,10),random.uniform(-10,10)]
ri.Surface("wood",{"Ks":[0.1],"point c0":c0,"point c1":c1,"float grain":random.randint(2,20)})
ri.Translate(plank,0,0)
ri.Patch("bilinear",{'P':face})
ri.TransformEnd()
plank=plank+0.206
ri.AttributeEnd()

ri = prman.Ri() # create an instance of the RenderMan interface
ri.Option("rib", {"string asciistyle": "indented"})

SpotFrom=[2,4,3]
SpotTo=[0,0,0]
SpotName="Spot1"
coneAngle=0.4

Spot2From=[2,4,-3]
Spot2To=[0,0,0]
Spot2Name="Spot2"
coneAngle2=0.3

# this is the begining of the rib archive generation we can only
# make RI calls after this function else we get a core dump
ri.Begin(filename)
ri.Clipping(1,10)

# ArchiveRecord is used to add elements to the rib stream in this case comments
# note the function is overloaded so we can concatinate output
ri.ArchiveRecord(ri.COMMENT, 'File ' +filename)
ri.ArchiveRecord(ri.COMMENT, "Created by " + getpass.getuser())
ri.ArchiveRecord(ri.COMMENT, "Creation Date: " +time.ctime(time.time()))
ri.Declare(SpotName ,"string")

ri.Declare("Ambient" ,"string")
ri.Attribute("displacementbound",{ri.COORDINATESYSTEM:["world"],"uniform float sphere":[10.8]})

# now we add the display element using the usual elements
# FILENAME DISPLAY Type Output format
ri.Display("Teapot.png", "file", "rgb")
# Specify PAL resolution 1:1 pixel Aspect ratio
ri.Format(720,576,1)
# now set the projection to perspective
ri.Projection(ri.PERSPECTIVE,{ri.FOV:50})
ri.Translate(0,0,3)

# now we start our world
ri.WorldBegin()

ri.LightSource ("ambientlight",{ri.HANDLEID: "Ambient","intensity" :[0.05]})

"point from" : SpotFrom,
"point to" : SpotTo,
"float intensity" : [30],
"float coneangle" : coneAngle,
"float conedeltaangle" : [0.05]})

"point from" : Spot2From,
"point to" : Spot2To,
"float intensity" : [30],
"float coneangle" : coneAngle2,
"float conedeltaangle" : [0.05]})

ri.Illuminate(SpotName,1)
ri.Illuminate(Spot2Name,1)
Scene(ri)

ri.WorldEnd()

# and finally end the rib file
ri.End()