RenderMan
Curve Basics


return to main index

References

    "Nurb Curves: A Guide for the Uninitiated"
    http://devworld.apple.com/dev/techsupport/develop/issue25/schneider.html



Introduction

This tutorial covers the basic issues of dealing with the RenderMan's Curves primitive. Figure 1 shows four colored curves that have the same cv's but rendered using different "curve types" ie.

    b-spline
    bezier
    catmull-rom
    hermite

The cv's are shown as black disks while the connecting lines show the sequence of cv's.



Figure 1


There is a clear difference between the curves. In particular, notice the green bezier curve is the only one to begin and end at the first and last cv. The rib statements used to render the bezier curve are,

Basis "bezier" 3 "bezier" 3
Curves "cubic" [4] "nonperiodic" 
                   "P" [-0.75 0 0.5  -0.45 0 1  0.5 0 0  0.75 0 1]
                   "constantwidth" [0.005]
                   "Cs" [0 1 0   0 1 0]

The rib file used to render figure 1 is shown in listing 1.


Listing 1 (curves.rib)


Display "curves" "it" "rgba"
Format 400 270 1
Projection "perspective" "fov" 25
ShadingRate 1
  
Translate -0.03 0.5 2.75
Rotate -90 1 0 0
Rotate 0 0 0 1
Scale 1 1 -1
WorldBegin
    Surface "constant"
    Points "P"  [-0.75 0 0.5   -0.45 0 1   0.5 0 0   0.75 0 1 ] 
               "constantwidth" [0.03] 
               "Cs" [0 0 0  0 0 0  0 0 0  0 0 0]
    Curves "linear" [4] "nonperiodic" "P" [-0.75 0 0.5   -0.45 0 1   0.5 0 0   0.75 0 1]
            "constantwidth" [0.005]
            "Cs" [.5 .5 .5   .5 .5 .5   .5 .5 .5   .5 .5 .5]
    Basis "b-spline" 1 "b-spline" 1
    Curves "cubic" [4] "nonperiodic" "P" [-0.75 0 0.5   -0.45 0 1   0.5 0 0   0.75 0 1]
            "constantwidth" [0.007]
            "Cs" [1 0 0   1 0 0]
    Basis "bezier" 3 "bezier" 3
    Curves "cubic" [4] "nonperiodic" "P" [-0.75 0 0.5  -0.45 0 1   0.5 0 0   0.75 0 1]
            "constantwidth" [0.007]
            "Cs" [0 1 0   0 1 0]
  
    Basis "catmull-rom" 1 "catmull-rom" 1
    Curves "cubic" [4] "nonperiodic" "P" [-0.75 0 0.5   -0.45 0 1   0.5 0 0   0.75 0 1]
            "constantwidth" [0.007]
            "Cs" [0 0 1   0 0 1]
    Basis "hermite" 2 "hermite" 2
    Curves "cubic" [4] "nonperiodic" "P" [-0.75 0 0.5   -0.45 0 1   0.5 0 0   0.75 0 1]
            "constantwidth" [0.007]
            "Cs" [1 1 0   1 1 0]
WorldEnd

Curves generated by Maya and mtor/Rfm the curve type is "b-spline" ie.

    Basis "b-spline" 1 "b-spline" 1

Control Vertices

Although it may appear a RenderMan curve can be defined by an arbitary number of cv's, infact, the number of cv's must conform to formula determined by the type of curve being rendered. The simpliest curve type to use is a "b-spline" because it can be defined by any number of cv's - greater than 4. However, in general, a "b-spline" curve does not begin and end at the first and last cv unless the point data for those cv's are repeated twice. For an example of how RenderMan Studio writes a Curves statement refer to the tutorial "RenderMan: Maya Curves to Rman Curves").

A "bezier" curve does have the desirable property of starting and finishing at the first and last cv. However, the number of cv's, less 1, must be exactly divisible by 3. For instance, we might use 4, 7, 10 or 13 cv's for a "bezier" curve. The only time when the restriction on the number of cv's can be ignored is when a "periodic" curve is produced, in which case, the end of the curve wraps around to coincide with the beginning of the curve.


Curve Segments

