home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / GRAPHICS / rayshade.lzh / DOC / texture.ms < prev   
Text File  |  1990-05-08  |  15KB  |  483 lines

  1. .\"
  2. .\" Brief tutorial on adding textures to rayshade.
  3. .\" Craig Kolb 10/89
  4. .\"
  5. .\" $Id: texture.ms,v 3.0.1.2 90/02/12 12:44:41 craig Exp $
  6. .\"
  7. .\" $Log:    texture.ms,v $
  8. .\" Revision 3.0.1.2  90/02/12  12:44:41  craig
  9. .\" patch4: Fixed minor formatting problems.
  10. .\" 
  11. .\" Revision 3.0.1.1  89/11/27  18:49:26  craig
  12. .\" patch2: Example texture now uses colormap to scale ambient & diffuse
  13. .\" patch2: components of surface color.
  14. .\" 
  15. .\" Revision 3.0  89/10/23  16:42:57  craig
  16. .\" Baseline for first official release.
  17. .\" 
  18. .de D(
  19. .DS
  20. .nr PS 8 
  21. .ps 8 
  22. .nr VS 12p
  23. .vs 12p
  24. .cs 1 20 
  25. ..
  26. .de D)
  27. .DE
  28. .nr PS 10
  29. .ps 10
  30. .nr VS 18p
  31. .vs 18p
  32. .cs 1
  33. ..
  34. .DS
  35. .ND October, 1989
  36. .RP
  37. .de ]H
  38. .nr PS 10
  39. .ps 10
  40. 'sp |0.5i-1
  41. .tl 'Rayshade Texture Tutorial'-%-'Draft'
  42. .ps
  43. .ft
  44. 'sp |1.0i
  45. .ns
  46. ..
  47. .wh0 ]H
  48. .pn 2
  49. .LP
  50. .TL
  51. Adding Textures to Rayshade
  52. .TL
  53. \fR\fIDraft\fR
  54. .AU
  55. Craig Kolb
  56. .AI
  57. Department of Mathematics
  58. Yale University
  59. New Haven, CT  06520
  60. .sp .5i
  61. .nr VS 18pts
  62. .PP
  63. This tutorial describes the process of adding new textures to
  64. rayshade.  Although the texturing interface is relatively
  65. straight-forward, it is difficult to see the "big picture" by 
  66. studying the source code, as changes must be made in a
  67. number of different files.  While this tutorial is primarily 
  68. meant for those interested getting their hands dirty, and
  69. assumes familiarity with solid texturing,
  70. it provides a overview of the design of at least one portion
  71. of rayshade and thus may be of interest even if you are not
  72. planning on making modifications.
  73. .LP
  74. Adding new textures to rayshade involves modifying at least
  75. four source files:
  76. .NH
  77. texture.h
  78. .LP
  79. A numerical type is given to the texture.  The routines to
  80. create a reference to the new texture and the texturing function
  81. itself are declared.
  82. .NH
  83. texture.c
  84. .LP
  85. At least two new routines are added.  The first creates and
  86. returns a reference to a texture of the given type, and the
  87. second performs the actual texturing.  In addition, an
  88. array of pointers to functions is modified to include the
  89. new texturing function.
  90. .NH
  91. input_lex.l
  92. .LP
  93. The keyword used to identify the new texture is added to
  94. the list of recognized keywords.
  95. .NH
  96. input_yacc.y
  97. .LP
  98. The new texture is added to the list of textures parsed by
  99. the yacc grammar.  The added code will call the routine
  100. which creates and returns a reference to the texture type
  101. which was defined in texture.c
  102. .PP
  103. In this tutorial, a new texture, 
  104. .I mountain,
  105. is added to rayshade.  This
  106. texture will modify the diffuse component of a surface as a function of the
  107. Z component of the point of intersection.  If the name of a colormap
  108. is given, an index into
  109. the colormap is computed and the corresponding color is used to scale
  110. the ambient and diffuse components of the surface.
  111. Otherwise, the ambient and diffuse components of the surface are simply scaled.
  112. To avoid strictly horizontal boundaries
  113. between colors when using a colormap, we add a bit of "noise" to the
  114. Z component of the point of intersection.  The magnitude and nature of the
  115. noise
  116. is defined by the user.
  117. .br
  118. .NR PS 12
  119. .ps 12
  120. .sp .5
  121. \fBThe Texture type\fR
  122. .nr PS 10
  123. .ps 10
  124. .sp .5
  125. .LP
  126. All textures in rayshade are referenced using a single Texture structure.
  127. This structure is defined as:
  128. .D(
  129. typedef struct Texture {
  130.         char type;              /* Texture type */
  131.         Surface *surf1;         /* Alternate surface */
  132.         double size;            /* Scale/size factor */
  133.         double *args;           /* Random arguments. */
  134.         Color *colormap;        /* Colormap */
  135.         Trans *trans;           /* Transformation matrices. */
  136.         struct Texture *next;   /* Pointer to next texture. */
  137. } Texture;
  138. .D)
  139. .LP
  140. The 
  141. .I type
  142. field is used by apply_textures() to determine which texturing
  143. function to call.  The
  144. .I trans
  145. field and 
  146. .I next
  147. field are used internally
  148. by rayshade.
  149. .LP
  150. The rest of the fields are for use by the texturing functions.
  151. .LP
  152. The
  153. .I args
  154. field provides a pointer to space for storing an arbitrary
  155. number of floating-point parameters.  The
  156. .I size
  157. field is a handy
  158. general-purpose floating-point value (the idea being that you get
  159. one parameter "free"
  160. with every Texture structure).
  161. The
  162. .I colormap
  163. field is generally used to store an array of Colors read from
  164. an ascii colormap file.
  165. The
  166. .I surf1
  167. field is often set to point to a secondary surface.  This is
  168. useful if the texture performs some sort of interpolation between two
  169. surfaces -- the first being the surface assigned to the object being textured
  170. and the second specified as an argument to the texture
  171. (e.g., the blotch texture). 
  172. for an example).
  173. .NH 0
  174. Modifying texture.h
  175. .LP
  176. The file texture.h contains a list of #defines which look something like:
  177. .D(
  178. #define CHECKER         0       /* Checkerboard */
  179. #define BLOTCH          1       /* Color blotches */
  180. #define BUMP            2       /* Bump mapping */
  181. #define MARBLE          3       /* marble texture */
  182. #define FBM             4       /* fBm texture */
  183. #define FBMBUMP         5       /* fBm bump map */
  184. #define WOOD            6       /* wood-like texture */
  185. .D)
  186. .LP
  187. These numerical types are used to identify the type of a given
  188. texture structure (via the
  189. .I type
  190. field in the Texture structure).
  191. We need to add a similar definition for our new texture.
  192. After the WOOD definition, we add:
  193. .D(
  194. #define MOUNTAIN        7       /* bad mountain texture */
  195. .D)
  196. .LP
  197. In addition, we must declare the two new functions which we will
  198. add to texture.c.  The first function, NewMountainText(), will
  199. return a pointer to a Texture structure:
  200. .D(
  201. Texture *NewMountainText();
  202. .D)
  203. The second routine, MountainText, returns nothing, but needs to be
  204. declared:
  205. .D(
  206. int MountainText();
  207. .D)
  208. .NH
  209. Modifying texture.c
  210. .LP
  211. Firstly, we must include the new texturing function in the array of
  212. texturing functions used by rayshade.  This array, indexed by texture
  213. type, is used by apply_textures() to call the correct texturing function.
  214. .LP
  215. So, we modify the textures[] definition to read:
  216. .D(
  217. int (*textures[])() =
  218.         {CheckerText, BlotchText, BumpText, MarbleText, fBmText, fBmBumpText,
  219.          WoodText, MountainText};
  220. .D)
  221. .LP
  222. Note that MOUNTAIN was defined to be 7, and that MountainText is texture
  223. number 7 (starting from 0) in the array.
  224. .LP
  225. Next, we need to write NewMountainText(), which will create and return a
  226. reference to the texture.  Our new texture will be a function of 5 parameters:
  227. .D(
  228.         scale           amount to scale \fINoise()\fR by
  229.         omega, lambda   fBm parameters
  230.         octaves         number of octaves of \fINoise()\fR in fBm
  231.         colormap        name of colormap file, if any
  232. .D)
  233. Thus, we add to the end of texture.c:
  234. .D(
  235. Texture *
  236. NewMountainText(scale, omega, lambda, octaves, mapname)
  237. double scale, omega, lambda;
  238. int octaves;
  239. char *mapname;
  240. {
  241.         /*
  242.          * Pointer to new texture.
  243.          */
  244.         Texture *text;
  245.  
  246.         /*
  247.          * Allocate new texture of type MOUNTAIN
  248.          */
  249.         text = new_texture(MOUNTAIN);
  250.         /*
  251.          * Allocate space to store omega, lambda and octaves.
  252.          */
  253.         text->args = (double *)Malloc(3 * sizeof(double));
  254.         text->args[0] = omega;
  255.         text->args[1] = lambda;
  256.         text->args[2] = (double)octaves;
  257.         /*
  258.          * scale is stored in 'size'.
  259.          */
  260.         text->size = scale;
  261.         /*
  262.          * If a colormap name was specified, read it into 'colormap'.
  263.          */
  264.         if (mapname != (char *)0)
  265.                 text->colormap = read_colormap(mapname);
  266.         /*
  267.          * All done -- return new texture.
  268.          */
  269.         return text;
  270. }
  271. .D)
  272. .LP
  273. Thus, NewMountainText is called with the desired parameters and a
  274. new Texture is returned.
  275. .LP
  276. Finally, we must write the routine which actually performs the texturing.
  277. Each texturing function is called by apply_textures() with the following
  278. arguments:
  279. .D(
  280.         text
  281.                 a pointer to the Texture being applied
  282.         pos
  283.                 a pointer to the coordinates of the point of intersection
  284.         norm
  285.                 a pointer to the surface normal at the point of intersection
  286.         surf
  287.                 the pointer to a copy of the surface of the object being
  288.                 textured  (a copy is used so that the surface can be
  289.                 modified for a given shading calculation without affecting
  290.                 subsequent calculations).
  291. .D)
  292. .LP
  293. Thus, we write:
  294. .D(
  295. MountainText(text, pos, norm, surf)
  296. Texture *text;
  297. Vector *pos, *norm;
  298. Surface *surf;
  299. {
  300.         double val;
  301.         int index;
  302.  
  303.         /*
  304.          * Compute value of fBm (fractional Brownian motion) for
  305.          * the given point.
  306.          */
  307.         val = fBm(pos, text->args[0], text->args[1], (int)text->args[2]); 
  308.         /*
  309.          * Scale the result as requested and add in the Z component of
  310.          * the point of intersection.  Note that in a better texture
  311.          * we'd probably have additional parameters to afford
  312.          * greater control of val.
  313.          */
  314.         val = pos->z + text->size * val;
  315.  
  316.         if (text->colormap) {
  317.                 /*
  318.                  * If we're using a colormap, compute an index into
  319.                  * the colormap and use the appropriate color as the
  320.                  * diffuse component of the surface.
  321.                  */
  322.                 index = 255. * val;
  323.                 if (index > 255)
  324.                         index = 255;
  325.                 if (index < 0)
  326.                         index = 0;
  327.                 surf->diff.r *= text->colormap[index].r;
  328.                 surf->diff.g *= text->colormap[index].g;
  329.                 surf->diff.b *= text->colormap[index].b;
  330.                 surf->amb.r *= text->colormap[index].r;
  331.                 surf->amb.g *= text->colormap[index].g;
  332.                 surf->amb.b *= text->colormap[index].b;
  333.         } else {
  334.                 /*
  335.                  * If there's no colormap, simply scale the diffuse
  336.                  * component. 
  337.                  */
  338.                 ScaleColor(val, surf->diff, &surf->diff);
  339.         ScaleColor(val, surf->amb, &surf->amb);
  340.         }
  341. }
  342. .D)
  343. .LP
  344. .NH
  345. input_lex.l
  346. .LP
  347. Now that we have the hard parts written, all that is left is making
  348. the parser recognize the new texture.  To do this, we first need to 
  349. add a keyword for our texture to the list of keywords recognized by
  350. rayshade.  This is done by editing input_lex.l.
  351. .LP
  352. The file input_lex.l contains, among other things, an alphabetical list of
  353. rayshade's keywords.  To add a new keyword, one simply follows the
  354. example of the other keywords.  Thus, we add the line:
  355. .D(
  356. mountain                     {return(tMOUNTAIN);}
  357. .D)
  358. between the lines defining 'mist' and 'object'.  This line directs
  359. lex to return the token tMOUNTAIN whenever the string "mountain" is
  360. encountered in an input file.
  361. .NH
  362. input_yacc.y
  363. .LP
  364. Finally, we need to write the code which will actually create an instance
  365. of the new texture by calling NewMountainText().  This is done in
  366. input_yacc.y.
  367. .LP
  368. In input_yacc.y, there are a series of lines which look something like:
  369. .D(
  370. %token tBACKGROUND tBLOTCH tBOX tBUMP tCONE tCYL tDIRECTIONAL 
  371. %token tENDDEF tEXTENDED tEYEP tFBM tFBMBUMP tFOG tFOV tGRID
  372. %token tHEIGHTFIELD tLIGHT tLIST tLOOKP tMARBLE tMAXDEPTH tMIST
  373. %token tOBJECT tOUTFILE
  374. %token tPLANE tPOINT tPOLY tROTATE
  375. %token tSCALE tSCREEN tSPHERE tSTARTDEF tSUPERQ tSURFACE tRESOLUTION
  376. %token tTHRESH tTRANSLATE tTRANSFORM tTRIANGLE tUP tENDFILE
  377. %token tTEXTURE tCHECKER tWOOD
  378. .D)
  379. .LP
  380. These lines declare the tokens returned by lex.  We need to declare
  381. tMOUNTAIN in a similar manner.  So, we change the last line to
  382. read:
  383. .D(
  384. %token tTEXTURE tCHECKER tWOOD tMOUNTAIN
  385. .D)
  386. Next, we need to call NewMountainText() in the proper place.  In input_yacc.y,
  387. there is a production which reads something like:
  388. .D(
  389. Texturetype     : tCHECKER String
  390.                 {
  391.                         $$ = NewCheckText($2);
  392.                 }
  393.                 | ...
  394.                 ...
  395.                 | tWOOD
  396.                 {
  397.                         $$ = NewWoodText();
  398.                 }
  399.                 ;
  400. .D)
  401. .LP
  402. These productions invoke the proper texture creation routine when appropriate.
  403. For example, when the keyword corresponding to tCHECKER is followed by
  404. a String, yacc will invoke NewCheckText() with the string (the name of
  405. a surface, in this case) as an argument.  The Yacc grammar understands
  406. the following datatypes, among others:
  407. .D(
  408.         String          a series of alphanumerics surrounded by
  409.                         white space (i.e., the string need not be quoted)
  410.         Fnumber         a floating-point number
  411.         tINT            an integer
  412.         Vector          a vector (x, y, z)
  413.         Color           a color (r, g, b)
  414. .D)
  415. To add a texture to the list of recognized textures, we change:
  416. .D(
  417.                 ...
  418.                 | tWOOD
  419.                 {
  420.                         $$ = NewWoodText();
  421.                 }
  422.                 ;
  423. .D)
  424. to:
  425. .D(
  426.                 | tWOOD
  427.                 {
  428.                         $$ = NewWoodText();
  429.                 }
  430.                 | tMOUNTAIN Fnumber Fnumber Fnumber tINT
  431.                 {
  432.                         $$ = NewMountainText($2, $3, $4, $5, (char *)0);
  433.                 }
  434.                 | tMOUNTAIN Fnumber Fnumber Fnumber tINT String
  435.                 {
  436.                         $$ = NewMountainText($2, $3, $4, $5, $6);
  437.                 }
  438.                 ;
  439. .D)
  440. .LP
  441. The first new production invokes NewMountainText() when the keyword
  442. associated with tMOUNTAIN ("mountain") appears in an appropriate place
  443. in the input file followed by
  444. .I four
  445. parameters (scale, omega,
  446. lambda, and octaves).  In this case, NewMountainText is passed a
  447. NULL pointer as the name of the colormap to use.
  448. This code creates a reference to a mountain texture that does
  449. .I not
  450. make
  451. use of a colormap.  So, this production is invoked whenever a line
  452. such as the following is encountered in the input file:
  453. .D(
  454.         texture mountain 0.2 0.5 2.0 6
  455. .D)
  456. .LP
  457. The second production works in a similar manner, except that it passes
  458. a colormap name to NewMountainText().  It handles lines such as:
  459. .D(
  460.         texture mountain 0.2 0.5 2.0 6 mountain.map
  461. .D)
  462. .NH
  463. Compiling
  464. .LP
  465. That, in theory, is all there is to it.  Run 'make' to recompile
  466. input_lex.o, input_yacc.o, and texture.o.
  467. .NH
  468. Testing
  469. .LP
  470. A good test input file for the new texture might be something like:
  471. .D(
  472. screen 512 512
  473. eyep 0 -10 0
  474. lookp 0 0 0
  475.  
  476. fov 20.
  477.  
  478. light 1.0 directional 1. -1. 1.
  479. surface boring .1 .1 .1 .8 .8 .8 0 0 0 0 0 0 0
  480.  
  481. sphere boring 1. 0. 0. 0. texture mountain 0.2 0.5 2.0 6 planet.map
  482. .D)
  483.