Procedural Texture Programming for Imagine 3D


This tutorial was designed for those that know how to program in C and C++, but might not be familiar with Windows programming. The tutorial will get the reader creating textures without doing any Windows programming. Then a more in depth look will be taken into how to use and modify Property Pages. Details are also given on the location and function of important code within the sample texture provided by Imagine 3D. The tutorial assumes that the reader is familiar with how to use Windows.

If you are having problems getting the Imagine 3D Texture SDK to work in Visual C++ version 5.0 you can download a VC++ version 5.0 compatible SDK here. Or if you are using Visual C++ version 6.0 try the VC++ version 6.0 compatible SDK here.

Third-Party Imagine 3D Textures


Other helpful information

Installing the sample texture files


In this section of the tutorial we will decompress the files contained in the Imagine 3D Texture Software Development Kit archive.


The file contains a file called This is where the source code for the example texture is located. If you are using WinZip, open and double click on the file name in the file list window. The other file, textures.doc, contains information about using the texture SDK. You should decompress textures.doc and refer to it later for more in-depth information.

The source code archive contains files for two versions of the sample texture. One is written in C, the other in C++ using the Microsoft Foundation Class libraries. We will be using the C++ project only for this tutorial.


To select the files we will need, hold down the Ctrl key and single click the left mouse button on the following files in the archive. The files are listed here in the order that they appear in the archive.


Then click on the “Extract command button.

Select a directory to place the files in and click on OK.

Quick-Start Guide to making textures for Imagine

(Or, how to change agates into checkerboards)


This section of the tutorial will get you started with the basics of using the Imagine 3D Texture SDK. You will load the project files, modify the sections of code that create the algorithmic texture, and compile a new texture. The sample texture in the SDK is the source code for the Agate texture supplied with Imagine for Windows. We will be modifying this in the following section to produce a checkerboard texture instead. Doing this requires us to edit the contents of only two files, agate.c and agate.txt


Load Microsoft Visual C++. (Other compilers may work, but none were tested for this article)

Click the Open icon. Navigate to the directory where you installed the Imagine texture files. Select the Files of type: setting to be "Workspaces (.dsw;.mdp)" Double click on the cppproj.mdp project file. The Imagine sample texture project will now open. Press F7 to build the project and make sure everything compiles all right. Note that the sample code will produce several warnings when compiling, but should produce no errors.


Modify the “friendly” name of the texture. This is the name that will appear on the Maps Property Page of an Imagine object when you open it’s Attributes dialog box.
To open the agate.c file, click on the “FileView” tab of the Workspace Browser on the left hand side of your screen. At the bottom of the file list tree you will see a file named agate.c. Double click on the file name and it will be loaded into the edit window on the right side of your screen. Go to line number 16 of agate.c and change the szName texture name value to “Checks”. The new code will look like this:

char	szName[18] = "Checks";


Now we will modify the part of the code that does the actual work of creating a texture. Its not surprising that the name of this function is called Work( ). The Work( ) function is passed a memory pointer to the list of parameters set by the user, this is the *params argument. The Work( ) function is also passed a pointer to a data structure that contains information about the attributes of the point where the ray has hit the object, this is the *patch argument. The *vector argument is a pointer to the coordinates on the object where the ray being traced has hit. The last argument, *axes, is a pointer to a data structure that contains information about the position and orientation of the axis of this texture.

We will now change the sample texture from Agate to a Checkerboard texture. Starting at line number 52 of agate.c, replace the Work( ) function with the following code:

void Work(float *params, PATCH *patch, VECTOR *vector, TFORM *axes)

	float X, Y, Z;

	X = vector->X / params[0]; // use size values for scaling
	Y = vector->Y / params[1];
	Z = vector->Z / params[2];

	if (X < 0.0) X -= 1.0; // Offset checks when they cross
	if (Y < 0.0) Y -= 1.0; // into negative quadrants.
	if (Z < 0.0) Z -= 1.0;

	if (((int)X + (int)Y + (int)Z) % 2) // Checks on even numbers
		patch->ptc_col.r = params[7] / 255; // Normalize colors
		patch->ptc_col.g = params[8] / 255; // into 0.0 to 1.0
		patch->ptc_col.b = params[9] / 255; // range

