home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Crawly Crypt Collection 1
/
crawlyvol1.bin
/
graphics
/
rshade30
/
doc
/
texture.ms
< prev
Wrap
Text File
|
1991-06-12
|
15KB
|
483 lines
.\"
.\" Brief tutorial on adding textures to rayshade.
.\" Craig Kolb 10/89
.\"
.\" $Id: texture.ms,v 3.0.1.2 90/02/12 12:44:41 craig Exp $
.\"
.\" $Log: texture.ms,v $
.\" Revision 3.0.1.2 90/02/12 12:44:41 craig
.\" patch4: Fixed minor formatting problems.
.\"
.\" Revision 3.0.1.1 89/11/27 18:49:26 craig
.\" patch2: Example texture now uses colormap to scale ambient & diffuse
.\" patch2: components of surface color.
.\"
.\" Revision 3.0 89/10/23 16:42:57 craig
.\" Baseline for first official release.
.\"
.de D(
.DS
.nr PS 8
.ps 8
.nr VS 12p
.vs 12p
.cs 1 20
..
.de D)
.DE
.nr PS 10
.ps 10
.nr VS 18p
.vs 18p
.cs 1
..
.DS
.ND October, 1989
.RP
.de ]H
.nr PS 10
.ps 10
'sp |0.5i-1
.tl 'Rayshade Texture Tutorial'-%-'Draft'
.ps
.ft
'sp |1.0i
.ns
..
.wh0 ]H
.pn 2
.LP
.TL
Adding Textures to Rayshade
.TL
\fR\fIDraft\fR
.AU
Craig Kolb
.AI
Department of Mathematics
Yale University
New Haven, CT 06520
.sp .5i
.nr VS 18pts
.PP
This tutorial describes the process of adding new textures to
rayshade. Although the texturing interface is relatively
straight-forward, it is difficult to see the "big picture" by
studying the source code, as changes must be made in a
number of different files. While this tutorial is primarily
meant for those interested getting their hands dirty, and
assumes familiarity with solid texturing,
it provides a overview of the design of at least one portion
of rayshade and thus may be of interest even if you are not
planning on making modifications.
.LP
Adding new textures to rayshade involves modifying at least
four source files:
.NH
texture.h
.LP
A numerical type is given to the texture. The routines to
create a reference to the new texture and the texturing function
itself are declared.
.NH
texture.c
.LP
At least two new routines are added. The first creates and
returns a reference to a texture of the given type, and the
second performs the actual texturing. In addition, an
array of pointers to functions is modified to include the
new texturing function.
.NH
input_lex.l
.LP
The keyword used to identify the new texture is added to
the list of recognized keywords.
.NH
input_yacc.y
.LP
The new texture is added to the list of textures parsed by
the yacc grammar. The added code will call the routine
which creates and returns a reference to the texture type
which was defined in texture.c
.PP
In this tutorial, a new texture,
.I mountain,
is added to rayshade. This
texture will modify the diffuse component of a surface as a function of the
Z component of the point of intersection. If the name of a colormap
is given, an index into
the colormap is computed and the corresponding color is used to scale
the ambient and diffuse components of the surface.
Otherwise, the ambient and diffuse components of the surface are simply scaled.
To avoid strictly horizontal boundaries
between colors when using a colormap, we add a bit of "noise" to the
Z component of the point of intersection. The magnitude and nature of the
noise
is defined by the user.
.br
.NR PS 12
.ps 12
.sp .5
\fBThe Texture type\fR
.nr PS 10
.ps 10
.sp .5
.LP
All textures in rayshade are referenced using a single Texture structure.
This structure is defined as:
.D(
typedef struct Texture {
char type; /* Texture type */
Surface *surf1; /* Alternate surface */
double size; /* Scale/size factor */
double *args; /* Random arguments. */
Color *colormap; /* Colormap */
Trans *trans; /* Transformation matrices. */
struct Texture *next; /* Pointer to next texture. */
} Texture;
.D)
.LP
The
.I type
field is used by apply_textures() to determine which texturing
function to call. The
.I trans
field and
.I next
field are used internally
by rayshade.
.LP
The rest of the fields are for use by the texturing functions.
.LP
The
.I args
field provides a pointer to space for storing an arbitrary
number of floating-point parameters. The
.I size
field is a handy
general-purpose floating-point value (the idea being that you get
one parameter "free"
with every Texture structure).
The
.I colormap
field is generally used to store an array of Colors read from
an ascii colormap file.
The
.I surf1
field is often set to point to a secondary surface. This is
useful if the texture performs some sort of interpolation between two
surfaces -- the first being the surface assigned to the object being textured
and the second specified as an argument to the texture
(e.g., the blotch texture).
for an example).
.NH 0
Modifying texture.h
.LP
The file texture.h contains a list of #defines which look something like:
.D(
#define CHECKER 0 /* Checkerboard */
#define BLOTCH 1 /* Color blotches */
#define BUMP 2 /* Bump mapping */
#define MARBLE 3 /* marble texture */
#define FBM 4 /* fBm texture */
#define FBMBUMP 5 /* fBm bump map */
#define WOOD 6 /* wood-like texture */
.D)
.LP
These numerical types are used to identify the type of a given
texture structure (via the
.I type
field in the Texture structure).
We need to add a similar definition for our new texture.
After the WOOD definition, we add:
.D(
#define MOUNTAIN 7 /* bad mountain texture */
.D)
.LP
In addition, we must declare the two new functions which we will
add to texture.c. The first function, NewMountainText(), will
return a pointer to a Texture structure:
.D(
Texture *NewMountainText();
.D)
The second routine, MountainText, returns nothing, but needs to be
declared:
.D(
int MountainText();
.D)
.NH
Modifying texture.c
.LP
Firstly, we must include the new texturing function in the array of
texturing functions used by rayshade. This array, indexed by texture
type, is used by apply_textures() to call the correct texturing function.
.LP
So, we modify the textures[] definition to read:
.D(
int (*textures[])() =
{CheckerText, BlotchText, BumpText, MarbleText, fBmText, fBmBumpText,
WoodText, MountainText};
.D)
.LP
Note that MOUNTAIN was defined to be 7, and that MountainText is texture
number 7 (starting from 0) in the array.
.LP
Next, we need to write NewMountainText(), which will create and return a
reference to the texture. Our new texture will be a function of 5 parameters:
.D(
scale amount to scale \fINoise()\fR by
omega, lambda fBm parameters
octaves number of octaves of \fINoise()\fR in fBm
colormap name of colormap file, if any
.D)
Thus, we add to the end of texture.c:
.D(
Texture *
NewMountainText(scale, omega, lambda, octaves, mapname)
double scale, omega, lambda;
int octaves;
char *mapname;
{
/*
* Pointer to new texture.
*/
Texture *text;
/*
* Allocate new texture of type MOUNTAIN
*/
text = new_texture(MOUNTAIN);
/*
* Allocate space to store omega, lambda and octaves.
*/
text->args = (double *)Malloc(3 * sizeof(double));
text->args[0] = omega;
text->args[1] = lambda;
text->args[2] = (double)octaves;
/*
* scale is stored in 'size'.
*/
text->size = scale;
/*
* If a colormap name was specified, read it into 'colormap'.
*/
if (mapname != (char *)0)
text->colormap = read_colormap(mapname);
/*
* All done -- return new texture.
*/
return text;
}
.D)
.LP
Thus, NewMountainText is called with the desired parameters and a
new Texture is returned.
.LP
Finally, we must write the routine which actually performs the texturing.
Each texturing function is called by apply_textures() with the following
arguments:
.D(
text
a pointer to the Texture being applied
pos
a pointer to the coordinates of the point of intersection
norm
a pointer to the surface normal at the point of intersection
surf
the pointer to a copy of the surface of the object being
textured (a copy is used so that the surface can be
modified for a given shading calculation without affecting
subsequent calculations).
.D)
.LP
Thus, we write:
.D(
MountainText(text, pos, norm, surf)
Texture *text;
Vector *pos, *norm;
Surface *surf;
{
double val;
int index;
/*
* Compute value of fBm (fractional Brownian motion) for
* the given point.
*/
val = fBm(pos, text->args[0], text->args[1], (int)text->args[2]);
/*
* Scale the result as requested and add in the Z component of
* the point of intersection. Note that in a better texture
* we'd probably have additional parameters to afford
* greater control of val.
*/
val = pos->z + text->size * val;
if (text->colormap) {
/*
* If we're using a colormap, compute an index into
* the colormap and use the appropriate color as the
* diffuse component of the surface.
*/
index = 255. * val;
if (index > 255)
index = 255;
if (index < 0)
index = 0;
surf->diff.r *= text->colormap[index].r;
surf->diff.g *= text->colormap[index].g;
surf->diff.b *= text->colormap[index].b;
surf->amb.r *= text->colormap[index].r;
surf->amb.g *= text->colormap[index].g;
surf->amb.b *= text->colormap[index].b;
} else {
/*
* If there's no colormap, simply scale the diffuse
* component.
*/
ScaleColor(val, surf->diff, &surf->diff);
ScaleColor(val, surf->amb, &surf->amb);
}
}
.D)
.LP
.NH
input_lex.l
.LP
Now that we have the hard parts written, all that is left is making
the parser recognize the new texture. To do this, we first need to
add a keyword for our texture to the list of keywords recognized by
rayshade. This is done by editing input_lex.l.
.LP
The file input_lex.l contains, among other things, an alphabetical list of
rayshade's keywords. To add a new keyword, one simply follows the
example of the other keywords. Thus, we add the line:
.D(
mountain {return(tMOUNTAIN);}
.D)
between the lines defining 'mist' and 'object'. This line directs
lex to return the token tMOUNTAIN whenever the string "mountain" is
encountered in an input file.
.NH
input_yacc.y
.LP
Finally, we need to write the code which will actually create an instance
of the new texture by calling NewMountainText(). This is done in
input_yacc.y.
.LP
In input_yacc.y, there are a series of lines which look something like:
.D(
%token tBACKGROUND tBLOTCH tBOX tBUMP tCONE tCYL tDIRECTIONAL
%token tENDDEF tEXTENDED tEYEP tFBM tFBMBUMP tFOG tFOV tGRID
%token tHEIGHTFIELD tLIGHT tLIST tLOOKP tMARBLE tMAXDEPTH tMIST
%token tOBJECT tOUTFILE
%token tPLANE tPOINT tPOLY tROTATE
%token tSCALE tSCREEN tSPHERE tSTARTDEF tSUPERQ tSURFACE tRESOLUTION
%token tTHRESH tTRANSLATE tTRANSFORM tTRIANGLE tUP tENDFILE
%token tTEXTURE tCHECKER tWOOD
.D)
.LP
These lines declare the tokens returned by lex. We need to declare
tMOUNTAIN in a similar manner. So, we change the last line to
read:
.D(
%token tTEXTURE tCHECKER tWOOD tMOUNTAIN
.D)
Next, we need to call NewMountainText() in the proper place. In input_yacc.y,
there is a production which reads something like:
.D(
Texturetype : tCHECKER String
{
$$ = NewCheckText($2);
}
| ...
...
| tWOOD
{
$$ = NewWoodText();
}
;
.D)
.LP
These productions invoke the proper texture creation routine when appropriate.
For example, when the keyword corresponding to tCHECKER is followed by
a String, yacc will invoke NewCheckText() with the string (the name of
a surface, in this case) as an argument. The Yacc grammar understands
the following datatypes, among others:
.D(
String a series of alphanumerics surrounded by
white space (i.e., the string need not be quoted)
Fnumber a floating-point number
tINT an integer
Vector a vector (x, y, z)
Color a color (r, g, b)
.D)
To add a texture to the list of recognized textures, we change:
.D(
...
| tWOOD
{
$$ = NewWoodText();
}
;
.D)
to:
.D(
| tWOOD
{
$$ = NewWoodText();
}
| tMOUNTAIN Fnumber Fnumber Fnumber tINT
{
$$ = NewMountainText($2, $3, $4, $5, (char *)0);
}
| tMOUNTAIN Fnumber Fnumber Fnumber tINT String
{
$$ = NewMountainText($2, $3, $4, $5, $6);
}
;
.D)
.LP
The first new production invokes NewMountainText() when the keyword
associated with tMOUNTAIN ("mountain") appears in an appropriate place
in the input file followed by
.I four
parameters (scale, omega,
lambda, and octaves). In this case, NewMountainText is passed a
NULL pointer as the name of the colormap to use.
This code creates a reference to a mountain texture that does
.I not
make
use of a colormap. So, this production is invoked whenever a line
such as the following is encountered in the input file:
.D(
texture mountain 0.2 0.5 2.0 6
.D)
.LP
The second production works in a similar manner, except that it passes
a colormap name to NewMountainText(). It handles lines such as:
.D(
texture mountain 0.2 0.5 2.0 6 mountain.map
.D)
.NH
Compiling
.LP
That, in theory, is all there is to it. Run 'make' to recompile
input_lex.o, input_yacc.o, and texture.o.
.NH
Testing
.LP
A good test input file for the new texture might be something like:
.D(
screen 512 512
eyep 0 -10 0
lookp 0 0 0
fov 20.
light 1.0 directional 1. -1. 1.
surface boring .1 .1 .1 .8 .8 .8 0 0 0 0 0 0 0
sphere boring 1. 0. 0. 0. texture mountain 0.2 0.5 2.0 6 planet.map
.D)