CTBFtoy2In an attempt to get things started, I am going to make 3 surface settings available that I have used in the past. These 3 surfaces (StonePath, StainedGlass, and CrackedMud) have been assigned to unit cubes and made available in the zip file below. Click on the small image to view a larger version of the image, or click on the ctbf_samples1.zip link to download the 3 object files.
These examples each use 2 layers of CTBFtoy. In general, one layer is used to define the border areas of the cells (although in some instances this layer is also used for shading/coloring portions of the interior areas of the cells as well). This layer uses the Bump Amp. Gradient to bump the "border" areas of the cells, but leave the interior areas flat. The other layer is used to define the main color/shade of each cell in the StainedGlass and StonePath samples, and to provide the slight concave shape of each cell in the CrackedMud example. The best way to see how each attribute affects the final surface is to disable each shader attribute one at a time (just click off the check mark next to each shader parameter) and re-render to see the change.
Summary: This is a shader plugin for Lightwave Layout. It allows the user to experiment with an implementation of Steve Worley's SIGGRAPH 96 paper titled "A Cellular Texture Basis Function". (NOTE: This is a password protected link to ACM's online library, so unless you have an ACM online library membership, there is no use trying the previous link. However, here is a link to Michael Monks' online presentation about the paper).
CTBFtoy2 runs under either Lightwave 3D version 5.6 or 6.5
NOTE for LW6 users: You will also want to check out CTBFproc, a procedural texture version of this shader plugin. The main advantages are interactive previews and the ability to use the procedural texture in displacements, volumetric lights, and hypervoxel surfaces!
(Sorry Inspire 3D users, this shader uses the gradient.p which is not included with Inspire. The official word from Newtek's Brad Peebler... "gradient.p wasn't intended to be a part of Inspire and I don't want to change the spec now.").
Since it's introduction in 1985, Perlin's fractal noise basis function has become the primary tool for procedural texturing. In 1996, Steve Worley presented a paper at SIGGRAPH that proposed a new set of procedural texture basis functions based on the distance from a point to "feature points" scattered throughout space. These cellular basis functions have been used as procedural textures in Lightwave for years (Crumple, Crust, Veins and others are specific variations of these basis functions). This plugin allows the user to experiment with other combinations of these basis functions, as well as providing several fractalization techniques for them. All of Lightwave's surface attributes (including bumps) can be adjusted using these functions. Envelopes are available for animating the surface attributes over time, and gradients are available for filtering the output from the cellular basis functions.
As the word "toy" in the title of this plugin suggests, it is mainly intended to allow a programmer or lightwave artist an opportunity to play with the concepts presented in the paper. I'm sure there are ways to speed up the texture generation, especially in the implementation of the fractalization routines. The interface for the plugin is not very artist friendly, and the user will need to be familiar with the basic concepts in the paper. But hopefully at least the envelope and gradient capabilities will make this plugin interesting enough to experiment with.
The CTBFtoy2 plugin is currently available for SGI (LW5.6), Intel (LW5.6 and LW6.0), Alpha (LW5.6), and Mac (LW5.6) platforms. Thanks to Bill Hemenway and Jonas Gustavsson for the Alpha compile, and Gordon Miller for the Macintosh compile. I really appreciate the time these people take to port the plugins!
To install CTBFtoy2, just copy the CTBFtoy2_56.p or CTBFtoy2_60.p file into your Lightwave plugins directory. Run the Lightwave Layout program and choose Add Plug-ins (for LW6.0, you will want to do this in Modeler as well). After selecting the appropriate CTBFtoy2 plugin from the file requester, you should see a message that says that 2 plugins were successfully added to the database. But in reality, it is just a single shader plugin and it's lwpanels interface. You should now find a "CTBFtoy" option under the Shader Plug-ins menu.
To use this plugin, you will definitely want to be familiar with the concepts presented in the Worley paper. I encourage you to read Michael Monks' online presentation about the paper, if you haven't already followed the link earlier on this page.
In summary, Steve's cellular basis functions calculate the distance (and gradient) between the point being shaded and "Feature Points" scattered throughout 3D space. To aid in this calculation, space is divided up into "voxels" that are aligned on the integer boundaries of the 3D coordinates. Each voxel will contain a certain number of randomly placed Feature points (this plugin allows 1 to 9 Feature Points per voxel). If we now consider the Current Point being shaded, there is some Feature Point that is closer to it than all the other Feature Points in space. F1 is defined to be the distance (actually the squared distance is used to avoid a square root calculation) from the Current Point to that closest Feature Point. At certain critical locations, the Current Point will be equidistant from two Feature Points. These cusp locations are the most interesting, and will provide the basis for the visual effects that this plugin can produce.
To extend this another step, F2 is defined to be the distance between the Current Point and the second closest Feature Point. To calculate F2, F1 must also be calculated, so F2 will return the distance to the first closest Feature Point as well as the second closest Feature Point. These nth closest Feature Point calculations can be made for any value of n, but Steve notes that the patterns are not as varied and interesting beyond F4, so this plugin will only allow F1 through F4 calculations and combinations.
If you have a VRML browser installed, you
can click on this image to display a VRML
scene that demonstrates a simple 3 x 3 grid of voxels. In this
example, each voxel contains a Feature Point density of 1 (the red
spheres), and the Current Point being shaded is represented by the
purple spiked ball. If F1 is calculated for the Current Point, the
basis function returns two values: the vector that points from the
Current Point to the closest Feature Point (the yellow arrow), and the
squared length of that vector. Calculating F2 would return 4 values:
the same 2 values returned from F1, the vector from the Current Point
to the second closest Feature Point (the blue arrow), and it's squared
length. Calculating F3 returns all 3 vectors displayed in the VRML file
and their corresponding squared lengths.
Once the basis function returns the vectors and their lengths, these values are used to shade the surface. The vectors are used as gradients to modify the surface's normal, giving this plugin it's bump texturing capability. The squared lengths are used to modify the other surface attributes such as color, luminosity, transparency and reflection.
The CTBFtoy interface is divided into several sections. Some of the sections will contain parameters and input requesters similar to Lightwave's built-in textures. Lightwave's envelope global (lwenvaccess.p) is available for changing many of the plugin's attributes over time. Lightwave's gradient global (gradient.p) can also used to filter the output of the cellular basis functions before being applied to the surface texture. This document will not explain all of the parameters and options available in the common areas of the interface, the Lightwave documentation should be consulted if clarifications are needed for the Lightwave globals and common parameters.
Here is a short description of each section of the interface. But once you understand the concept behind the calculation of the cellular basis functions, the best way to use this plugin is to experiment. The final portion of this web page will lead you through several hands-on experiments that demonstrate some of the capabilities available with this shader plugin.

This section contains the same parameters that Lightwave uses for it's built-in 3D textures, although visually, they are presented differently. This document will only describe one important feature of the Texture Opacity parameter. All other parameters are described in the texturing section of the Lightwave Layout Reference manual.
Texture Opacity allows you to blend this instance of the texture with previous layers of textures on your object. You may set this value to 0 if you want to turn off all calculations for this particular instance of the shader.
This section of the plugin is where the user can select the desired basis function, the fractalization type, an optional illumination model, the number of Feature Points in each voxel, and the distance metric that will be used.

The first row of horizontal choice buttons allow the user to select the desired Basis Function used in this instance of the shader. If F1 through F4 is chosen, then the following 4 requesters (C1 through C4) are ignored (the fifth, C5, is always used... more about this shortly). If Custom is chosen, then all 5 of the following requesters will be used to combine and customize the F1 through F4 basis functions.
F1*C1 + F2*C2 + F3*C3 + F4*C4 + C5 are the requesters used to combine the primitive basis functions into a new basis function. Each requester is labelled F1 *, F2 *, and so on, but I will refer to these requesters as C1 through C5. They are the constant multipliers for each of the 4 primitive basis functions. You should notice that if you choose Custom, the default equation that is used to form the new basis function is F2 - F1. But you may choose any combination of values in C1 through C4 to discover other interesting combinations of the primitive basis functions.
IMPORTANT NOTE: The C5 requester is special. As mentioned above, the C1 through C4 requesters are ignored unless the Custom button of the Basis Function parameter has been chosen. But if there is a value in the C5 requester, it will be used to modify ALL values coming from the basis functions, even when F1 through F4 is chosen. I chose to do this because it can help in locating the range of values being returned from the basis functions, and also allows the user to shift the return values into a more desired range. The last portion of the second exercise below shows an example of how to use a color gradient and the C5 requester to locate the range of values being returned from a fractalized basis function, and automatically shift that range closer to values between 0 and 1.
The Fractalize parameter is a toggle button which will show whether you have the fractalization capability of the plugin turned on or off. By clicking on the toggle button, a sub-panel will pop up with the available fractalization parameters. If the Fractal Type parameter is set to "None" when you exit this sub-panel, the Fractalize toggle button on the main panel will not be checked. If the Fractal Type parameter is set to any of the other options except None, the Fractalize toggle button will be checked when you exit the sub-panel.

The Fractal Type parameter allows the user to choose between several different types of fractal constructions. These fractal constructions are covered in detail by Ken Musgrave in "Texturing and Modeling: A Procedural Approach". Of course, these constructions use the cellular functions chosen by the user in the next section as basis functions rather than the more commonly used noise basis functions.
The Increment parameter is available for all types of fractal constructions. Increment is the fractal increment parameter. When Increment is equal to 1, the the fractalization is fairly smooth. As Increment approaches 0, the fractalization begins to resemble white noise.
Lacunarity is also available for all the fractal types. This parameter determines the frequency multiplier used in the chosen fractalization routine. This parameter affects the average size of the "gaps" in the texture pattern.
Octaves is used by all the fractal types to determine the number of loops used to calculate the final fractal. The chosen cellular basis function is calculated Octave number of times for each pixel being shaded by this shader. Usually, a small number of Octaves is sufficient (0 to 5), and setting this to a large value may cause extremely long rendering times.
The Offset parameter is only available for the Multifractal, Heterogeneous, Hybrid Multifractal, and Ridged Multifractal types. These are the multifractal routines, which are mainly thought of as fractals whose dimensions vary with location. When Offset is 0, the function is extremely heterogeneous (not the same everywhere). As its value is increased, the fractal exhibits more and more homogeneous behavior.
Threshold is only available for use with the Ridged Multifractal type. This parameter is used to weight successive contributions to the accumulated fractal result. It can be used to determine the frequency and sharpness of the ridges.
One final note about fractalization, mainly for programmers. Two of the Fractal Types, Turbulence and Ridged Multifractal, use the absolute value of the signal to calculate the final fractal results. However, since the cellular basis functions return squared distances, these values are never negative, and the absolute value function will have no effect. To counter this problem, I have defined a constant in the code (ABS_OFFSET found in ctbf_frctl.h) that will be subtracted from the signal before taking the absolute value. Currently only programmers can change this value and then recompile the plugin. If this proves to be a useful parameter for the user, it will need to be changed from a constant to a user defined value, and a new entry must be added to the user interface.
Shade by Feature Point is still experimental. This allows the user to color and shade the surface based on the coordinate of the Feature Point rather than the distance to the Feature Point. I do this a little differently (not necessarily better though) than Steve suggests in his paper, so I'm not sure what I'll do with this in the future. But for now, the coordinate of the current Feature Point is sent to a noise function. The return value from that noise function is used in place of the distance to the Feature Point in ALL remaining calculations, including fractalizations. I won't make any more comments about this, but I would be interested in hearing any thoughts you might have about this parameter.
Feature Point Density is a slider that will let you choose the number of Feature Points that will be located in a voxel. This slider will let you set the density from 0 to 8. In reality, the density sent to the cellular basis function is in the range from 1 to 9, but I could not get the lwpanels to work correctly if the minimum value for the slider wasn't equal to 0.
As Steve Worley points out in his paper, since you can scale the texture to any size, this parameter can be used to optimize the speed of the algorithm. If the density is low, more neighboring voxels will need to be visited when looking for the nth closest point. When the density is higher, less neighboring voxels will need to be checked, but there will be need to be more tests in the current voxel because there are more Feature Points located there.
Distance Metric allows the user to determine what type of calculation is used to determine the distance between the current point being shaded and the nearest feature points.
The image below shows the mathematical calculations used to determine D(a,b) which is the distance between two 3D points, a and b.

This section of the plugin will need very little explanation. This section allows you to use the return value of the basis function to modify the surface attributes. The attributes can be animated over time using envelopes, and the basis function's return value can be filtered with a gradient before the modification. There is a toggle button in front of every attribute which allows the user to select which attributes will be chosen to modify the surface.

Color is the only attribute that does not have envelope capability (I didn't see an easy way to combine both envelope and gradient access to vectors). If the Gradient button is not selected, the result from the basis function will be used to blend the surface color and the color shown in this requester. If the return value is 0, the surface color will be used. If the value is 1, the Color value will be used. Values in between will blend the two colors appropriately.
If the Color Gradient (G) button is selected, the surface color is ignored completely (unless you have the Texture Opacity at the top of the plugin's interface set to less than 100%, in which case the final color calculated here will be blended with the underlying surface color appropriately). The return value from the basis function can be used to look up the current color from the color gradient. Other possible Input Parameters for the color gradient include Time, Slope, Incidence Angle, X position, Y position, and Z position.
Other surface attributes can be modified based on time and gradient filtering of the basis function's results. Make sure the toggle button in front of the attribute is checked for the modifications to be enabled for that attribute. Also note that these values are NOT percentages like they are in Lightwave's main surface atrribute panel. Instead of values in the range of 0 - 100%, you should be using values in the range of 0 - 1. But don't be afraid to try values outside of that range as well.
If both the Envelope (E) button and the Gradient (G) button are NOT active, the value in the requester will be multiplied by the basis function's return value. The result of that multiplication will be used as the new value for that attribute.
If the Envelope (E) button is active, the value in the requester will be ignored, and the value used for the multiplication described above will be evaluated from the Envelope, based on the current frame/time.
If the Gradient (G) button is active, the basis function's return value (or any of the other Input Parameters which includes Slope, Incidence Angle, X position, Y position, and Z position) will be used to look up the filtered value from the Gradient. That filtered value will then be multiplied by the requester value or the Envelope value, depending on whether the Envelope is active or not. The result of that multiplication is used for the new surface attribute.
The Gradient and Envelope capability of the Bump Amp. parameter works in the same way as the other surface attributes. The final value returned from the evaluation of the envelope and gradient will be used to increase or decrease the amplitude of the bump vector.
The Illumination Models parameter was added to this version of CTBFtoy by Gordon Miller, who originally supplied the code for the Shades project. The toggle button on the main interface will indicate whether you have one of the two optional illumination models (Diffuse or Oren_Nayer) turned on or off. By clicking on the toggle button, a sub-panel will pop up with the available illumination parameters. If either of the "Use" toggle buttons on this panel are activated, the Illumination Models button on the main panel will be checked when you exit the Illumination sub-panel.

After choosing either one of the optional illumination models, the parameters for that model will be unghosted, and can be modified by the user. Most of the parameters are self-explanatory, however experimentation is the best way to determine how they affect the lighting of your surface. One of the interesting features available in this sub-panel is the ability to assign a color gradient to the Specular Color parameter. By clicking on the G button, the user may create a color gradient whose input parameter (the Basis Function's return value, Time, Slope, Incidence Angle, X position, Y position, or Z position) determines the specular color used by the shader.
The simplest way to get acquainted with the plugin's capabilities is to work your way through some simple exercises. This first exercise will only modify the bump mapping of an object. We will create some extremely varied types of F1 bumps by only changing the Bump Amp. and it's gradient filter. The examples below build on each other, so you only need to change the parameters listed in each step. The only notation that should need an explanation is that used for the Gradient keyframes. Each keyframe is listed as a pair: (Parameter, Value). Multiple keyframes will be seperated by a --> symbol: (Parameter0, Value0)--> (Parameter1, Value1). So, for example, this notation would describe the gradient shown directly below it:
Gradient - 3 Keys: (0.0, 1.0) --> (.3, 0.0) --> (1.0, 0.5)

So let's try our first exercise. Choose the CTBFtoy shader from the Surfaces->Advanced Options->Shader Plug-ins menu. Click on the plugin's Options button, then click off the Color toggle button near the bottom of the interface since this exercise will only create bump textures. Now change the appropriate values shown in each step below.
|
|
Now let's experiment with the Gradient. Actually, in this step we are going to make the gradient calculation constant by creating a single key gradient. Gradient: (0.0, 1.0) |
|
Bump Amp.: -2.0 Gradient: (0.0, 0.0) --> (1.0, 1.0) Filter: HiClip, 0 Gain, 0 Bias |
|
Now just change the filter's Gain and Bias. Filter: HiClip, 89 Gain, 42 Bias |
|
Bump Amp.: 4.0 Filter: Linear, 0 Gain, 0 Bias Gradient: (0.0, 1.0) --> (.133, 0.0) --> (1.0, 0.0) |
|
Gradient: 6 Keys approximately shown below
|
|
This next exercise will demonstrate how I use a color gradient to determine the approximate range of values being returned from the basis function. Sometimes, especially when fractalizing the basis function, the return values are no longer in the range of 0 to 1. By using these techniques you can determine the approximate range, which will help you choose values that should be used for modifying surface attributes or the bump amplitude.
| Start with a new instance of CTBFtoy to insure starting
this exercise with the plugin's default settings. Turn off the Bump Amp. parameter and make sure Color is enabled. Click the Color Gradient buClick on the Color Gradient button again, and make sure the chosen Mouse mode is "Edit". Click on the rightmost keyframe in the gradient (the red keyframe). Hold down the mouse while pointing to the Scale Keys button, then slide the mouse to the left. Scale the gradient down until the Key Parameter is approximately 0.5 You'll notice the purple areas of the preview are smaller, and now there is a trace of yellow showing. Still no red in the preview, so we haven't discovered the maximum range value yet, but we know it's less than 0.5 |
![]() |
| We'll return to the F1 basis function in the next step, but
at this point let's see what happens when we choose a different basis
function. Choose the F2 Basis Function Because the values from F2 are larger by definition than the values from F1, you can see traces of red in this preview, even though we did not change the Color Gradient. Since this preview contains both purple and red colors, we have discovered that the range of values for this basis function is approximately 0.0 to 0.5 |
![]() |
| Choose the F1 Basis Function again. Scale the Color Gradient keyframes down again, until the rightmost keyframe (Key Parameter) is approximately 0.35 We are now approaching a good approximation for the range of values being returned by this instance of the F1 basis function. |
![]() |
| Now for a short pop quiz :-)!! Increase the Feature Point Density to 8 Will this shift the basis function's return values toward the low end (purple) of the gradient or towards the high end (red)? Answer: Since there are more Feature Points in a cell, the current point is more likely to be closer to a Feature Point than it was before. Therefore, the return values (which represent the distance between the Current Point and the Feature Point) will be smaller. This will shift the colors being used to the lower end of the gradient. Sure enough, no more red or yellow in this preview. |
![]() |
| Many times, fractalization of the basis function will
drastically alter the range of values returned from the function.
Scaling the gradient is not necessarily the easiest way to begin
discovering the new range of values in this case. We'll use the C5
parameter to our advantage in this example. Click on the Fractalize button, and choose the "Turbulence" Fractal Type. This preview is definitely not very exciting. But since the sphere is completely red, we know that the values being returned from the function are all greater than 0.35, so we will want to reduce the return values to see if we can bring them back into a range near 0.0 to 1.0 Input the value -1.0 into the C5 requester. This will subtract 1 from the return value before it is passed to the gradient. Nope, still red. Try -5.0 in the C5 requester. Nope still red. Try -10.0. Ahhh, it's purple now, so we know the return values are somewhere between 5.0 and 10.0 |
![]() |
| Now it doesn't take long to discover that a C5 value
between -6.0 and -6.2 will relocate our return value between the
current gradient range of 0.0 to 0.35 Just remember, if you use the C5 parameter to discover some purple in your preview (which means you've found the minimum of the range), then you can go back to the Color Gradient and scale the keyframes like we did earlier in this exercise, to find the maximum value of the range. Knowing these minimum and maximum values of the basis function's return values will aid greatly in knowing what value to use when modifying the surface attributes. |
![]() |
This exercise did not create any useful textures, mainly because the hsv gradient is not real useful for many texture applications. But once you've discovered the range of values returned from the basis function, you can now replace the hsv gradient with a much more useful one. Here are 3 previews that use the exact same settings as 3 of the previews above. The only change is the use of a different Color Gradient (all 3 of these previews share the same Color Gradient). Can you match these previews to the ones above?

The last exercise is to experiment with the many other possibilities this plugin makes available to you.
| Lightwave's built-in Vein texture uses a custom basis function of F2 - F1. By choosing the Custom Basis Function option, this is the default formula used for the new basis function (-1.0 * F1 + 1.0 * F2). In this example, only the Color attribute is being modified. You will probably need to play with the Color Gradient to get the results shown here. | ![]() |
| Here, a constant Bump Amp. Gradient is used to
achieve a bumpy scale type pattern. The Color
attribute is still being modified as it was in the first example. Bump Amp. Gradient: (0.0, 1.0) |
![]() |
Using an inverted Bump Amp. Gradient and a different
"mortar" color in the Color Gradient, the
texture starts to look like a rock wall. |
![]() |
| I don't even remember what Custom Basis Function I used to create this preview, and it's probably not a texture many of you have needed :-). But it just demonstrates some of the varied patterns that are hiding in this plugin. It's also the first example that modifies some of the other surface attributes besides Color and Bump Amp. | ![]() |
| Lightwave's built-in Crumple texture uses a fractalized version of the F1 Basis Function. Click on the Fractalize button and choose fBm from the Fractal Type menu. You should also set the Bump Amp. to a constant gradient: (0.0, 1.0) [Remember, this is a single keyframe at (Parameter 0.0, Value 1.0), and can be easily achieved by clicking on the Bump Amp. G button, then immediately clicking OK in the Gradient panel.] | ![]() |
This is a similar F1 texture with fBm fractalization. But the Bump Amp. Gradient has been disabled. [You should realize that this causes the same result as creating a Bump Amp. Gradient with two keyframes: (0.0, 0.0) --> (1.0,1.0)] The Luminous, Diffuse, and Specular attributes have also been modified using the fractalized F1 basis function. |
![]() |
| An F2 Basis Function with the Turbulence Fractal Type. Don't forget, the higher the number of Octaves, the longer the render times will be for the texture. | ![]() |
| F4 Basis Function with Hybrid Multifractal. Other parameters such as the Increment, Lacunarity, and Offset on the Fractal panel were also modified. | ![]() |
Hopefully by now you have experimented enough with the interface, that you can discover new textures on your own. The next two sections don't contain pictures, but if you use the ideas presented above, you should be able to follow along and find ways to use these techniques on your own. I know this is not a very friendly interface for these textures, but the lack of friendliness allows the most flexibility for experimentation. I hope you will enjoy discovering new textures.
This feature will be used most often just with the F1 Basis Function, and there is a description of an example use of this in the next section.
Because of the experimental way I calculated this, this can get REALLY wild results when you choose to Fractalize and Shade by Feature Point in the same instance of the shader.
You can blend multiple of instances of CTBFtoy in several different ways.
The simplest is to create one instance of the shader that modifies some of the surface attributes, and then a second (or more) instance of the shader that modifies one or more of the other attributes. For example, the first instance of the CTBFtoy shader could define an F2 - F1 Basis Function that modifies the Bump Amp. and the Color attribute. Then a second instance of the shader could use an F1 Basis Function, with Shade by Feature Point enabled, that modifies the Diffuse and Specular attributes of the surface.
You can also blend multiple instances of the shader that modify the same attributes, or blend the CTBFtoy shader's output with the underlying surface attributes. Use the Texture Opacity and the Falloff parameters at the top of the interface to "layer" an instance of the shader with any of the underlying surface.
Just a few final thoughts... I know this shader needs some improvements. but I doubt I will ever be making many changes to the plugin (unless someone discovers some really drastic problems). But the source code is available, and I encourage programmers to do whatever they'd like with the code.
There are a couple of improvements I think would be great:
I'm sure there are others. If you send e-mail (marvinl AT email.arizona.edu), I will list any other suggestions here on the web page. I hope you enjoy the plugin, and will possibly even find it useful.