We are using the values set by the size controls on the Size Property Page to set the size of the checks. The original code assigns the X-Size, Y-Size, and Z-Size to the params[0], params[1], and params[2] variables, respectively. The Red, Green, and Blue values of the first color button on the Color Property Page are assigned to params[7], params[8], and params[9] respectively. The controls and the variables they are assigned to can be changed. But for the purpose of this lesson we are going to use them as they are. Our replacement code in the Work( ) function will now paint a checkerboard pattern on the object using the color set on the first color button of the Colors Property Page for this texture.

For more information on where the sample texture stores values from the Property Pages jump to the next section of this tutorial. For information on the contents of the patch structure you can jump ahead to the Patch Structure Reference.


Next we change the help information that is displayed in the Info Property Page of this texture. The help info is stored in the agate.txt text file. Double click on the agate.txt file and replace it’s contents with the following text:

Checks Texture
Type: Color.

This texture creates a checkerboard pattern


Color 1:  This sets the color of the checks.

Controls:  Nothing here to adjust.

Size:  These allow you to scale the overall size of the checks


Press F7 to build the texture. Copy the cppproj.itx file from the ReleaseP subdirectory of the texture project directory to the Textures directory of Imagine.

Start Imagine. Add a ground plane by selecting the Object | New | Ground menu item. Press the F1 key to select the new ground plane. Press F7 to open the Attributes dialog box. Click on the name tab for the Maps Property Page. Click on the “Add Texture” command button. Double click on the cppproj.itx texture file. Finally, click on the “Apply” command. You should now see something like this sample dialog box.

You can rename the file to MyChecks.itx or something else if you would like to keep this texture.

Where the values on Property Pages are stored


This section of the tutorial shows you how to view the Resources of the sample texture project. In Windows programming, Resources are where information about creating objects such as windows, icons, menus, and buttons for your program are stored. With Imagine textures, the values set by the user on the Property Pages of the texture interface are stored in an array of float values. The name of this array is params[]. Property Pages are nothing more than a special type of dialog window, so it’s settings are stored in the project’s resource file.


To view the resources for the texture, click on the Resource View tab of the Workspace browser on the left side of your screen. Click on the icon next to "cppproj resources" label, the resources tree will expand. Now click on the icon next to "Dialog" label, the resources for the property pages of this texture are revealed.


Double click on the "IDD_COLOR" label, the Color Property Page will be loaded into the edit window on the right side of your screen. The diagram to the left shows how the values of the Red, Green, and Blue components of each color button are stored in the params[] array. (The red text has been added to illustrate the variable names) Even though params[] is a float array, the Red, Green, and Blue components are integers in the range of 0 to 255. But, the Imagine rendering engine uses floating point color component values in the range of 0.0 to 1.0. So it is important to remember that before you use these values in your texture Work( ) function that you divide the value by 255 in order to normalize the value into the 0.0 to 1.0 range. We did this in the Quick Start Guide section of the tutorial to use the "Color 1" settings as our Checks color. Die-hard code optimizers will tell you that it is more efficient to do a multiply than a divide. So for better performance you may instead wish to multiply the color values by the reciprocal value of 255 which is 0.00392157

Here is how the values from the Controls Property Page are stored in the params[] array.

Here is how the values from the Size Property Page are stored in the params[] array.


If you are wondering exactly where in the source code the params[] array actually gets used, it is in the cpppages.cpp file. The function called CreatePages( ) loads the data values from the params[] array into the controls on the Property Pages. The StoreParams( ) function stores Property Page control data in the params[] array.

