Renderman

Spherical Harmonics with RenderMan (some progress)

As part of my RenderMan for real-time research, I have for some time, been working on a solution for generating spherical harmonic coefficients from arbitrarily complex lighting environments for use in real-time. While not completely finished with this, I have some promising preliminary results.

First, some background. Spherical Harmonic lighting is a method for computing complex low-frequency lighting solutions in real-time by pre-calculating a series of SH coefficients which when used in a linear combination of SH basis functions, can produce an approximation of the original lighting. For more details on this process, I would direct you to Robin Green’s Spherical Harmonic Lighting: The Gritty Details. I drew much of my code from this paper.

Continue Reading »

Programming
Renderman
Uncategorized

Comments (0)

Permalink

RenderMan for Real-time: Baking Light Maps

Those familiar with creating 3D assets for games or other real-time applications are probably familiar with the process of baking static lighting information as a series of maps which are combined with dynamic light contribution at runtime. Precomputed light maps can represent even the most complex lighting rigs in real time, provided neither the geometry or the lights change.

These maps can be easily generated by popular modeling packages, so why use RenderMan? Using RenderMan to precompute lighting information comes with several distinct advantages. First, one may leverage the power of a RenderMan renderer, which in many cases can be obtained for free. You don’t need Maya or 3D Studio Max to make nice light maps. Secondly, if a generic interface is created between game data (your proprietary scene files) and RenderMan, you could easily integrate a RenderMan system into your existing code and tools. Finally, while light maps can easily be generated by Maya or Max, neither of them support compressed HDR output, which I plan to implement. Also, they cannot create spherical harmonic data for dynamic geometry - another issue I plan to address. See my other post on the subject for more information.

Continue Reading »

Programming
Renderman

Comments (1)

Permalink

Compiling RenderMan extensions in Mac OS X

The RenderMan Shading Language is very feature rich, but at some point, you may need to extend the language to do something it doesn’t do out of the box. To do this, you must write a DSO Shadeop.

I have written several of these using PRMan’s SDK at school, but when it came time to write one at home using 3Delight on my Mac, I was at a loss. The 3Delight documentation regarding DSOs describes how to write a very simple one and compile it using the command line. Compiling stuff with the command line is cumbersome, so I worked out a way to do it with the XCode IDE.

First, I tried creating a compiling one of XCode’s C++ dynamic library projects, but was unable to link the resulting library in when it came time to compile the shader. The example in the 3Delight documentation compiles to a “.so” file (shared object, I assume), so I set about trying to create one with XCode.

Continue Reading »

Programming
Renderman

Comments (0)

Permalink

Annoying RenderMan linking issue

When using the PRMan SDK for Windows, I ran into a strange linking issue. Linking against prman.lib and libprmansdk.lib caused about 100 undefined symbol errors. It took me a while to track down a fix (there is very little documentation for the RenderMan SDK). It turns out that the SDK links against wsock32.lib and iphlpapi.lib. Just add those in your linker settings, and you should be golden. I hope this saves somebody some time.

Renderman

Comments (1)

Permalink

Renderman for Real-time

Realtime Renderman PipelineI have been investigating solutions for real-time global illumination over the past few weeks, and thought I might share some of my thoughts on a potential approach to this problem.

To create a global illumination effect for static geometry and lights is relatively easy. One must simply bake the lighting information into a series of light maps which are then multiplied into the scene when rendered in game. The lighting rigs can be as complicated as you like, and can leverage whatever cool features your DCC tool has, provided the lights and the geometry in the scene never change.

Dynamic geometry presents a bit of a problem. Light maps won’t do because when the object moves from it’s baking position, the values from the map are no longer valid. Though some global illumination effects can look passable even when not accurate (ambient occlusion, for example), in most cases, this is unacceptable. The most common solution for achieving low-frequency global illumination effects on dynamic objects is to approximate the environment’s lighting using spherical harmonic approximation, a technique similar to Fourier signal approximation.

Continue Reading »

Programming
Renderman

Comments (1)

Permalink

Preliminary results for volume rendering using point clouds

In order to render volumes in Renderman, one has two options. The first is to render a large amount of points (several million to get good results) or ray march through a volume using some density function to compute the volume’s accumulated color and opacity along the ray. In this work, I am attempting to produce a hybrid approach. By sampling density from a relatively sparse point cloud (several thousand points rather than several million) in a ray marching shader rather than a density function, volumes can be modeled or simulated in external applications and rendered in Renderman using Ray-marching. See older posts on the subject for more details.  The following is based on Ian Stephenson.

