Lonely Planet
Creating 3D Worlds with Python Procedural in Pixar RenderMan

As a 3D VFX artist, I’m always looking for new ways to create stunning visuals. Recently, I’ve been experimenting with Python Procedural in Pixar’s RenderMan system. This powerful tool allows me to generate geometry at render-time, giving me greater control over the final product. In this article, I’ll be sharing my experience using Python Procedural to create a 3D world inspired by “The Little Prince.”

Generating Geometry with Python Procedural

The Python Procedural tool in Pixar’s RenderMan system is a game changer for creating custom geometry. It allows me to generate geometry on the fly, using Python scripts that run during the rendering process. This gives me more control over the final output, as I can adjust parameters in real-time to create the desired effect.

For this project, I used a flower creator that I designed to generate flowers that I then applied to my 3D world. I also used Python scripts to generate spheres of varying sizes, colors, and textures. The result is a visually stunning world that looks like it came straight out of a storybook.

Creating a World Inspired by “The Little Prince”

“The Little Prince” is one of my favorite books, and I wanted to create a 3D world that captured the whimsical and magical feeling of the story. I used Python Procedural to create a variety of objects, from flowers and trees to planets and stars. By adjusting parameters such as color, size, and texture, I was able to create a visually cohesive world that captured the essence of the book.

Python Procedural in Pixar’s RenderMan system is an incredibly powerful tool for 3D VFX artists and digital composition experts. It allows us to generate custom geometry in real-time, giving us greater control over the final output. By using Python scripts to adjust parameters such as color, size, and texture, we can create stunning visuals that bring our projects to life. I hope this article has inspired you to explore the world of Python Procedural in your own work.

Generates Spheres

				
					import prman
import sys
import string
from random import uniform, seed
import math
ri = prman.Ri()
#------------------------------------------------
def distance(x,y,z):
    return math.sqrt(x*x + y*y + z*z)
#------------------------------------------------
while True:
    try:
        line = raw_input().strip()
    except EOFError:
        break
    else:
        detail, data = line.split(' ', 1);
        inputs = data.split()
        num = int(inputs[0])
        rad = float(inputs[1])
        outerR = float(inputs[2])
        minC = float(inputs[3])
        maxC = float(inputs[4])
        
        seed(1)
        # Open a rib stream
        ri.Begin("-")
        count = 0
        
        #generate some geometry
        while count < num:
            ri.TransformBegin()
            x = uniform(-outerR, outerR)
            y = uniform(-outerR, outerR)
            z = uniform(-outerR, outerR)
            
            
            #Find the distance and ignore if it is
            #larger than the outer radius
            if distance(x,y,z) > outerR:
                continue
            count = count + 1
            ri.Translate(x,y,z)
            
            # Look-dev code begins here......
            rgb = [uniform(minC,maxC),
                   uniform(minC,maxC),
                   uniform(minC,maxC)]
            usr_attrs = { 'color cs' : rgb,
                          'float gain' : uniform(0.1,1),
                          'float disp' : uniform(0.3,0.8),
                          'float freq' : uniform(3,10) }
            ri.Attribute('user', usr_attrs)
            ri.Attribute('displacementbound', { 'float sphere' : 0.8} )
            ri.Sphere(rad, -rad, rad, 360)
            ri.TransformEnd()

        # Generate some geometry...
        ri.Sphere(rad, -rad, rad, 360)
        
        # The "/377" escape sequence tells prman we have finished.
        ri.ArchiveRecord(ri.COMMENT, "\n\377")
        sys.stdout.flush
        # Close the rib stream
        ri.End()
            

Generates Spheres
import prman
import sys
import string
from random import uniform, seed
import math
ri = prman.Ri()
#------------------------------------------------
def distance(x,y,z):
    return math.sqrt(x*x + y*y + z*z)
#------------------------------------------------
def aimY(direction):
    out = []
    dirx,diry,dirz = direction
    
    xyLength = math.sqrt(dirx * dirx + diry * diry)
    xyzLength = math.sqrt(dirx * dirx + diry * diry + dirz * dirz)
    
    if xyLength == 0:
        if dirx > 0:
            zAngle =  math.radians(90)
        else:
            zAngle =  math.radians(-90)
    else:
        zAngle = math.acos(diry/xyLength);
        
    xAngle = math.acos(xyLength/xyzLength)
    
    if dirz < 0:
        xAngle = -xAngle
    
    out.append(math.degrees(xAngle))
    
     if dirx > 0:
        zAngle = -zAngle
    
    out.append(math.degrees(zAngle))
    return out
#------------------------------------------------------
while True:
    try:
        line = raw_input().strip()
    except EOFError:
        break
    else:
        detail, data = line.split(' ', 1);
        inputs = data.split()
        num = int(inputs[0])
        scale = float(inputs[1])
        outerR = float(inputs[2])
        innerR = float(inputs[3])
        
        seed(1)
        # Open a rib stream
        ri.Begin("-")
        count = 0
        
        rotation_value = 360.0/num
        #generate some geometry
        while count < num:
            x = uniform((-outerR), (outerR))
            y = uniform((-outerR), (outerR))
            z = uniform((-outerR), (outerR))        
            
            #Find the distance and ignore if it is
            #larger than the outer radius
            if distance(x,y,z) >= outerR or distance(x,y,z) <= innerR:
                continue
            ri.TransformBegin()
            count = count + 1
            ri.Translate(x,y,z)
            
            xrot, zrot = aimY([x,y,z])
            ri.Rotate(zrot, 0,0,1)
            ri.Rotate(xrot, 1,0,0)
            
            #ri.Rotate(rotation_value * count, 1, 0, 1)
            ri.Scale(scale, scale, scale)
            # load rib file
            #ri.ReadArchive("/home/weilin22/mount/stuhome/maya/projects/705/Procedural/rib_archives/rose01.rib")
            #ri.ReadArchive("/home/weilin22/mount/stuhome/maya/projects/705/final/rib_archives/rose01.rib")
            ri.ReadArchive("D:/MAYA/project/705/final/final/rib_archives/rose01.rib")
            ri.TransformEnd()
                    
        
        # The "/377" escape sequence tells prman we have finished.
        ri.ArchiveRecord(ri.COMMENT, "\n\377")
        sys.stdout.flush
        # Close the rib stream
        ri.End()