You can find class definitions and functions in your project very easily by using the Class Viewer. To go quickly to these two functions, click on the “Class Viewer” tab and expand the “Globals” folder. Here you will find a list of all the global functions in the texture sample project. Double clicking on a function name will load the source code file into the edit window and move to cursor to the beginning of that function.

Next we will learn how to modify the Property Page Resources.

Modifying Property Pages

(or, the brute-force method of creating texture interfaces)


This section of the tutorial will show the reader how to do basic manipulations of the existing Property Pages in the sample texture project. These methods can be used to do “brute-force” texture programming for Imagine. Many times custom textures can be made with only minor modifications to the existing Property Pages.


Open up the resource window for the Color Property Page. Double click on the “Color 1” text label. A Properties window will pop up for the text label. Changing the text in the “Caption:” edit box will change the displayed text on the Property Page. The “&” ampersand symbol in front of the 1 is what creates the underlined character, 1. Using this method will allow you to change the displayed names of all of the controls. Double clicking on the title bar of the Property Page will allow you to change the text displayed in the tab for the page. For example, you could change the name of the “Color” page to something like “Transparency“.


The Visible property will let you hide the control on a page by clearing the check mark. The control will still be visible at design time, but you won’t see it in the compiled program. You can use this method to hide controls that you won’t be using.

The image shown at the right illustrates how you can change the settings on multiple controls at the same time. Simply hold down the Shift key and click on all the controls you want to change (or drag a selection box around the controls). Then click the Right mouse button. A context menu will pop-up letting you select Properties off the menu. Any changes you make in the Multiple Selection Properties window after this will affect all the selected controls.

By hiding or modifying the text labels of the controls you can create you own custom textures without having to do any interface programming at all!

Next we will go into the details of the patch structure that is passed to the texture Work( ) function.

Patch Structure Reference


During rendering the texture’s Work( ) function is passed four arguments, one of which is a pointer to a data structure called patch. This section of the tutorial details the meaning of the variables in the patch structure. This information is also provided in the textures.doc file provided in the Imagine Texture Software Development Kit.


These are the X, Y, and Z world coordinates of the point where the ray being traced intersected with the object this texture was applied to. You can use this for textures that have variations based on where the object is located in the scene. If you need hit point coordinates that are relative to the texture’s axis use the vector argument that is passed to the Work( ) function instead. These values are read only. Modifying these values directly has no effect on the texture, but it’s a bad idea to do anyway.
This is the surface normal vector at the point that the ray intersected with the object. If this surface has Phong shading, then this normal has already been modified by the Phong algorithm. This tells you which direction the surface is facing at the hit point. You can modify this direction vector to create bump maps. If you do modify this, remember to re-normalize it, make sure (X * X) + (Y * Y) + (Z * Z) = 1.0
These are the Red, Green, and Blue values for the color of the pixel at the point that the ray intersected the object. They are floating point values and must be in the range of 0.0 to 1.0. Modifying these values will change the color of the object at this point.
These are the Red, Green, and Blue values for the reflectivity at this point on the object. They are floating point values and must be in the range of 0.0 to 1.0. Modifying these values will change the color and strength of reflections at this point. Anything reflected at this point will have it’s color filtered by these settings.
These are the Red, Green, and Blue values for the transparency at this point on the object. They are floating point values and must be in the range of 0.0 to 1.0. Modifying these values will change the transparency of the object. The color of anything beyond this point on the object will be filtered through these color settings.
These are the Red, Green, and Blue values for the specular color of the pixel at this point if it happens to lay within an area of specular highlight. They are floating point values and must be in the range of 0.0 to 1.0. Modifying these values will change the specular color of the object at this point. This setting works together with the patch->hard value which controls the hardness, or tightness of specular highlights for the object at this point.
patch->sbj_shp You can extract shape information from this value by using this formula:

shape = patch->sbj_shp & 0x07;
Where the result will mean:
0 Object is a perfect sphere.
3 A face on a faceted object.
4 A face on a particle.
5 A ground object.

