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

Wordpress formatting is dumb

I can’t seem to get code and HTML formatting to work properly. Sorry about the inconvenience. I’ll be fixing this once my wonderful girlfriend nurses me back to health from EXTREME WISDOM TEETH EXTRACTION.

Programming
Random

Comments (1)

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

Aaron gets his comeupins

For contributing, however insignificantly, to my horrific movie selection the other day, I destroyed Aaron in a blindingly brilliant game of Go.  Well, alright, maybe not blindingly, but whatever.  Feeling good about myself, I played GNUGo shortly thereafter, and was handed my ass in relatively short order.

gosmall.jpg 

I finally got around to getting the new Zelda game for DS, thanks to a Best Buy gift card.  I must say, it’s a pretty sweet game.  Phantom Hourglass is a direct continuation of Wind Waker, which I loved.  Driving your little boat around is FUN and not LAME as it was in Wind Waker.  Moving and cutting with the stylus is sweet, though I’m having trouble rolling for some reason (probably because I have the motor skills of a two year old).

God bless the Best Buy gift card.  If you simply get money for Christmas or your birthday, you are presented with a dilemma.   On the one hand, you could save the money, or buy food for your apartment (we ran out), or you could on the other hand  neglect your domestic and long term financial responsibilities and get something sweet, like Zelda: Phantom Hourglass.  With a gift card from Best Buy, you are required to get something sweet.  You just can’t beat that. 

Uncategorized

Comments (0)

Permalink

Atari!!

goboardsmall.jpgThat’s what I’ll be saying to you when I’m about to pwn your face with my new Go board!  Just look at it.  Awesome.  It even makes that *click* sound when you place a stone.  This beats GNUGo any day, although GNUGo beats me every day.  Damn you, GNUGo…  I’ll get you one day. If anybody wants to play with me, either come to my house with a bunch of your samurai and kick my sign down, or grab the Go app on Facebook.

bookssmall.jpgOn another note, I am running out of shelf space. After this Christmas, the influx of new reading material has brought my poor Wal Mart bookshelf to its knees. Its particle board shelfs have begun to grown under the added weight, and I think it’s leaning away from the wall a bit more than it used too. I’m pretty sure the thing will eventually fall over and kill me. It would be a good death.

After plowing through the third season of West Wing the other night, I decided I would grab one of Aaron’s movies from the shelf and watch it. For some reason, the end of “The Last Crusade” slipped my mind, and I picked the movie that was the shiniest. I chose… poorly. While my face didn’t melt off, I did sit for an hour and a half transfixed by the horrors of “Shriek If You Know What I Did Last Friday The 13th”. Why, Aaron? It had Coolio in it! COOLIO!!

Random

Comments (0)

Permalink

After Christmas hangover

photo-1.jpg… Not really.  As much as I wish this was all some hallucination, I am in fact in the Wichita airport, tired and an hour away from a flight to Dallas, followed by a flight to Atlanta, and topped off with a 3 1/2 hour drive to Savannah.  Somebody shoot me. 

Time spent with the family is totally worth the trip, though.  I so rarely see my folks anymore, and it’s nice to be fed and to load up on books and nice things to wear.  It has been snowing in Kansas; something that is still novel to me.  In the South, we believe ‘hard water’ is a man-created thing, fashioned out of the need to cool one’s beer, and certainly not something that simply falls from the sky.  Dad apparently drove into a snow drift the other day and had to be dislodged by a better equipped passerby.  He claimed he had been caught in a ‘white out’ of arctic magnitude - three meters from the Target entrance.  Okay, Dad. 

I’m very much looking forward to being home again, and to picking up Iterator from the kitty hotel.  I have a few days before the other half of my family arrives for New Years. 

There are still some rather large loose ends I need to tie up after the work Dave and I did on the code base.  The new background loading system which we implemented has opened a whole new can of worms.  As it turns out, the Windows OpenGL implementation’s rendering context is thread locked.  When a scene is loaded in the background, it has to initialize new textures and VBOs with the rendering context, which belongs to the rendering thread.  This means that I’ll either have to switch ownership of the context between threads when something is loading (bad) or defer initialization of OpenGL dependent objects (meshes and materials) and load them in the rendering thread when it isn’t rendering (also bad).  This is all really dumb.  When writing this kind of thing on the Mac, you can manipulate a GL context from whatever threads you want, provided you set up your critical sections correctly.  Windows is dumb. 

On the plus side, I’ve refactored each class’s Lua initialization code to register with the script system using a pre-main initializer, and have rearranged things such that each new Actor instance has its own Lua state, which shares a global table with the script system’s main Lua state.  Prior to this, I had a single global Lua state which was shared by all Actor instances.  This fixes a problem I was running into when trying to add new Actors (and their scripts) in a background thread using the same Lua state being executed in another thread.  Only one Lua state per thread.  Always. 

Enough rambling.  I have a plane to catch.  See you in Savannah!! 

Programming
Random

Comments (3)

Permalink

Long build is loooooooong