The RenderMan Interface Specification allows "attributes" to be bound to each segment or span of a curve. Looking at a curve, from the "outside", it is defined by,

  • the xyz values of its control vertices (cv's),
  • curve type

From the point of view of the renderer, from the "inside", a curve consists of a number of segments (spans).


Figure 2


For example, figure 2 shows a b-spline curve that consists of 9 cv's (the first and last cv's have been repeated twice). The curve has (cv's - 2) segments and each of them was assigned a width ie.

    Curves "cubic" [9] "nonperiodic" 
           "P" [0 0 0    0 0 0    0 0 0    1 0 0     2 0 0
                3 0 0    4 0 0    4 0 0    4 0 0] 
           # one width for each of the 7 segments
           "width" [0.01  0.1  0.01  0.1  0.01  0.1  0.01]

Surface color "Cs", for example, can be used to blend colors along the length of a curve - figure 3.



Figure 3

    Curves "cubic" [9] "nonperiodic"
           "P" [0 0 0 0 0 0 0 0 0 1 1 0 2 -1 0
                3 1 0 4 -1 0 4 -1 0 4 -1 0] 
           "constantwidth" [0.03]
           "Cs" [1 1 1
                 1 1 1
                 1 0 0
                 0 1 0
                 0 0 1
                 1 1 1
                 1 1 1]

Figures 4, 5 and 6 illustrate what happens when the first and last cv's are not repeated, repeated once and repeated twice.



Figure 4 - no repeats


Figure 5 - one repeat


Figure 6 - two repeats


Python Curves Generator

It is sometimes useful to generate a complex curve consisting of an arbitary number of cv's. Listing 2 gives a python script that outputs a rib file that may be referenced as a pre-baked (listing 3). The curve is defined by random cv's constrained to lie on the surface of a sphere.


Listing 2 (curves.py)


import sys, math, random
  
random.seed(5)
def randBetween(min, max):
    return random.random() * (max - min) + min
def length(x, y, z):
    return math.sqrt(x*x + y*y + z*z)
def normalize(x, y, z):
    len = length(x, y, z)
    return x/len, y/len, z/len
def scaleVector(x, y, z, sc):
    return x*sc, y*sc, z*sc
  
def filament(fileID, numCV, radius, width, addCs):
    fileID.write('Basis \"b-spline\" 1 \"b-spline\" 1\n')
    fileID.write('Curves \"cubic\" [%s] \"periodic\" \"P\" [\n' % numCV)
    for n in range(numCV):
        x = random.random() * 2 - 1
        y = random.random() * 2 - 1
        z = random.random() * 2 - 1
        x,y,z = normalize(x, y, z)
        x,y,z = scaleVector(x, y, z, radius)
        
        fileID.write('%s %s %s ' % (x, y, z))
        if (n + 1)  % 2 == 0:
            fileID.write('\n')
    fileID.write( '] \"constantwidth\" [%s]' % width)    
    if addCs == 1:
        fileID.write('\n\"Cs\" [')
        for i in range(numCV):
            r = randBetween(0, 1)
            g = randBetween(0, 1)
            b = randBetween(0, 1)
            fileID.write('%s %s %s' % (r, g, b))
            if (i + 1)  % 2 == 0:
                fileID.write('\n')
        fileID.write( ']')
  
def main():
    fileID = open('RULL_PATH/curve.rib','w')
    filament(fileID, 400, 1, 0.01, 1)
    fileID.close()
    print 'completed'
  
if __name__ == "__main__":
    main()


ReadArchive

After executing the python script the pre-baked rib that is generated may be rendered with the rib file shown in listing 3. The result of outputing 400 cv's is shown in figure 7.


Listing 3 (archiveTester.rib)


Display "untitled" "it" "rgba"
Format 400 400 1
Projection "perspective" "fov" 30
ShadingRate 1
  
LightSource "distantlight" 1 "intensity" .5 "from" [0 0 0] "to" [0 0 1]
  
Translate  -0.1 -0.25 2.5
Rotate -30 1 0 0
Rotate 0   0 1 0
Scale 1 1 -1
WorldBegin
    LightSource "pointlight" 1 "intensity" 25 "from" [1 4 1]
    TransformBegin
        Surface "plastic"
        Color 0.5 0.5 0.5
        ReadArchive "FULL_PATH/curve.rib"
    TransformEnd
WorldEnd



Figure 7




© 2002- Malcolm Kesson. All rights reserved.