If the result of patch->sbj_shp & 0x0400 is not zero, then this means the ray has hit a perfect sphere and if it continued, it would hit the sphere again. On perfect sphere objects this tells you whether the ray is entering or exiting the sphere. The value is read only.

patch->sbj_shd This tells the render engine whether this object can cast shadows on itself. In other words, if it has protrusions or concave features. Imagine sets this to non-zero only on perfect spheres. This value is read only.
First and second barycentric coordinates. Basically, barycentric coordinates are a coordinate system that is relative to the space a surface (in this case, a triangle) occupies. No matter how large or small the triangle may be in world coordinates, these coordinates have been normalized into a 0.0 to 1.0 range. This coordinate system is useful in graphics for calculating Phong shading because it tells you how far from the edges of the triangle you are. You can use that information to adjust the surface normal and thereby smooth the surface between the edges of the triangle. However, since phong shading is already done for you by the render engine, these numbers are not of much use. You could use these coordinates to make a texture that varies depending on how close a point is to the edge of the object’s triangle surfaces. Or you could turn off Phong shading on the object, and use these values to modify the surface normal, and thereby create your own smooth shading algorithm. These values are read only.
The third point of the barycentric coordinates can be calculated by this formula:
c3 = 1.0 - patch->ptc_c1 - patch->ptc_c2
The base coordinates are the starting point of the ray that hit this object. The unit values are a directional vector that points in the direction that the ray is going in. These values are in world coordinates and are read only.

You can use the ray’s directional vector patch->rayptr->unit in combination with the patch->ptc_nor surface normal to create effects like the Fakely texture provided with Imagine. These may also be useful in writing textures for lights.

patch->ptc_raydst This is the distance that the ray has traveled from it’s base point to the hit point on the object. This value is in world coordinates and is read only.
patch->foglen This lets you alter the Fog Length for the object. Changing this will only work if the object originally had a Fog Length setting greater than zero.
patch->ptc_shiny This sets the shininess value for the object. See the shininess tutorial for more information on what shininess is in Imagine. This value is in a 0.0 to 1.0 range.
patch->ptc_hard This sets the hardness value for the object. Hardness controls how tight specular reflections will be on the object. This value is in a 0.0 to 1.0 range.
patch->ptc_index This sets the index of refraction for the object. This value should be in a 1.0 to 3.6 range. An index of refraction less than 1.0 will crash the render engine. A value greater than 3.6 may cause unpredictable results and possibly hang the render engine.
patch->ptc_bright This sets the brightness level of the object. This value is in a 0.0 to 1.0 range. The higher this value is the less effect shadows and shading will have on the object. This is not the same as making the object a light source. No light will be cast into the scene by setting this value.
patch->ptc_txdata This is a pointer to an array of 16 bytes. This array is passed from texture to texture during rendering of an object with multiple textures. You can use it to pass information or data to the next texture to be processed for this object. There are no standards established for using this data array. It is most useful if you are planning on creating multiple textures that need to communicate with each other. For example, you could create a bump texture and have it pass bump height information to the next texture to use in mapping different colors to different bump heights. The method of passing this information is decided by you. If the end user has a texture made by someone else that uses this data array it may interfere with how you are passing data. To be safe, instruct users of your textures to place them together in the texture list with no other textures in between.
patch->ptc_rough This sets the roughness value for the object. Roughness randomly alters the surface normal for you. Higher numbers cause more severe alterations. Since this alteration is random there will be no continuity from frame to frame in an animation. Under these conditions the object surface will appear to sparkle or crawl. This value is in a 0.0 to 1.0 range.
These set the color and intensity of ambient light at this point on the object. This is the same concept as using a bitmap for an ambient lighting map in the attributes of an object. Traditionally ambient light maps are used to simulate radiosity (inter-reflection of light between objects). It can also be used to simulate translucent lighting effects like the glow of a candle stick near the flame. These values are in a 0.0 to 1.0 range.