How do we sample density from a cloud of points? Given a position in the cloud, we need the number of points within a given radius. Dividing this by the volume sampled, density can be approximated. Renderman has two data structures for storing point data: point clouds and brick maps. Brick maps are the most attractive solution, as they store point data in an octree for easy spatial look. However, the function for accessing data from a brick map from within a shader, texture3d(), returns an interpolated value of all the samples within a given radius, not an accumulated one. This necessitated the creation of a custom data structure and interface in the form of a DSO shadop.

Here are the source files:

particlemap.h

particlemap.c

pmapdso.c

converter.c

A simple volume shader samples the kd-tree while ray-marching. Here are some preliminary renders:

Cloud 1Cloud 2Cloud 3

Each of these renders uses a increasingly large sampling radius when calculating density from the kd-tree. As the sample radius increases, the volume begins to converge to a shape with hard edges along what I think are the kd-tree cell boundaries. I’m still investigating how to fix these artifacts. More later.

Renderman

Comments (0)

Permalink

Clouds continued…

interiorvolumetest_1.jpgI’ve settled on using an interior volume shader to render my clouds. The image below shows a torus rendered with the ‘noisysmoke’ volume shader which ships with the 3Delight renderer. The shader ray-marches through the volume, grabbing density values from a density function (noise in this case) along the way.

Obviously, simply using a function to compute the density of the cloud leaves something to be desired, and is not at all flexible. Ideally, the density of the cloud should be based on particle data, which can be generated in a specialized application (RealFlow, for example). An optimized data structure containing the particle data would be queried at a point and return the number of points within a radius. The density could then be approximated by dividing the number of points returned by the volume sampled. To achieve this, I have written a DSO-shadeop like the one written by Ian Stephenson here. Results from this method are forthcoming.

Presumably, the RenderMan brickmap or pointcloud data structure should support this kind of querying (given a position and a radius, return the number of points found), saving me the trouble of implementing my own Kd-tree and sampling functions. While it’s not the density of a point cloud being computed in procedures like photon mapping and point-based ambient occlusion, this kind of querying is required.

Programming
Renderman

Comments (0)

Permalink

Cloud rendering preliminaries

clouds.jpg

Clouds present an interesting rendering challenge. Approaches to cloud rendering are varied and range from convincing sprite-based solutions that render in real-time to expensive volumetric solutions which must be rendered off-line. A sprite-based approach, while fast and convincing from a distance, does not provide a realistic representation when the cloud is viewed up close or when passing through the cloud.

Some interesting work has been done by Mark Harris on a hybrid method for rendering clouds in real-time using impostors - billboarded planes textured with a render of the cloud - to speed up rendering considerably. Hit the jump for more details.

For a Renderman-based solution, I have come up with two approaches that might merit further inquiry. The first is to use particles to build a brickmap (voxel data) then sample the brickmap in a volume shader using ray-marching. The second approach would be to define a transparent surface with an internal volume shader and ray-march using a parametric equation of some kind (Perlin noise would be a good start).

The major benefit of the first approach is that the representation of the cloud - a collection of particles - is a common format for representing fluids in computer graphics, and as such, data could be generated using an external application (RealFlow, for example) and built into a brickmap.

Baking a constant color with zero opacity could be sufficient to extract a density approximation from the brickmap. Consider the image below:

ray-march.jpg

A ray is being projected into the brickmap, and two samples are being taken. At sample A, a light blue color would be returned because there are fewer particles covered by the sample’s radius. Sample B would return a dark blue color, indicating a higher density. The final color of the ray is attenuated by the densities sampled from the brickmap in this manner. If the sample points along the ray are uniform, striations might occur in the final image. It might be wise to jitter the samples’ location (not count) to produce a smoother transition.

The second approach forgoes a particle-based cloud representation in favor of a parametric equation. This method might be useful for generating clouds without having to use expensive fluid dynamics software to generate the initial point data.

internalshader.jpg

In the image above, the cloud’s volume (the blue stuff) is generated of some parametric function instead of a collection of point data. This method has several user-centric advantages over the particle-based approach. An artist could easily create the cloud’s geometry from primitives. The equation used to generate the cloud volume could be changed and animated to suit aesthetic requirements.

In both situations,  the use of ray-marching makes it easy to compute the anisotropic scattering effects scene in clouds.  Doing so is a simple matter of altering the incident light vector by some phase function as we integrate.

Renderman

Comments (0)

Permalink

Renderman Helper Applications

Renderman Helper applications can be executed from a rib file to generate rib commands. The helper application and Renderman communicate via stdin - simply write a program which prints the rib statements you want.

Procedural "RunProgram" ["/usr/bin/python ../python/FractalFlame.py" "100000 0.001 "] [-1 1 -1 1 -1 1]