The difference between a good programmer and a mediocre one is the good programmer has seen more errors and fixed more problems than the mediocre one. With this in mind, I’m going to start a segment on my blog where in I will share some of the little things I’ve picked up in hopes it will save someone out there some time. I haven’t come up with a clever title for this yet, but when I do, I’ll let you know.

So let’s get started! Today, I’d like to share with you a little trick I picked up from David Eberly’s books. This tidbit deals with pre-main static initialization in C++.

The Problem:

When you create several static variables or class members in C++, there is no guarantee as to the order in which C++ will initialize their values. Let’s say you have a vector class. You’ve defined a static member somewhere in your vector class called UNIT_X, which is the unit vector along the X axis. Somewhere else in your code, you want to initialize another static vector called sMyUnitX to UNIT_X. This doesn’t present a problem if UNIT_X is initialized first, but because there is no way of knowing which static variable C++ will initialize first, there could arise a situation where the sMyUnitX is initialized before UNIT_X, in which case, sMyUnitX will be filled with zeros instead of the unit vector like it should be.

Solution:

The solution is to somehow defer the initialization of sMyUnitX until after we’ve entered main, thus insuring UNIT_X has been initialized before we assign its value to sMyUnitX. To accomplish this, we’ll create a class called Main. Main will have two static methods: Main::Initialize() and Main::Terminate(), and will store an array of initializer and terminator function pointers. Here’s the class:

(Note: WordPress is dumb, and won’t let me put less than or greater than signs in my code without thinking it’s another tag. Those two Array variables down there are supposed to be templates of type Initializer and Terminator)

(Another note: Would somebody please tell me how to get WordPress to retain code indentions?)

// We typedef the initializer and terminator function pointers
typedef void (*Initializer)(void);
typedef void (*Terminator)(void);
class Main {
public:
static void AddInitializer(Initializer initializer);
static void AddTerminator(Terminator terminator);
static void Initialize();
static void Terminate();
private:
static Array<initializer> sInitializers;
static Array<terminator> sTerminators;
}

Main holds two arrays of function pointers. All of the initializers are called when Main::Initialize() is called, and all of the terminator functions are called when Main::Terminate() is called. Here’s what your main function should look like:

void main() {
Main::Initialize();
// Do something cool
Main::Terminate();
}

This is cool and all, but how do we set the initializers and terminators in the first place? That obviously must be done pre-main. There is no way to explicitly tell C++ to execute code pre-main. All C++ will do is initialize static variables. We can however include a function call as part of a static variable’s initialization, thus forcing C++ to call a function pre-main.

// SomeFunction returns a bool, and because it's part
// of sMyStaticVar's initialization, it is called pre-main.
bool MyClass::sMyStaticVar = SomeFunction();

We’ll exploit this little trick and build three preprocessor macros for registering initializers and terminators with Main. Here are the macros for registering initializers:

#define DECLARE_INITIALIZER \
public: \
static bool RegisterInitializer(); \
static void Initialize(); \
private: \
static bool sIsInitializerRegistered \
#define IMPLEMENT_INITIALIZER(classname) \
bool classname::sIsInitializerRegistered = 0; \
bool classname::RegisterInitializer() { \
if (!sIsInitializerRegistered) { \
Main::AddInitializer(classname::Initialize); \
sIsInitializerRegistered = 1; \
} \
return sIsInitializerRegistered; \
}
#define REGISTER_INITIALIZER(classname) \
static bool sIsInitializerRegistered_##classname = classname::RegisterInitializer();

The DECLARE_INITIALIZER macro goes in your class definition somewhere, and sets up the function Initialize(), in which you will put any static initialization you need to defer to post-main, the function RegisterInitialize() which is responsible for adding a pointer to Initialize to Main, and a bool to make sure we don’t initialize twice. The IMPLEMENT_INITIALIZER macro implements the RegisterInitialize() function, and should go in your .cpp file somewhere. The REGISTER_INITIALIZER macro goes in your .h file outside of your class definition, and by using the little pre-main function call trick discussed earlier, actually called the RegisterInitializer() method.

For clarification, here is where the macros go:

// In your .h file:
#include "Main.h"
class AwesomeClass {
DECLARE_INITIALIZER;
public:
AwesomeClass();
~AwesomeClass();
// More methods...
};
REGISTER_INITIALIZER(AwesomeClass);
// In your .cpp file
IMPLEMENT_INITIALIZER(AwesomeClass);
void AwesomeClass::Initialize() {
// Write your initialization code here.
}

That’s it for now!

Programming

Comments (0)

Permalink

Will Miller rocks the 80s and a new podcast

encore.jpgSo I’m on MobyGames.com the other day looking somebody up, and decided to check in on my profile to you know, make sure it was still there. As it turns out, I got a credit on Guitar Hero Encore: Rock the 80s! A pleasant surprise, considering I had nothing to do with 80s. I’ll take what I can get, though.

Also something which might be a pleasant surprise to some, Buck, Fic and I recorded a brand new podcast this evening. Episode 33 is special because as one listener’s feedback pointed out, 33 was the last show we did for IMG with Big Tuncer, after which we left and made BDPE, which is invitingly cooler. Rest easy, dear listeners. We’re here to stay.

Random

Comments (0)

Permalink