/**************************************************************************** * pigment.c * * This module implements solid texturing functions that modify the color * transparency of an object's surface. * * from Persistence of Vision Raytracer * Copyright 1993 Persistence of Vision Team *--------------------------------------------------------------------------- * NOTICE: This source code file is provided so that users may experiment * with enhancements to POV-Ray and to port the software to platforms other * than those supported by the POV-Ray Team. There are strict rules under * which you are permitted to use this file. The rules are in the file * named POVLEGAL.DOC which should be distributed with this file. If * POVLEGAL.DOC is not available or for more info please contact the POV-Ray * Team Coordinator by leaving a message in CompuServe's Graphics Developer's * Forum. The latest version of POV-Ray may be found there as well. * * This program is based on the popular DKB raytracer version 2.12. * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * *****************************************************************************/ /* Some texture ideas garnered from SIGGRAPH '85 Volume 19 Number 3, "An Image Synthesizer" By Ken Perlin. Further Ideas Garnered from "The RenderMan Companion" (Addison Wesley). */ #include "frame.h" #include "vector.h" #include "povproto.h" #include "texture.h" COLOUR_MAP_ENTRY Black_White_Entries[2]= {{0.0, {0.0,0.0,0.0,0.0}}, {1.0, {1.0,1.0,1.0,0.0}}}; COLOUR_MAP Gray_Default_Map = { 2, FALSE, -1, Black_White_Entries}; COLOUR_MAP_ENTRY Bozo_Entries[6]= {{0.4, {1.0,1.0,1.0,0.0}}, {0.4, {0.0,1.0,0.0,0.0}}, {0.6, {0.0,1.0,0.0,0.0}}, {0.6, {0.0,0.0,1.0,0.0}}, {0.8, {0.0,0.0,1.0,0.0}}, {0.8, {1.0,0.0,0.0,0.0}}}; COLOUR_MAP Bozo_Default_Map = { 6, FALSE, -1, Bozo_Entries}; COLOUR_MAP_ENTRY Wood_Entries[2]= {{0.6, {0.666,0.312, 0.2, 0.0}}, {0.6, {0.4, 0.1333,0.066,0.0}}}; COLOUR_MAP Wood_Default_Map = { 2, FALSE, -1, Wood_Entries}; COLOUR_MAP_ENTRY Mandel_Entries[5]= {{0.001, {0.0,0.0,0.0,0.0}}, {0.001, {0.0,1.0,1.0,0.0}}, {0.012, {1.0,1.0,0.0,0.0}}, {0.015, {1.0,0.0,1.0,0.0}}, {0.1, {0.0,1.0,1.0,0.0}}}; COLOUR_MAP Mandel_Default_Map = { 5, FALSE, -1, Mandel_Entries}; COLOUR_MAP_ENTRY Agate_Entries[6]= {{0.0, {1.0, 1.0, 1.0, 0.0}}, {0.5, {0.95,0.75,0.5, 0.0}}, {0.5, {0.9, 0.7, 0.5, 0.0}}, {0.6, {0.9, 0.7, 0.4, 0.0}}, {0.6, {1.0, 0.7, 0.4, 0.0}}, {1.0, {0.6, 0.3, 0.0, 0.0}}}; COLOUR_MAP Agate_Default_Map = { 6, FALSE, -1, Agate_Entries}; COLOUR_MAP_ENTRY Radial_Entries[4]= {{0.0, {0.0,1.0,1.0,0.0}}, {0.333, {1.0,1.0,0.0,0.0}}, {0.666, {1.0,0.0,1.0,0.0}}, {1.0, {0.0,1.0,1.0,0.0}}}; COLOUR_MAP Radial_Default_Map = { 4, FALSE, -1, Radial_Entries}; COLOUR_MAP_ENTRY Marble_Entries[3]= {{0.0, {0.9,0.8, 0.8, 0.0}}, {0.9, {0.9,0.08,0.08,0.0}}, {0.9, {0.0,0.0,0.0,0.0}}}; COLOUR_MAP Marble_Default_Map = { 3, FALSE, -1, Marble_Entries}; void agate (x, y, z, Pigment, colour) DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { register DBL noise, turb; COLOUR New_Colour; turb = Turbulence(x, y, z,Pigment->omega, Pigment->lambda,Pigment->Octaves) * Pigment->Agate_Turb_Scale; noise = 0.5 * (cycloidal(1.3 * turb + 1.1 * z) + 1.0); if (noise <= 0.0) noise = 0.0; else { noise = (noise > 1.0 ? 1.0 : noise); noise = pow(noise, 0.77); } Compute_Colour (&New_Colour, Pigment, noise); colour -> Red += New_Colour.Red; colour -> Green += New_Colour.Green; colour -> Blue += New_Colour.Blue; colour -> Filter += New_Colour.Filter; } void bozo (x, y, z, Pigment, colour) DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { register DBL noise; COLOUR New_Colour; noise = Noise (x, y, z); Compute_Colour (&New_Colour, Pigment, noise); colour -> Red += New_Colour.Red; colour -> Green += New_Colour.Green; colour -> Blue += New_Colour.Blue; colour -> Filter += New_Colour.Filter; } void brick (x, y, z, Pigment, colour) DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { /* Disabled. Needs work. DBL xr, yr, zr; xr = fabs(fmod(x, 1.0)); yr = fabs(fmod(y, 1.0)); zr = fabs(fmod(z, 1.0)); *colour = *Pigment -> Colour2; if (xr > 0 && xr < Pigment-> Mortar) { *colour = *Pigment -> Colour1; return; } if (yr > 0 && yr < Pigment-> Mortar) { *colour = *Pigment -> Colour1; return; } if (zr > 0 && zr < Pigment-> Mortar) *colour = *Pigment -> Colour1; */ return; } void checker (x, y, z, Pigment, colour) DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { DBL value; COLOUR_MAP_ENTRY *Cur; int Num_Entries = Pigment->Colour_Map->Number_Of_Entries; int Fudge = (int)((DBL)Num_Entries/Pigment->Frequency); x += Small_Tolerance; y += Small_Tolerance; z += Small_Tolerance; x -= FLOOR(x/Fudge) * Fudge-Small_Tolerance; y -= FLOOR(y/Fudge) * Fudge-Small_Tolerance; z -= FLOOR(z/Fudge) * Fudge-Small_Tolerance; value = (FLOOR(x)+FLOOR(y)+FLOOR(z))* Pigment->Frequency; value = fmod(FLOOR(value + (int)(Pigment->Phase)), (DBL) Num_Entries); Cur = &(Pigment->Colour_Map->Colour_Map_Entries[0]); while (value > Cur->value) Cur++; colour->Red += Cur->Colour.Red; colour->Green += Cur->Colour.Green; colour->Blue += Cur->Colour.Blue; colour->Filter += Cur->Colour.Filter; } /* Color Gradient Pigment - gradient based on the fractional values of x, y or z, based on whether or not the given directional vector is a 1.0 or a 0.0. Note - ONLY works with colour maps, preferably one that is circular - i.e. the last defined colour (value 1.001) is the same as the first colour (with a value of 0.0) in the map. The basic concept of this is from DBW Render, but Dave Wecker's only supports simple Y axis gradients. */ void gradient (x, y, z, Pigment, colour) DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { COLOUR New_Colour; DBL value = 0.0; if (Pigment -> Colour_Gradient.x != 0.0) { x = FABS(x); value += x - FLOOR(x); /* obtain fractional X component */ } if (Pigment -> Colour_Gradient.y != 0.0) { y = FABS(y); value += y - FLOOR(y); /* obtain fractional Y component */ } if (Pigment -> Colour_Gradient.z != 0.0) { z = FABS(z); value += z - FLOOR(z); /* obtain fractional Z component */ } value = ((value > 1.0) ? fmod(value, 1.0) : value); /* clamp to 1.0 */ Compute_Colour (&New_Colour, Pigment, value); colour -> Red += New_Colour.Red; colour -> Green += New_Colour.Green; colour -> Blue += New_Colour.Blue; colour -> Filter += New_Colour.Filter; } /* Granite - kind of a union of the "spotted" and the "dented" textures, using a 1/f fractal noise function for color values. Typically used w/ small scaling values. Should work with colour maps for pink granite... */ void granite (x, y, z, Pigment, colour) DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { register int i; register DBL temp, noise = 0.0, freq = 1.0; COLOUR New_Colour; for (i = 0; i < 6 ; freq *= 2.0, i++) { temp = 0.5 - Noise (x * 4 * freq, y * 4 * freq, z * 4 * freq); temp = FABS(temp); noise += temp / freq; } Compute_Colour (&New_Colour, Pigment, noise); colour -> Red += New_Colour.Red; colour -> Green += New_Colour.Green; colour -> Blue += New_Colour.Blue; colour -> Filter += New_Colour.Filter; } void mandel (x, y, z, Pigment, colour) DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { DBL a,b,cf,a2,b2; int it_max,col; COLOUR thepoint; a = x; a2 = a*a; b = y; b2 = b*b; it_max = Pigment->Iterations; for (col=0;col 4.0) break; } cf = (DBL)col / it_max; Compute_Colour (&thepoint, Pigment, cf); colour->Red +=thepoint.Red; colour->Green +=thepoint.Green; colour->Blue +=thepoint.Blue; colour->Filter +=thepoint.Filter; } void marble (x, y, z, Pigment, colour) DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { register DBL noise, turb; COLOUR New_Colour; if ((Pigment->Flags) & HAS_TURB) turb = Turbulence(x, y, z,Pigment->omega,Pigment->lambda,Pigment->Octaves) * Pigment->Turbulence.x; else turb = 0.0; noise = Triangle_Wave(x + turb); Compute_Colour (&New_Colour, Pigment, noise); colour -> Red += New_Colour.Red; colour -> Green += New_Colour.Green; colour -> Blue += New_Colour.Blue; colour -> Filter += New_Colour.Filter; } void radial (x, y, z, Pigment, colour) DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { register DBL value; COLOUR New_Colour; if ( (fabs(x)<0.001) && (fabs(z)<0.001)) value = 0.25; else value = 0.25+(atan2(x,z)+M_PI)/(2*M_PI); Compute_Colour (&New_Colour, Pigment, value); colour -> Red += New_Colour.Red; colour -> Green += New_Colour.Green; colour -> Blue += New_Colour.Blue; colour -> Filter += New_Colour.Filter; } /* With a little reflectivity and brilliance, can look like organ pipe metal. With tiny scaling values can look like masonry or concrete. Works with color maps. */ void spotted (x, y, z, Pigment, colour) DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { register DBL noise; COLOUR New_Colour; noise = Noise (x, y, z); Compute_Colour (&New_Colour, Pigment, noise); colour -> Red += New_Colour.Red; colour -> Green += New_Colour.Green; colour -> Blue += New_Colour.Blue; colour -> Filter += New_Colour.Filter; } void wood (x, y, z, Pigment, colour) DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { register DBL noise, length; VECTOR WoodTurbulence; VECTOR point; COLOUR New_Colour; DTurbulence (&WoodTurbulence, x, y, z,Pigment->omega,Pigment->lambda,Pigment->Octaves); point.x = cycloidal((x + WoodTurbulence.x) * Pigment -> Turbulence.x); point.y = cycloidal((y + WoodTurbulence.y) * Pigment -> Turbulence.y); point.z = 0.0; point.x += x; point.y += y; /* point.z += z; Deleted per David Buck -- BP 7/91 */ VLength (length, point); noise = Triangle_Wave(length); Compute_Colour (&New_Colour, Pigment, noise); colour -> Red += New_Colour.Red; colour -> Green += New_Colour.Green; colour -> Blue += New_Colour.Blue; colour -> Filter += New_Colour.Filter; } /* Two new pigments by Scott Taylor LEOPARD & ONION */ void leopard (x, y, z, Pigment, colour) /* SWT 7/18/91 */ DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { /* The variable noise is not used as noise in this function */ register DBL noise,temp1,temp2,temp3; COLOUR New_Colour; /* This form didn't work with Zortech 386 compiler */ /* noise = Sqr((sin(x)+sin(y)+sin(z))/3); */ /* So we break it down. */ temp1 = sin(x); temp2 = sin(y); temp3 = sin(z); noise = Sqr((temp1+temp2+temp3)/3); Compute_Colour (&New_Colour, Pigment, noise); colour -> Red += New_Colour.Red; colour -> Green += New_Colour.Green; colour -> Blue += New_Colour.Blue; colour -> Filter += New_Colour.Filter; } void onion (x, y, z, Pigment, colour) /* SWT 7/18/91 */ DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { /* The variable noise is not used as noise in this function */ register DBL noise; COLOUR New_Colour; /* This ramp goes 0-1,1-0,0-1,1-0... noise = (fmod(SQRT(Sqr(x)+Sqr(y)+Sqr(z)),2.0)-1.0); if (noise<0.0) {noise = 0.0-noise;} */ /* This ramp goes 0-1,0-1,0-1,0-1... */ noise = (fmod(SQRT(Sqr(x)+Sqr(y)+Sqr(z)),1.0)); Compute_Colour (&New_Colour, Pigment, noise); colour -> Red += New_Colour.Red; colour -> Green += New_Colour.Green; colour -> Blue += New_Colour.Blue; colour -> Filter += New_Colour.Filter; } /* TriHex pattern -- Ernest MacDougal Campbell III (EMC3) 11/23/92 * * Creates a hexagon pattern in the XZ plane. * * This algorithm is hard to explain. First it scales the point to make * a few of the later calculations easier, then maps some points to be * closer to the Origin. A small area in the first quadrant is subdivided * into a 6 x 6 grid. The position of the point mapped into that grid * determines its color. For some points, just the grid location is enough, * but for others, we have to calculate which half of the block it's in * (this is where the atan2() function comes in handy). */ #define xfactor 0.5; /* each triangle is split in half for the grid */ #define zfactor 0.866025404; /* sqrt(3)/2 -- Height of an equilateral triangle */ void hexagon (x, y, z, Pigment, colour) DBL x, y, z; PIGMENT *Pigment; COLOUR *colour; { int xm, zm; DBL xs, zs, xl, zl, value; int brkindx; COLOUR_MAP_ENTRY *Cur; /* Keep all numbers positive. Also, if z is negative, map it in such a * way as to avoid mirroring across the x-axis. The value 5.196152424 * is (sqrt(3)/2) * 6 (because the grid is 6 blocks high) */ x = fabs(x); z = z<0?5.196152424 - fabs(z):z; /* avoid mirroring across x-axis */ xs = x/xfactor; /* scale point to make calcs easier */ zs = z/zfactor; xs -= floor(xs/6) * 6; /* map points into the 6 x 6 grid where */ zs -= floor(zs/6) * 6; /* the basic formula works */ xm = (int) FLOOR(xs) % 6; /* Get a block in the 6 x 6 grid */ zm = (int) FLOOR(zs) % 6; switch (xm) { /* These are easy cases: */ case 0: /* color depends only on xm and zm */ case 5: switch (zm) { case 0: case 5: value = 0; break; case 1: case 2: value = 1; break; case 3: case 4: value = 2; break; } break; case 2: case 3: switch (zm) { case 0: case 1: value = 2; break; case 2: case 3: value = 0; break; case 4: case 5: value = 1; break; } break; /* These cases are harder. These blocks are divided diagonally * by the angled edges of the hexagons. Some slope positive, and * others negative. We flip the x value of the negatively sloped * pieces. Then we check to see if the point in question falls * in the upper or lower half of the block. That info, plus the * z status of the block determines the color. */ case 1: case 4: xl = xs-xm; /* map the point into the block at the origin */ zl = zs-zm; if (((xm+zm) % 2) == 1) /* These blocks have negative slopes */ xl = 1 - xl; /* so we flip it horizontally */ if (xl == 0) xl = .0001; /* avoid a divide-by-zero error */ /* is the angle less-than or greater-than 45 degrees? */ brkindx = (zl/xl) < 1; /* was... * brkindx = (atan2(zl,xl) < (45 * M_PI/180)); * ...but because of the mapping, it's easier and cheaper, * CPU-wise, to just use a good ol' slope. */ switch (brkindx) { case TRUE: switch (zm) { case 0: case 3: value = 0; break; case 2: case 5: value = 1; break; case 1: case 4: value = 2; break; } break; case FALSE: switch (zm) { case 0: case 3: value = 2; break; case 2: case 5: value = 0; break; case 1: case 4: value = 1; break; } } } value = fmod (value+(int)Pigment->Phase,3); Cur = &(Pigment->Colour_Map->Colour_Map_Entries[0]); while (value > Cur->value) Cur++; colour->Red += Cur->Colour.Red; colour->Green += Cur->Colour.Green; colour->Blue += Cur->Colour.Blue; colour->Filter += Cur->Colour.Filter; return; } /* End trihex() */ PIGMENT *Create_Pigment () { PIGMENT *New; if ((New = (PIGMENT *) malloc (sizeof (PIGMENT))) == NULL) MAError ("pigment"); INIT_TPATTERN_FIELDS(New,NO_PIGMENT) New->Colour1 = NULL; Make_Colour(&(New->Quick_Colour), 0.5,0.5,0.5) ; New->Colour_Map = NULL; Make_Vector (&(New->Colour_Gradient), 0.0, 0.0, 0.0); New->Image = NULL; New->Mortar = 0.2; New->Agate_Turb_Scale = 1.0; New->Iterations = 0; return (New); } PIGMENT *Copy_Pigment (Old) PIGMENT *Old; { PIGMENT *New; if (Old != NULL) { New = Create_Pigment (); *New = *Old; New->Trans = Copy_Transform (Old->Trans); New->Colour1 = Copy_Colour (Old->Colour1); New->Image = Copy_Image (Old->Image); New->Colour_Map = Copy_Colour_Map (Old->Colour_Map); } else New = NULL; return (New); } void Translate_Pigment(Pigment,Vector) PIGMENT *Pigment; VECTOR *Vector; { TRANSFORM Trans; if (Pigment == NULL) return; Compute_Translation_Transform (&Trans, Vector); Transform_Pigment (Pigment, &Trans); } void Rotate_Pigment(Pigment,Vector) PIGMENT *Pigment; VECTOR *Vector; { TRANSFORM Trans; if (Pigment == NULL) return; Compute_Rotation_Transform (&Trans, Vector); Transform_Pigment (Pigment, &Trans); } void Scale_Pigment(Pigment,Vector) PIGMENT *Pigment; VECTOR *Vector; { TRANSFORM Trans; if (Pigment == NULL) return; Compute_Scaling_Transform (&Trans, Vector); Transform_Pigment (Pigment, &Trans); } void Transform_Pigment(Pigment,Trans) PIGMENT *Pigment; TRANSFORM *Trans; { if (Pigment == NULL) return; if (!Pigment->Trans) Pigment->Trans = Create_Transform (); Compose_Transforms (Pigment->Trans, Trans); } void Destroy_Pigment (Pigment) PIGMENT *Pigment; { if (Pigment == NULL) return; Destroy_Colour (Pigment->Colour1); Destroy_Colour_Map (Pigment->Colour_Map); Destroy_Image (Pigment->Image); Destroy_Transform (Pigment->Trans); free (Pigment); } void Post_Pigment (Pigment) PIGMENT *Pigment; { if (Pigment == NULL) Error("Missing pigment"); if (Pigment->Flags & POST_DONE) return; if (Pigment->Type == NO_PIGMENT) { Pigment->Type = COLOUR_PIGMENT; Pigment->Colour1 = Create_Colour (); Warn("No pigment type given",1.5); } Pigment->Flags |= POST_DONE; switch (Pigment->Type) { case COLOUR_PIGMENT: Destroy_Transform (Pigment->Trans); Pigment->Trans = NULL; Make_Vector(&(Pigment->Turbulence),0.0,0.0,0.0); break; case BOZO_PIGMENT: if (Pigment->Colour_Map == NULL) Pigment->Colour_Map = &Bozo_Default_Map; break; case WOOD_PIGMENT: if (Pigment->Colour_Map == NULL) Pigment->Colour_Map = &Wood_Default_Map; break; case MANDEL_PIGMENT: if (Pigment->Colour_Map == NULL) Pigment->Colour_Map = &Mandel_Default_Map; break; case RADIAL_PIGMENT: if (Pigment->Colour_Map == NULL) Pigment->Colour_Map = &Radial_Default_Map; break; case AGATE_PIGMENT: if (Pigment->Colour_Map == NULL) Pigment->Colour_Map = &Agate_Default_Map; break; case MARBLE_PIGMENT: if (Pigment->Colour_Map == NULL) Pigment->Colour_Map = &Marble_Default_Map; break; case SPOTTED_PIGMENT: case GRADIENT_PIGMENT: case GRANITE_PIGMENT: case ONION_PIGMENT: case LEOPARD_PIGMENT: if (Pigment->Colour_Map == NULL) Pigment->Colour_Map = &Gray_Default_Map; break; } return; }