To test this, I have created a very simple implementation of the Scott Draves’ Fractal Flame algorithm; a type of iterated function system which generates nice fractal point clouds. For each iteration of the algorithm, a point is transformed by a random non-linear function. After several iterations, the system begins to converge on a shape. In Python, I utilized the __call__ method to create ‘functor’ objects:

class Sinusoidal:
def __call__(self, point):
point[0] = math.sin(point[0])
point[1] = math.sin(point[1])
point[2] = math.sin(point[2])
return point

These are added to a table from which I indexed into with a random value:

self.Functions = []
self.Functions.append(Linear())
self.Functions.append(Sinusoidal())
self.Functions.append(Spherical())
self.Functions.append(Swirl())
self.Functions.append(Polar())
self.Functions.append(Handkerchief())
self.Functions.append(Heart())
self.Functions.append(Horseshoe())

Finally, the script prints rib statements to stdin:

def MakePoints(self, iterations, size):
'Runs the chaos game and makes the points'

# Create a random point
point = [ random.random(),
random.random(),
random.random() ]

# A random color
c = [ random.random(),
random.random(),
random.random() ]

for i in range(0, iterations):
print('AttributeBegin\n')
# Select a random function
funcIndex = random.randint(0, len(self.Functions) - 1)

# Transform the point
point = self.Functions[funcIndex](point)

# Fetch the functions color from the color table
color = self.Colors[funcIndex]

# Average the color with the previous color
c[0] = (c[0] + color[0]) / 2
c[1] = (c[1] + color[1]) / 2
c[2] = (c[2] + color[2]) / 2

# Plot the point
print('Points "P" [%s %s %s]\n' % (point[0], point[1], point[2]))
print('"constantwidth" [%s]\n' % size)
print('"Cs" [%s %s %s]\n' % (c[0], c[1], c[2]))
print('"Oi" [0 0 0]\n')
print('AttributeEnd\n')

Renderman

Comments (1)

Permalink

Adventures in RenderMan: Fake Sub-surface Effect

subsurface6.png

The objective of this project is to simulate a surface which has a bumpy surface coated with a smooth glaze without actually generating two surfaces.

First Pass

Creating the smooth top surface is fairly trivial. To get the bottom surface to appear bumpy, a new normal and point must be generated and a new lighting calculation performed. My first attempt utilized RenderMan’s new object-oriented shader to combine both a surface and displacement shader. The class had member variables which stored an original undisplaced position and normal as well as a displaced position and normal as computed in the displacement method. In the surface method, specular lighting was calculated with both sets of P and N, then combine for the final result.

Class-based sub-surface

subsurface.sl

Though somewhat unrefined, the effect is present. The larger specular highlight of the top surface is unaffected by the bumpy sub-surface.Even though class-based shaders are infinitely cooler than the traditional procedural ones, they are currently not compatible with RenderMan for Maya.Second PassTo make usage in Maya possible, my second pass was a traditional procedural shader. Refining the effect a little, I added environment mapping and a nice Fresnel effect.

fakesubsurfacepass2.png

subsurfacetraditional.sl

The environment reflections and Fresnel effect add some much needed realism to the surface. Both are computed in a relatively simple manner. The environment reflections are simply sampled from an environment map via the reflected eye vector (no ray tracing). The Fresnel effect is computed relative to the angle between the eye vector and the current surface normal (eyeVec dot N) and multiplied by the reflection color value which was sampled from the environment map.

Third Pass

subsurface5small.png

My third pass was to try to replicate and polish the effect using a few custom Slim templates and RenderMan Studio in Maya. At first, I tried to rewrite my original implementation as a Slim template. I found this solution lacking both in visual quality and in scalability.

After discovering the Delux surface shader in Slim, I utilized its ability to have multiple diffuse and specular layers to create my effect. In my original implementation, I was actually displacing each micropolygon and computing light for the sub-surface. I decided for the third pass to abandon this approach in favor of a technique common to real-time shading - the normal map.Normals are expressed as a 3-tuple, and as such can be stored as an RGB color value. Instead of displacing each point and recomputing the normal, I generated a normal map from a gray-scale fractal pattern using a hue shift Slim template. I then offset the original normal value by a value sampled from the normal map using an SLBox node.

normalmapfig.png

hueshift.slim

I also found the specular highlights looked much better when the original base gray-scale image was attached to the specular component’s intensity value.

finalsubspecular.png

The diffuse component for the sub-surface is calculated in a similar manner.To further refine the effect, I added true ray-traced reflections with falloff controlled by the same Fresnel code I used in my traditional shader, now as a Slim template.

fresnel.slim

Additionally, I changed the diffuse coloration of the sub-surface to be driven by the angle between the eye vector and the light vector as well as the length of the eye vector, which produced an interesting effect.

diffusecoloration.png

Here is the final Slim network:

slimnetwork.png

Renderman

Comments (1)

Permalink