RSL
Sierpinski Point Clouds


return to main index



Introduction

Point clouds are generated by a specialized shader. They bake "expensive to calculate" shading data into a file using the RSL function bake3d(). After the point cloud file has been written it is converted into an optimized 3D file format called a brickmap. When the scene is re-rendered the previously computed "expensive to calculate" shading data is read from the brickmap and is used as a 3D texture. The important issue to note is that the surface(s) that generated the 3D data are normally the same surface(s) that re-use the data.

This page presents a technique for generating point clouds that contain information that is not the same as the surface that generated the data. Instead, the cloud contains points created procedurally - what this tutorial refers to as an arbitary point cloud. Such point clouds, after conversion to a brickmap, can be directly rendered as a geometric primitive via a call to the Geometry rib statement ie.

    Geometry "brickmap" "filename" "PATH/sierpinski.bkm"

Information about this rib statement can be found at,

    prman_technical_rendering/AppNotes/brickmapgprim.html


Figure 1


To demonstrate the technique the shader in listing 1 generates a 3D version of a Sierpinski gasket. An example of which is shown in figure 1. The shader in listing 2 is, computationally, extremely expensive because it uses a for-loop that repeats a calculation 10's of thousands, 100's of thousands, or even more times per micro-polygon. Such a shader cannot be used to shade a "regular" surface. However, there is a RenderMan primitive that can be rendered with the shader because it will run the shader only once. That object is a RenderMan point (aka RiPoint).

An excellent book on fractals is,
    Fractals for the Classroom
    by Maletsky, Perciante and Yunker
    ISBN: 0-387-97041-X

The inspiration to generate a Sierpinski point cloud using a special purpose shader came from this book.


Basic Code

The shader code consists of two files, sierpinski.sl and sierpinski_lib.h, listing 1 and 2 respectively. When the shader is assigned to a RenderMan point (aka RiPoint) a point cloud is written that contains a point cloud in the shape of a 3D Sierpinski gasket. After the bake-pass the point cloud must be converted to a brickmap in preparation for its use as renderable geometry - listing 5.

The RSL code in listings 1 & 2 generates the shader that when assigned to a RiPoint will write a point cloud - see RIB listing 1. After the point cloud has been written it must be converted to a brickmap - refer to the System statement in listing 4.


Listing 1 (sierpinski_lib.h)


point pickVert(point pnt[])
{
float n = arraylength(pnt);
float index = floor(random() * n);
return pnt[index];
}
  
point halfStep(point from, to)
{
float x = (xcomp(to) - xcomp(from))/2;
float y = (ycomp(to) - ycomp(from))/2;
float z = (zcomp(to) - zcomp(from))/2;
point pnt = point(xcomp(from) + x,
                  ycomp(from) + y,
                  zcomp(from) + z);
return pnt;
}


Listing 2


#include "sierpinski_lib.h"
  
surface 
serpinski(point tri[4] = {point (0,0,1),
                          point (1,0,-1),
                          point (-1,0,-1),
                          point (0,1.5,0)};
          float  iterations = 100000,
                 bakeNormals = 1; /* [0 or 1] */
          string bakename = "")
{
normal n = normalize(N);
point  playpnt = point(0,0.5,0), vert;
float  j;
for(j = 0; j < iterations; j = j + 1)
    {
    vert = pickVert(tri);
    playpnt = halfStep (playpnt, vert);
    point pp = point "world" (xcomp(playpnt),  
                              ycomp(playpnt),
                              zcomp(playpnt));
    normal nn = normal "camera" (xcomp(playpnt),  
                              ycomp(playpnt),
                              zcomp(playpnt));
    if(bakeNormals)
        n = normalize(nn);
    else
        n = normal(0,0,0);
    bake3d(bakename, "Cs,N,P", pp, n, 
                     "Cs", Cs, "N", n, "P", pp);
    }
Oi = Os;
Ci = Oi * Cs;
}


Bake Pass - Beauty Pass

Listing 3 provides a rib file suitable for baking the Sierpinski point cloud.


Listing 3 (bake-pass)


DisplayChannel "color Cs"
DisplayChannel "point P"
DisplayChannel "normal N"
  
Display "serpinski" "framebuffer" "rgba"
Format 800 800 1
Projection "perspective" "fov" 40
ShadingRate .01
LightSource "distantlight" 1 "intensity" 1.5 
                 "from" [0 0 0] "to" [0 0 1]
  
Translate  0 0 4
Rotate 9   1 0 0
Rotate 0   0 1 0
Scale 1 1 -1
  
WorldBegin
    TransformBegin
       Color 1 0 0
       Surface "serpinski"
               "bakename" ["./sierpinski.ptc"]
               "iterations" 10000
               "bakeNormals" 0
  
       Points "P" [0 0 0] "constantwidth" [0.005]
    TransformEnd
WorldEnd
System "brickmake ./sierpinski.ptc ./sierpinski.bkm"


Listing 4 provides a rib file suitable for rendering a beauty pass of the Sierpinski brickmap.


Listing 4 (beauty-pass)


Display "oriented_normals" "framebuffer" "rgba"
Format 320 240 1
Projection "perspective" "fov" 30
ShadingRate 5
  
LightSource "distantlight" 1 "intensity" 1.5 
                 "from" [0 0 0] "to" [0 0 1]
  
Translate  0 -0.6 4
Rotate -10 1 0 0
Rotate 10   0 1 0
Scale 1 1 -1
WorldBegin
    AttributeBegin
        Surface "geo_show" "Kd" 0.8
        Geometry "brickmap" 
                 "filename" 
                 "./sierpinski.bkm"
    AttributeEnd
WorldEnd

Listing 5 gives the code for a shader that considers the normal of the micro-polygon being shaded to face the incident ray ie. the viewing vector. This ensures that when the brickmap is rendered as geometry all parts of the Serpinski cloud have equal brightness.


Listing 5


surface
geo_show(float    Kd = 1)
{
color  surfcolor = 1;
normal fakeN = normalize(-I);
color  diffusecolor = Kd * diffuse(fakeN);
Oi = Os;
Ci = Oi * Cs * surfcolor * diffusecolor;
}




© 2002- Malcolm Kesson. All rights reserved.