Showing posts with label Renderman. Show all posts
Showing posts with label Renderman. Show all posts

Saturday, 13 November 2010

Making Things OpenGL 3/4.x compatible (Initial Design Considerations)

Have been re-reading the OpenGL quick reference card again and looking at all the elements that have been deprecated. It would seem that most of the light / material elements have been removed from the "core profile"

As a handy reminder the quick reference card highlights the elements being removed in blue and for this initial design post I'm going to concentrate on the Light / Material / Colour properties here


As you can see all of the Light / Colour elements have been deprecated as well as the materials, this means that we will no longer have any access to these elements in the shader and structures such as gl_LightSource and the colour elements such as gl_FrontMaterial.

So we need to be able to pass both light and colour information to our Fragment shader and then use this in the calculations. This sounds a bit similar to how renderman works so I decided to design the initial system around renderman's lighting model.

In renderman we have a single colour for basic plastic surfaces and access to a global variable called Cs which describes the surface colour (either set by the Color [1,0,0] command or calculated in the shader itself). A full list of these variables are shown in the table below (from the RPS_15 user guide)

The basic renderman shader execution environment is as follows

Whilst we will not have all these variables available in the GLSL shader we can imitate most of it, and make our lights behave in a similar way to the renderman ones. 

The scans below illustrate my initial sketches of a Light class system to add to the new version of ngl:: 


Writing this up more formally I've have this as the initial design of the Light classes


This is going on the back burner for a while as I have loads of other elements to sort out but at least I can bear this in mind whilst re-writing some other parts of ngl::.





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;
point Pt=transform("shader",P);
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)



def ShadowPass(ri,Name,From,To,coneAngle,SceneFunc) :
 #
 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
 # find more readable
 #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.MakeShadow(Name+".z",Name+".shad")
 ri.End()
 print " Done MakeShadow %s.shad" %(Name)


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
ShadowPass(ri,SpotName,SpotFrom,SpotTo,coneAngle,Scene)

Spot2From=[2,4,-3]
Spot2To=[0,0,0]
Spot2Name="Spot2"
coneAngle2=0.3
ShadowPass(ri,Spot2Name,Spot2From,Spot2To,coneAngle2,Scene)


filename = "__render" #ShadowSpot.rib"
# 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.ShadingRate(0.1)
ri.Translate(0,0,3)

# now we start our world
ri.WorldBegin()



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


ri.LightSource( "shadowspot", {ri.HANDLEID:SpotName,
    "point from" : SpotFrom,
             "point to" : SpotTo,
             "float intensity" : [30],
             "string shadowname" :SpotName+".shad",
             "float coneangle" : coneAngle,
             "float conedeltaangle" : [0.05]})


ri.LightSource( "shadowspot", {ri.HANDLEID:Spot2Name,
    "point from" : Spot2From,
             "point to" : Spot2To,
             "float intensity" : [30],
             "string shadowname" :Spot2Name+".shad",
             "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()