A Toolbox for Procedural Solid Texturing with the EDGE Library
Steven Matuszek
University of Maryland Baltimore County


The Toolbox


The source code for the toolbox is available here.


These are the functions in the procedural texturing toolbox.

void read_procedural_parameters(illum_parm_td *illum);
void read_normal_procedural_parameters(illum_parm_td *illum);
void read_transp_procedural_parameters(illum_parm_td *illum);
float trilinear_noise_at(xyz_td pnt_obj);
float turbulence(xyz_td p, float pixel_size);
float normalized_turbulence(xyz_td p, float pixel_size);
float max_turbulence(float pixel_size);
rgb_td quad_color_between(float dist, rgb_td low, rgb_td high);
rgb_td linear_color_between(float dist, rgb_td low, rgb_td high);
float sawtooth(float x, float a);
xyz_td reference_point_cart_xyz_xyz(xyz_td p, xyz_td origin, xyz_td units);
float reference_point_float(float p, float origin, float unit);
int ordinal_id(float p, float origin, float unit);
polar_td reference_point_polar(xyz_td p, polar_td origin, polar_td units);
polar_td reference_point_polar_polar(polar_td p, polar_td origin, polar_td units);
float theta_polar(float x, float y, float z);
float theta_polar_xyz(xyz_td p) ;
float phi_polar(float x, float y, float z);
float phi_polar_xyz(xyz_td p) ;
float radius_polar(float x, float y, float z);
float radius_polar_xyz(xyz_td p) ;
polar_td polar_xyz(xyz_td c);
xyz_td xyz_polar(polar_td p);
xyz_td xyz_float(float theta, float phi, float radius);
int chance_xyz(xyz_td p, float chance);
int chance_polar(polar_td p, float chance);
polar_td oscillatewithrespectto_polar(polar_td p, polar_td origin, polar_td units);   
float oscillatewithrespectto(float p, float origin, float unit);
xyz_td perturb_xyz(xyz_td p, float maxchange, float roughness);
polar_td perturb_polar(polar_td p, float maxchange, float roughness);
rgb_td perturb_color_equally(rgb_td c, xyz_td p, 
    float maxchange, float roughness);
void make_noise_file();
int read_noise_file();
void turnbydegrees(xyz_td *v, float degrees, char which);
void turnbyradians(xyz_td *v, float radians, char which);
float randombetween(float low, float high);
float bias(float b, float x);
float gain(float g, float x);
void print_polar(polar_td p);

The uses of each and discussion

void read_procedural_parameters(illum_parm_td *illum);
void read_normal_procedural_parameters(illum_parm_td *illum);
void read_transp_procedural_parameters(illum_parm_td *illum);
I modified the definition of illum_parm_td, the illumination parameters structure, to include the parameters for the procedural textures. These functions read any floats that are on the color_type (normal_type, transparency) line in the input file and store them in a field, up to a maximum of 30. Each procedure is expected to know the order of the parameters input, and handle however many it is given, for example with defaults. For example:
    if (illum.numparams >= 3)
    {
            maxfruit = illum.parameters[0];
            avemaxwhite = illum.parameters[1];
            dwhite = illum.parameters[2];
    }
    else
    {
            maxfruit = .59;
            avemaxwhite = .2;
            dwhite = .1;
    }

float trilinear_noise_at(xyz_td pnt_obj);
float turbulence(xyz_td p, float pixel_size);
float normalized_turbulence(xyz_td p, float pixel_size);
float max_turbulence(float pixel_size);
trilinear_noise_at() returns the noise value for the given point in space. The noise matrix is a matrix of values between 0 and 1 repeated through 3D space, and the noise at any given point is the value trilinearly interpolated between the eight integer points closest to the point. turbulence() amplifies that information by summing the octaves of the point, that is to say, the noise at the first point, plus half the noise at twice the point, and so forth 1/pixel_size times. (Any additions after the pixel size has been reached would be unnoticeable.) Thus, a value of 16.0 results in a higher-resolution, more turbulent pattern. max_turbulence() simply returns the maximum possible value that turbulence() might return given that pixel_size, and normalized_turbulence() uses that information to always return a value between 0 and 1.


int chance_xyz(xyz_td p, float chance);
int chance_polar(polar_td p, float chance);

These functions return a true or false value for any given point, done by getting the normalized turbulence at that point and checking whether it is less than or equal to the given probability.


rgb_td quad_color_between(float dist, rgb_td low, rgb_td high);
rgb_td linear_color_between(float dist, rgb_td low, rgb_td high);

These two functions take a value between 0 and 1 and two colors, and return the appropriate blend between the two colors. quad_color_between() uses a quadratic blend.


float sawtooth(float x, float a);
float randombetween(float low, float high);
float bias(float b, float x);
float gain(float g, float x);

Utility functions for working with floats. sawtooth(x,a) returns (x mod a) / a, which results in a saw-like pattern /|/|/|/|. randombetween() is self-explanatory. Bias and gain are the well-known functions of the same name for adjusting a value between 0 and 1.


float theta_polar(float x, float y, float z);
float theta_polar_xyz(xyz_td p) ;
float phi_polar(float x, float y, float z);
float phi_polar_xyz(xyz_td p) ;
float radius_polar(float x, float y, float z);
float radius_polar_xyz(xyz_td p) ;
polar_td polar_xyz(xyz_td c);
xyz_td xyz_polar(polar_td p);
xyz_td xyz_float(float theta, float phi, float radius);
void print_polar(polar_td p);

These functions convert between Cartesian (x, y, z) and spherical polar (theta, phi, radius) coordinates. print_polar() prints the theta, phi and radius values of a polar_td, labeling them as such.


void turnbydegrees(xyz_td *v, float degrees, char which);
void turnbyradians(xyz_td *v, float radians, char which);

These functions turn an xyz point around the given axis -- 'x', 'y' and 'z' are the acceptable values for the third parameter. Note that they take the pointer to the xyz_td, so they actually change its values, not just return another one.


xyz_td perturb_xyz(xyz_td p, float maxchange, float roughness);
polar_td perturb_polar(polar_td p, float maxchange, float roughness);
rgb_td perturb_color_equally(rgb_td c, xyz_td p, 
      float maxchange, float roughness);

These are important functions for perturbing a texture so that it does not look too regular. By calling perturb_xyz() or perturb_polar() on a point and calculating the color at the new point, the color calculated will actually be for a slightly displaced point, resulting in a natural-looking, turbulent effect. You can also get a perturbed color at a given point, resulting in smooth but irregular-appearing variations in an object's color. In all these functions, roughness is the same value that is passed to turbulence to determine the length of the summation.


void make_noise_file();
int read_noise_file();

make_noise_file() uses the pseudo-random number generator drand48() to create a cubic matrix of values between 0 and 1, to be used by the noise function. The size of the matrix is defined in "tools.h", as is the global array into which read_noise_file() stores those values when it is called. The file is called "procnoise.dat". The reasons to have the file persist are so that the values at each point are always the same, ensuring consistency, and because generating it takes some time.


xyz_td reference_point_cart_xyz_xyz(xyz_td p, xyz_td origin, xyz_td units);
float reference_point_float(float p, float origin, float unit);
int ordinal_id(float p, float origin, float unit);
polar_td reference_point_polar(xyz_td p, polar_td origin, polar_td units);
polar_td reference_point_polar_polar(polar_td p, polar_td origin, polar_td units);
polar_td oscillatewithrespectto_polar(polar_td p, polar_td origin, polar_td units);   
float oscillatewithrespectto(float p, float origin, float unit);

 


Steven Matuszek
Last updated May 17 1998.