/**************************************************************************** * lighting.c * * This module calculates lighting properties like ambient, diffuse, specular, * reflection, refraction, etc. * * 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. * *****************************************************************************/ #include "frame.h" #include "vector.h" #include "povproto.h" extern int Trace_Level; extern FRAME Frame; extern OBJECT *Root_Object; extern unsigned int Options; extern int Use_Slabs; extern unsigned long Quality_Flags; extern int Shadow_Test_Flag; extern long Shadow_Ray_Tests, Shadow_Rays_Succeeded, Shadow_Cache_Hits; extern long Reflected_Rays_Traced, Refracted_Rays_Traced; extern long Transmitted_Rays_Traced; extern short *hashTable; extern unsigned short crctab[256]; #define rand3d(a,b) crctab[(int)(hashTable[(int)(hashTable[(int)((a)&0xfff)]^(b))&0xfff])&0xff] #define COORDINATE_LIMIT 1.0e17 #define BLACK_LEVEL 0.003 /* "Small_Tolerance" is just too tight for higher order polynomial equations. this value should probably be a variable of some sort, but for now just use a reasonably small value. If people render real small objects real close to each other then there may be some shading problems. Otherwise having SHADOW_TOLERANCE as large as this won't affect images. */ #define SHADOW_TOLERANCE 1.0e-3 static void do_light PARAMS((LIGHT_SOURCE *Light_Source, DBL *Light_Source_Depth, RAY *Light_Source_Ray, VECTOR *IPoint, COLOUR *Light_Colour)); static int do_blocking PARAMS((INTERSECTION *Local_Intersection, COLOUR *Light_Colour, ISTACK *Local_Stack)); static void do_phong PARAMS((FINISH *Finish, RAY *Light_Source_Ray, VECTOR *Eye, VECTOR *Layer_Normal, COLOUR *Colour, COLOUR *Light_Colour, COLOUR *Layer_Pigment_Colour)); static void do_specular PARAMS((FINISH *Finish, RAY *Light_Source_Ray, VECTOR *REye, VECTOR *Layer_Normal, COLOUR *Colour, COLOUR *Light_Colour, COLOUR *Layer_Pigment_Colour)); static void do_diffuse PARAMS((FINISH *Finish, RAY *Light_Source_Ray, VECTOR *Layer_Normal, COLOUR *Colour, COLOUR *Light_Colour, COLOUR *Layer_Pigment_Colour, DBL Attenuation)); static void Block_Area_Light PARAMS((LIGHT_SOURCE *Light_Source, DBL Light_Source_Depth, RAY *Light_Source_Ray_Ptr, VECTOR *IPoint, COLOUR *Light_Colour, int u1, int v1, int u2, int v2, int Level)); static void Block_Point_Light PARAMS((LIGHT_SOURCE *Light_Source, DBL Light_Source_Depth, RAY *Light_Source_Ray_Ptr, COLOUR *Light_Colour)); static void Block_Point_Light (Light_Source, Light_Source_Depth, Light_Source_Ray_Ptr, Light_Colour) LIGHT_SOURCE *Light_Source; DBL Light_Source_Depth; RAY *Light_Source_Ray_Ptr; COLOUR *Light_Colour; { OBJECT *Blocking_Object; int Quit_Looking, Not_Found_Shadow, Cache_Me; INTERSECTION *Local_Intersection, Bounded_Intersection; ISTACK *Local_Stack; RAY Local_Ray; RAY Light_Source_Ray; Light_Source_Ray = *Light_Source_Ray_Ptr; Local_Stack = open_istack (); Quit_Looking = FALSE; /* Test the cached object first */ /* Made changes so that semi-transparent objects never get cached */ if (Light_Source->Shadow_Cached_Object != NULL) { Shadow_Ray_Tests++; if (Ray_In_Bounds (&Light_Source_Ray, Light_Source->Shadow_Cached_Object->Bound)) { if (All_Intersections (Light_Source->Shadow_Cached_Object, &Light_Source_Ray, Local_Stack)) while ((Local_Intersection=pop_entry(Local_Stack)) != NULL) { if ((!Local_Intersection->Object->No_Shadow_Flag) && (Local_Intersection->Depth < Light_Source_Depth-Small_Tolerance) && (Local_Intersection->Depth > SHADOW_TOLERANCE)) if (do_blocking(Local_Intersection, Light_Colour, Local_Stack)) { Quit_Looking = TRUE; Shadow_Cache_Hits++; break; } } } } if (Quit_Looking) { close_istack (Local_Stack); return; } Not_Found_Shadow = TRUE; Cache_Me = FALSE; if (!Use_Slabs) { for (Blocking_Object = Frame.Objects; Blocking_Object != NULL; Blocking_Object = Blocking_Object->Sibling) { if (Blocking_Object == Light_Source->Shadow_Cached_Object) continue; Shadow_Ray_Tests++; if (!Ray_In_Bounds (&Light_Source_Ray, Blocking_Object->Bound)) continue; if (!All_Intersections (Blocking_Object, &Light_Source_Ray, Local_Stack)) continue; while ((Local_Intersection = pop_entry(Local_Stack)) != NULL) if ((!Local_Intersection->Object->No_Shadow_Flag) && (Local_Intersection->Depth < Light_Source_Depth-Small_Tolerance) && (Local_Intersection->Depth > SHADOW_TOLERANCE)) { if (do_blocking(Local_Intersection, Light_Colour, Local_Stack)) { Cache_Me = Not_Found_Shadow; Quit_Looking = TRUE; break; /* from while */ } Not_Found_Shadow = FALSE; } if (Quit_Looking) break; /* from for */ } } else /* Use bounding slabs to look for shadows */ { Local_Ray = Light_Source_Ray; while (!Quit_Looking) { Shadow_Ray_Tests++; Local_Ray.Quadric_Constants_Cached = 0; Bounded_Intersection.Depth = Light_Source_Depth; if (Bounds_Intersect(Root_Object, &Local_Ray, &Bounded_Intersection, &Blocking_Object)) { if (Bounded_Intersection.Depth > Light_Source_Depth) break; /* Intersection was beyond the light */ if (!(Bounded_Intersection.Object->No_Shadow_Flag)) if (Blocking_Object != Light_Source->Shadow_Cached_Object) { Shadow_Rays_Succeeded++; Filter_Shadow_Ray(&Bounded_Intersection, Light_Colour); if ((fabs(Light_Colour->Red) < BLACK_LEVEL) && (fabs(Light_Colour->Green) < BLACK_LEVEL) && (fabs(Light_Colour->Blue) < BLACK_LEVEL)) { Cache_Me = Not_Found_Shadow; Quit_Looking = TRUE; break; /* from while */ } } /* Move the ray to the point of intersection, plus some */ Light_Source_Depth -= Bounded_Intersection.Depth; Local_Ray.Initial = Bounded_Intersection.IPoint; Not_Found_Shadow = FALSE; } else /* No intersections in the direction of the ray */ { break; } } /*endwhile*/ } /*endelse*/ if (Cache_Me) Light_Source->Shadow_Cached_Object = Blocking_Object; close_istack (Local_Stack); } static void Block_Area_Light (Light_Source, Light_Source_Depth, Light_Source_Ray_Ptr,IPoint, Light_Colour, u1, v1, u2, v2, Level) LIGHT_SOURCE *Light_Source; DBL Light_Source_Depth; RAY *Light_Source_Ray_Ptr; VECTOR *IPoint; COLOUR *Light_Colour; int u1, v1, u2, v2, Level; { COLOUR Sample_Colour[4], Dummy_Colour; VECTOR Center_Save, NewAxis1, NewAxis2; int i, j, u, v, New_u1, New_v1, New_u2, New_v2; DBL Jitter_u, Jitter_v, ScaleFactor; RAY Light_Source_Ray; Light_Source_Ray = *Light_Source_Ray_Ptr; /* First call, initialize */ if (u1 == 0 && v1 == 0 && u2 == 0 && v2 == 0) { /* Flag uncalculated points with a negative value for Red */ for (i = 0; i < Light_Source->Area_Size1; i++) { for (j = 0; j < Light_Source->Area_Size2; j++) Light_Source->Light_Grid[i][j].Red = -1.0; } u1 = 0; v1 = 0; u2 = Light_Source->Area_Size1 - 1; v2 = Light_Source->Area_Size2 - 1; } /* Save the light source center since we'll be fiddling with it */ Center_Save=Light_Source->Center; /* Sample the four corners of the region */ for (i = 0; i < 4; i++) { switch (i) { case 0: u = u1; v = v1; break; case 1: u = u2; v = v1; break; case 2: u = u1; v = v2; break; case 3: u = u2; v = v2; break; } if (Light_Source -> Light_Grid[u][v].Red >= 0.0) /* We've already calculated this point, reuse it */ Sample_Colour[i]=Light_Source->Light_Grid[u][v]; else { Jitter_u = (DBL)u; Jitter_v = (DBL)v; if (Light_Source -> Jitter) { Jitter_u += (rand() % 4096)/4096.0 - 0.5; Jitter_v += (rand() % 4096)/4096.0 - 0.5; /* Not sure if jx = IPoint->x + 100*u; this works jy = IPoint->y + 100*v; yet Jitter_u += ((rand3d(jx, jy) & 0x7FFF)/32768.0) - 0.5; Jitter_v += ((rand3d(jx+10, jy+10) & 0x7FFF)/32768.0) - 0.5; */ } if (Light_Source -> Area_Size1 > 1) { ScaleFactor = Jitter_u/(DBL)(Light_Source->Area_Size1 - 1) - 0.5; VScale (NewAxis1, Light_Source->Axis1, ScaleFactor) } else Make_Vector (&NewAxis1, 0.0, 0.0, 0.0); if (Light_Source -> Area_Size2 > 1) { ScaleFactor = Jitter_v/(DBL)(Light_Source->Area_Size2 - 1) - 0.5; VScale (NewAxis2, Light_Source->Axis2, ScaleFactor) } else Make_Vector (&NewAxis2, 0.0, 0.0, 0.0); Light_Source->Center=Center_Save; VAdd (Light_Source->Center, Light_Source->Center, NewAxis1); VAdd (Light_Source->Center, Light_Source->Center, NewAxis2); /* Recalculate the light source ray but not the colour */ do_light (Light_Source, &Light_Source_Depth, &Light_Source_Ray, IPoint, &Dummy_Colour); Sample_Colour[i]=*Light_Colour; Block_Point_Light (Light_Source, Light_Source_Depth, &Light_Source_Ray, &Sample_Colour[i]); Light_Source->Light_Grid[u][v]=Sample_Colour[i]; } } Light_Source->Center=Center_Save; if ( (u2 - u1 > 1 || v2 - v1 > 1) && (Level < Light_Source -> Adaptive_Level || Colour_Distance (&Sample_Colour[0], &Sample_Colour[1]) > 0.1 || Colour_Distance (&Sample_Colour[1], &Sample_Colour[3]) > 0.1 || Colour_Distance (&Sample_Colour[3], &Sample_Colour[2]) > 0.1 || Colour_Distance (&Sample_Colour[2], &Sample_Colour[0]) > 0.1) ) { for (i = 0; i < 4; i++) { switch (i) { case 0: New_u1 = u1; New_v1 = v1; New_u2 = (int)floor ((u1 + u2)/2.0); New_v2 = (int)floor ((v1 + v2)/2.0); break; case 1: New_u1 = (int)ceil ((u1 + u2)/2.0); New_v1 = v1; New_u2 = u2; New_v2 = (int)floor ((v1 + v2)/2.0); break; case 2: New_u1 = u1; New_v1 = (int)ceil ((v1 + v2)/2.0); New_u2 = (int)floor ((u1 + u2)/2.0); New_v2 = v2; break; case 3: New_u1 = (int)ceil ((u1 + u2)/2.0); New_v1 = (int)ceil ((v1 + v2)/2.0); New_u2 = u2; New_v2 = v2; break; } /* Recalculate the light source ray but not the colour */ do_light (Light_Source, &Light_Source_Depth, &Light_Source_Ray, IPoint, &Dummy_Colour); Sample_Colour[i]=*Light_Colour; Block_Area_Light (Light_Source, Light_Source_Depth, &Light_Source_Ray, IPoint, &Sample_Colour[i], New_u1, New_v1, New_u2, New_v2, Level+1); } } /* Add up the light contributions */ Make_Colour (Light_Colour, 0.0, 0.0, 0.0); for (i = 0; i < 4; i++) { Scale_Colour (&Sample_Colour[i], &Sample_Colour[i], 0.25); Add_Colour (Light_Colour, Light_Colour, &Sample_Colour[i]); } } static void do_light(Light_Source, Light_Source_Depth, Light_Source_Ray, IPoint, Light_Colour) LIGHT_SOURCE *Light_Source; DBL *Light_Source_Depth; RAY *Light_Source_Ray; VECTOR *IPoint; COLOUR *Light_Colour; { DBL Attenuation = 1.0; /* Get the light source colour. */ *Light_Colour = Light_Source->Colour; Light_Source_Ray->Initial = *IPoint; Light_Source_Ray->Quadric_Constants_Cached = FALSE; VSub (Light_Source_Ray->Direction, Light_Source->Center, *IPoint); VLength (*Light_Source_Depth, Light_Source_Ray->Direction); VScale (Light_Source_Ray->Direction, Light_Source_Ray->Direction, 1.0/(*Light_Source_Depth)); Attenuation = Attenuate_Light(Light_Source, Light_Source_Ray); /* Now scale the color by the attenuation */ Light_Colour->Red *= Attenuation; Light_Colour->Green *= Attenuation; Light_Colour->Blue *= Attenuation; return; } static int do_blocking(Local_Intersection, Light_Colour, Local_Stack) INTERSECTION *Local_Intersection; COLOUR *Light_Colour; ISTACK *Local_Stack; { Shadow_Rays_Succeeded++; Filter_Shadow_Ray (Local_Intersection, Light_Colour); if ((fabs(Light_Colour->Red) < BLACK_LEVEL) && (fabs(Light_Colour->Green) < BLACK_LEVEL) && (fabs(Light_Colour->Blue) < BLACK_LEVEL)) { while ((Local_Intersection = pop_entry(Local_Stack)) != NULL) { } return(TRUE); } return(FALSE); } static void do_phong(Finish, Light_Source_Ray, Eye, Layer_Normal, Colour, Light_Colour, Layer_Pigment_Colour) FINISH *Finish; RAY *Light_Source_Ray; VECTOR *Layer_Normal, *Eye; COLOUR *Colour, *Light_Colour, *Layer_Pigment_Colour; { DBL Cos_Angle_Of_Incidence, Normal_Length, Intensity; VECTOR Local_Normal, Normal_Projection, Reflect_Direction; VDot(Cos_Angle_Of_Incidence, *Eye, *Layer_Normal); if (Cos_Angle_Of_Incidence < 0.0) { Local_Normal = *Layer_Normal; Cos_Angle_Of_Incidence = -Cos_Angle_Of_Incidence; } else { VScale (Local_Normal, *Layer_Normal, -1.0); } VScale (Normal_Projection, Local_Normal, Cos_Angle_Of_Incidence); VScale (Normal_Projection, Normal_Projection, 2.0); VAdd (Reflect_Direction, *Eye, Normal_Projection); VDot (Cos_Angle_Of_Incidence, Reflect_Direction, Light_Source_Ray->Direction); VLength (Normal_Length, Light_Source_Ray->Direction); if (Normal_Length == 0.0) Cos_Angle_Of_Incidence = 0.0; else Cos_Angle_Of_Incidence /= Normal_Length; if (Cos_Angle_Of_Incidence < 0.0) Cos_Angle_Of_Incidence = 0; if (Finish->Phong_Size != 1.0) Intensity = pow(Cos_Angle_Of_Incidence, Finish->Phong_Size); else Intensity = Cos_Angle_Of_Incidence; Intensity *= Finish->Phong; if (Finish->Metallic_Flag) { Colour->Red+=Intensity*(Layer_Pigment_Colour->Red)*(Light_Colour->Red); Colour->Green+=Intensity*(Layer_Pigment_Colour->Green)*(Light_Colour->Green); Colour->Blue+=Intensity*(Layer_Pigment_Colour->Blue)*(Light_Colour->Blue); } else { Colour->Red+=Intensity*(Light_Colour->Red); Colour->Green+=Intensity*(Light_Colour->Green); Colour->Blue+=Intensity*(Light_Colour->Blue); } } static void do_specular(Finish, Light_Source_Ray, REye, Layer_Normal, Colour, Light_Colour, Layer_Pigment_Colour) FINISH *Finish; RAY *Light_Source_Ray; VECTOR *Layer_Normal, *REye; COLOUR *Colour, *Light_Colour, *Layer_Pigment_Colour; { DBL Cos_Angle_Of_Incidence, Normal_Length, Intensity, Halfway_Length; VECTOR Halfway; VHalf (Halfway, *REye, Light_Source_Ray->Direction); VLength (Normal_Length, *Layer_Normal); VLength (Halfway_Length, Halfway); VDot (Cos_Angle_Of_Incidence, Halfway, *Layer_Normal); if (Normal_Length == 0.0 || Halfway_Length == 0.0) Cos_Angle_Of_Incidence = 0.0; else Cos_Angle_Of_Incidence /= (Normal_Length * Halfway_Length); if (Cos_Angle_Of_Incidence < 0.0) Cos_Angle_Of_Incidence = 0.0; if (Finish->Roughness != 1.0) Intensity = pow(Cos_Angle_Of_Incidence, Finish->Roughness); else Intensity = Cos_Angle_Of_Incidence; Intensity *= Finish->Specular; if (Finish->Metallic_Flag) { Colour->Red+=Intensity*(Layer_Pigment_Colour->Red)*(Light_Colour->Red); Colour->Green+=Intensity*(Layer_Pigment_Colour->Green)*(Light_Colour->Green); Colour->Blue+=Intensity*(Layer_Pigment_Colour->Blue)*(Light_Colour->Blue); } else { Colour->Red+=Intensity*(Light_Colour->Red); Colour->Green+=Intensity*(Light_Colour->Green); Colour->Blue+=Intensity*(Light_Colour->Blue); } } static void do_diffuse(Finish, Light_Source_Ray, Layer_Normal, Colour, Light_Colour, Layer_Pigment_Colour, Attenuation) FINISH *Finish; RAY *Light_Source_Ray; VECTOR *Layer_Normal; COLOUR *Colour, *Light_Colour, *Layer_Pigment_Colour; DBL Attenuation; { DBL Cos_Angle_Of_Incidence, Intensity; VDot (Cos_Angle_Of_Incidence, *Layer_Normal, Light_Source_Ray->Direction); if (Cos_Angle_Of_Incidence < 0.0) Cos_Angle_Of_Incidence = -Cos_Angle_Of_Incidence; if (Finish->Brilliance != 1.0) Intensity = pow(Cos_Angle_Of_Incidence, Finish->Brilliance); else Intensity = Cos_Angle_Of_Incidence; Intensity *= Finish->Diffuse * Attenuation; if (Finish->Crand > 0.0) Intensity -= ((rand()&0x7FFF)/(DBL) 0x7FFF) * Finish->Crand; Colour->Red += Intensity * (Layer_Pigment_Colour->Red) * (Light_Colour->Red); Colour->Green += Intensity * (Layer_Pigment_Colour->Green) * (Light_Colour->Green); Colour->Blue += Intensity * (Layer_Pigment_Colour->Blue) * (Light_Colour->Blue); return; } /* Given a 3d point and a pigment, accumulate colour from that layer */ /* Formerly called "Colour_At" */ void Add_Pigment (Colour, Pigment, IPoint) COLOUR *Colour; PIGMENT *Pigment; VECTOR *IPoint; { register DBL x, y, z; VECTOR TPoint,PTurbulence; if (Pigment->Trans != NULL) MInvTransPoint (&TPoint, IPoint, Pigment->Trans); else TPoint = *IPoint; x = TPoint.x; y = TPoint.y; z = TPoint.z; if(Pigment->Type != WOOD_PIGMENT && Pigment->Type != MARBLE_PIGMENT && Pigment->Type != NO_PIGMENT && Pigment->Type != COLOUR_PIGMENT && /* Pigment->Type != SPOTTED_PIGMENT && */ /*maybe?*/ Pigment->Flags & HAS_TURB) { DTurbulence (&PTurbulence, x, y, z, Pigment->omega,Pigment->lambda,Pigment->Octaves); x += PTurbulence.x * Pigment->Turbulence.x; y += PTurbulence.y * Pigment->Turbulence.y; z += PTurbulence.z * Pigment->Turbulence.z; } if (x > COORDINATE_LIMIT) x = COORDINATE_LIMIT; else if (x < -COORDINATE_LIMIT) x = -COORDINATE_LIMIT; if (y > COORDINATE_LIMIT) y = COORDINATE_LIMIT; else if (y < -COORDINATE_LIMIT) y = -COORDINATE_LIMIT; if (z > COORDINATE_LIMIT) z = COORDINATE_LIMIT; else if (z < -COORDINATE_LIMIT) z = -COORDINATE_LIMIT; switch (Pigment->Type) { case NO_PIGMENT: /* No colouring pigment has been specified - make it black. */ Make_Colour (Colour, 0.0, 0.0, 0.0); Colour -> Filter = 0.0; break; case COLOUR_PIGMENT: Colour -> Red += Pigment->Colour1->Red; Colour -> Green += Pigment->Colour1->Green; Colour -> Blue += Pigment->Colour1->Blue; Colour -> Filter += Pigment->Colour1->Filter; break; case BOZO_PIGMENT: bozo (x, y, z, Pigment, Colour); break; case MARBLE_PIGMENT: marble (x, y, z, Pigment, Colour); break; case WOOD_PIGMENT: wood (x, y, z, Pigment, Colour); break; case CHECKER_PIGMENT: checker (x, y, z, Pigment, Colour); break; case SPOTTED_PIGMENT: spotted (x, y, z, Pigment, Colour); break; case AGATE_PIGMENT: agate (x, y, z, Pigment, Colour); break; case GRANITE_PIGMENT: granite (x, y, z, Pigment, Colour); break; case GRADIENT_PIGMENT: gradient (x, y, z, Pigment, Colour); break; case HEXAGON_PIGMENT: hexagon (x, y, z, Pigment, Colour); break; case RADIAL_PIGMENT: radial (x, y, z, Pigment, Colour); break; case MANDEL_PIGMENT: mandel (x, y, z, Pigment, Colour); break; case IMAGE_MAP_PIGMENT: image_map (x, y, z, Pigment, Colour); break; case ONION_PIGMENT: onion (x, y, z, Pigment, Colour); break; case LEOPARD_PIGMENT: leopard (x, y, z, Pigment, Colour); break; case PAINTED1_PIGMENT: painted1 (x, y, z, Pigment, Colour); break; case PAINTED2_PIGMENT: painted2 (x, y, z, Pigment, Colour); break; case PAINTED3_PIGMENT: painted3 (x, y, z, Pigment, Colour); break; } } void Perturb_Normal(Layer_Normal, Tnormal, IPoint) VECTOR *Layer_Normal, *IPoint; TNORMAL *Tnormal; { register DBL x, y, z; VECTOR TPoint,NTurbulence; if (Tnormal->Trans != NULL) MInvTransPoint (&TPoint, IPoint, Tnormal->Trans); else TPoint = *IPoint; x = TPoint.x; y = TPoint.y; z = TPoint.z; if(Tnormal->Flags && HAS_TURB) { DTurbulence (&NTurbulence, x, y, z, Tnormal->omega,Tnormal->lambda,Tnormal->Octaves); x += NTurbulence.x * Tnormal->Turbulence.x; y += NTurbulence.y * Tnormal->Turbulence.y; z += NTurbulence.z * Tnormal->Turbulence.z; } switch (Tnormal->Type) { case WAVES: waves (x, y, z, Tnormal, Layer_Normal); break; case RIPPLES: ripples (x, y, z, Tnormal, Layer_Normal); break; case WRINKLES: wrinkles (x, y, z, Tnormal, Layer_Normal); break; case BUMPS: bumps (x, y, z, Tnormal, Layer_Normal); break; case DENTS: dents (x, y, z, Tnormal, Layer_Normal); break; case BUMPY1: bumpy1 (x, y, z, Tnormal, Layer_Normal); break; case BUMPY2: bumpy2 (x, y, z, Tnormal, Layer_Normal); break; case BUMPY3: bumpy3 (x, y, z, Tnormal, Layer_Normal); break; case BUMP_MAP: bump_map (x, y, z, Tnormal, Layer_Normal); break; } return; } void Diffuse (Finish, IPoint, Eye, Layer_Normal, Layer_Pigment_Colour, Colour, Attenuation) FINISH *Finish; VECTOR *IPoint, *Layer_Normal; COLOUR *Layer_Pigment_Colour; COLOUR *Colour; RAY *Eye; DBL Attenuation; { DBL Light_Source_Depth, Cos_Shadow_Angle; RAY Light_Source_Ray; LIGHT_SOURCE *Light_Source; VECTOR REye; COLOUR Light_Colour; if ((Finish->Diffuse == 0.0) && (Finish->Specular == 0.0) && (Finish->Phong == 0.0)) return; if (Finish->Specular != 0.0) { REye.x = -Eye->Direction.x; REye.y = -Eye->Direction.y; REye.z = -Eye->Direction.z; } for (Light_Source = Frame.Light_Sources ; Light_Source != NULL; Light_Source = Light_Source->Next_Light_Source) { /* Get a colour and a ray */ do_light(Light_Source, &Light_Source_Depth, &Light_Source_Ray, IPoint, &Light_Colour); /* Don't calculate spotlights when outside of the light's cone */ if (fabs(Light_Colour.Red) < BLACK_LEVEL && fabs(Light_Colour.Green) < BLACK_LEVEL && fabs(Light_Colour.Blue) < BLACK_LEVEL) continue; /* See if light on far side of surface from camera. */ VDot(Cos_Shadow_Angle,*Layer_Normal,Light_Source_Ray.Direction); if (Cos_Shadow_Angle < 0.0) continue; /* If light source was not blocked by any intervening object, then calculate it's contribution to the object's overall illumination */ Shadow_Test_Flag = TRUE; if (Quality_Flags & Q_SHADOW) { if ((Light_Source->Area_Light) && (Quality_Flags & Q_AREA_LIGHT)) Block_Area_Light (Light_Source, Light_Source_Depth, &Light_Source_Ray, IPoint, &Light_Colour, 0, 0, 0, 0, 0); else Block_Point_Light (Light_Source, Light_Source_Depth, &Light_Source_Ray, &Light_Colour); } Shadow_Test_Flag = FALSE; if (fabs(Light_Colour.Red) > BLACK_LEVEL || fabs(Light_Colour.Green) > BLACK_LEVEL || fabs(Light_Colour.Blue) > BLACK_LEVEL) { if (Finish->Phong > 0.0) do_phong(Finish,&Light_Source_Ray,&Eye->Direction,Layer_Normal,Colour,&Light_Colour, Layer_Pigment_Colour); if (Finish->Specular > 0.0) do_specular(Finish,&Light_Source_Ray,&REye,Layer_Normal,Colour,&Light_Colour, Layer_Pigment_Colour); if (Finish->Diffuse > 0.0) do_diffuse(Finish,&Light_Source_Ray,Layer_Normal,Colour,&Light_Colour,Layer_Pigment_Colour, Attenuation); } } return; } void Reflect (Reflection, IPoint, Ray, Layer_Normal, Colour) DBL Reflection; VECTOR *IPoint; RAY *Ray; VECTOR *Layer_Normal; COLOUR *Colour; { RAY New_Ray; COLOUR Temp_Colour; VECTOR Local_Normal; VECTOR Normal_Projection; VECTOR Surface_Offset; register DBL Normal_Component; if (Reflection != 0.0) { Reflected_Rays_Traced++; VDot (Normal_Component, Ray -> Direction, *Layer_Normal); if (Normal_Component < 0.0) { Local_Normal = *Layer_Normal; Normal_Component *= -1.0; } else VScale (Local_Normal, *Layer_Normal, -1.0); VScale (Normal_Projection, Local_Normal, Normal_Component); VScale (Normal_Projection, Normal_Projection, 2.0); VAdd (New_Ray.Direction, Ray -> Direction, Normal_Projection); New_Ray.Initial = *IPoint; /* ARE 08/25/91 */ VScale(Surface_Offset, New_Ray.Direction, 2.0 * Small_Tolerance); VAdd(New_Ray.Initial, New_Ray.Initial, Surface_Offset); Copy_Ray_Containers (&New_Ray, Ray); Trace_Level++; Make_Colour (&Temp_Colour, 0.0, 0.0, 0.0); New_Ray.Quadric_Constants_Cached = FALSE; Trace (&New_Ray, &Temp_Colour); Trace_Level--; Colour -> Red += Temp_Colour.Red * Reflection; Colour -> Green += Temp_Colour.Green * Reflection; Colour -> Blue += Temp_Colour.Blue * Reflection; } } void Refract (Texture, IPoint, Ray, Top_Normal, Colour) TEXTURE *Texture; VECTOR *IPoint; RAY *Ray; VECTOR *Top_Normal; COLOUR *Colour; { RAY New_Ray; COLOUR Temp_Colour; VECTOR Local_Normal; VECTOR Ray_Direction; register DBL Normal_Component, Temp_IOR; DBL temp, ior; /* int inside; */ if (Top_Normal == NULL) { New_Ray.Initial = *IPoint; New_Ray.Direction = Ray->Direction; Copy_Ray_Containers (&New_Ray, Ray); Trace_Level++; Transmitted_Rays_Traced++; Make_Colour (&Temp_Colour, 0.0, 0.0, 0.0); New_Ray.Quadric_Constants_Cached = FALSE; Trace (&New_Ray, &Temp_Colour); Trace_Level--; (Colour -> Red) += Temp_Colour.Red; (Colour -> Green) += Temp_Colour.Green; (Colour -> Blue) += Temp_Colour.Blue; } else { Refracted_Rays_Traced++; VDot (Normal_Component, Ray -> Direction, *Top_Normal); if (Normal_Component <= 0.0) { Local_Normal.x = Top_Normal -> x; Local_Normal.y = Top_Normal -> y; Local_Normal.z = Top_Normal -> z; Normal_Component *= -1.0; /* inside = FALSE;*/ } else { VScale (Local_Normal, *Top_Normal, -1.0); /* inside = TRUE;*/ } Copy_Ray_Containers (&New_Ray, Ray); if (Ray -> Containing_Index == -1) { /* The ray is entering from the atmosphere */ Ray_Enter (&New_Ray, Texture); ior = (Frame.Atmosphere_IOR)/(Texture->Finish->Index_Of_Refraction); } else { /* The ray is currently inside an object */ if (New_Ray.Containing_Textures [New_Ray.Containing_Index] == Texture) /* if (inside) */ { /* The ray is leaving the current object */ Ray_Exit (&New_Ray); if (New_Ray.Containing_Index == -1) /* The ray is leaving into the atmosphere */ Temp_IOR = Frame.Atmosphere_IOR; else /* The ray is leaving into another object */ Temp_IOR = New_Ray.Containing_IORs [New_Ray.Containing_Index]; ior = (Texture->Finish->Index_Of_Refraction)/Temp_IOR; } else { /* The ray is entering a new object */ Temp_IOR = New_Ray.Containing_IORs [New_Ray.Containing_Index]; Ray_Enter (&New_Ray, Texture); ior = Temp_IOR / (Texture->Finish->Index_Of_Refraction); } } temp = 1.0 + ior * ior * (Normal_Component * Normal_Component - 1.0); if (temp < 0.0) { Reflect ((1.0 - Texture->Finish->Reflection), IPoint, Ray, Top_Normal, Colour); return; } temp = ior*Normal_Component - sqrt(temp); VScale (Local_Normal, Local_Normal, temp); VScale (Ray_Direction, Ray->Direction, ior); VAdd (New_Ray.Direction, Local_Normal, Ray_Direction); VNormalize (New_Ray.Direction, New_Ray.Direction); New_Ray.Initial = *IPoint; Trace_Level++; Make_Colour (&Temp_Colour, 0.0, 0.0, 0.0); New_Ray.Quadric_Constants_Cached = FALSE; Trace (&New_Ray, &Temp_Colour); Trace_Level--; (Colour -> Red) += (Temp_Colour.Red) * (Texture -> Finish->Refraction); (Colour -> Green) += (Temp_Colour.Green) * (Texture -> Finish->Refraction); (Colour -> Blue) += (Temp_Colour.Blue) * (Texture -> Finish->Refraction); } } void Fog (Distance, Fog_Colour, Fog_Distance, Colour) DBL Distance, Fog_Distance; COLOUR *Fog_Colour, *Colour; { DBL Fog_Factor, Fog_Factor_Inverse; Fog_Factor = exp(-1.0 * Distance/Fog_Distance); Fog_Factor_Inverse = 1.0 - Fog_Factor; Colour->Red = Colour->Red*Fog_Factor + Fog_Colour->Red*Fog_Factor_Inverse; Colour->Green = Colour->Green*Fog_Factor + Fog_Colour->Green*Fog_Factor_Inverse; Colour->Blue = Colour->Blue*Fog_Factor + Fog_Colour->Blue*Fog_Factor_Inverse; } void Compute_Reflected_Colour (Ray, Finish, Ray_Intersection, Layer_Pigment_Colour, Filter_Colour, Colour, Layer_Normal) RAY *Ray; FINISH *Finish; INTERSECTION *Ray_Intersection; COLOUR *Layer_Pigment_Colour; COLOUR *Filter_Colour; COLOUR *Colour; VECTOR *Layer_Normal; { DBL Attenuation, Ambient; COLOUR Emitted_Colour; /* This variable keeps track of how much colour comes from the surface of the object and how much is transmited through. */ Make_Colour (&Emitted_Colour, 0.0, 0.0, 0.0); if (Quality_Flags & Q_FULL_AMBIENT) { Layer_Pigment_Colour->Filter = 0.0; Colour->Red += Layer_Pigment_Colour->Red * Filter_Colour->Filter; Colour->Green += Layer_Pigment_Colour->Green * Filter_Colour->Filter; Colour->Blue += Layer_Pigment_Colour->Blue * Filter_Colour->Filter; return; } Attenuation = Filter_Colour->Filter * (1.0 - Layer_Pigment_Colour->Filter); if ((Ambient = Finish->Ambient*Attenuation) != 0.0) { Emitted_Colour.Red += Layer_Pigment_Colour->Red * Ambient; Emitted_Colour.Green += Layer_Pigment_Colour->Green * Ambient; Emitted_Colour.Blue += Layer_Pigment_Colour->Blue * Ambient; } Diffuse (Finish, &Ray_Intersection ->IPoint, Ray, Layer_Normal, Layer_Pigment_Colour, &Emitted_Colour, Attenuation); Colour->Red += Emitted_Colour.Red; Colour->Green += Emitted_Colour.Green; Colour->Blue += Emitted_Colour.Blue; if (Quality_Flags & Q_REFLECT) Reflect (Finish->Reflection, &Ray_Intersection -> IPoint, Ray, Layer_Normal, Colour); } /* Given an intersection point, a ray, & a shadow flag, add that point's color to the given colour and return it. */ void Determine_Apparent_Colour (Ray_Intersection, Colour, Ray) INTERSECTION *Ray_Intersection; COLOUR *Colour; RAY *Ray; { COLOUR Layer_Pigment_Colour, Refracted_Colour, Filter_Colour; TEXTURE *Layer, *Texture; FINISH *Finish; VECTOR Layer_Normal, Raw_Normal, Top_Normal; DBL Normal_Direction; int layer_number; #define QColour Texture->Pigment->Quick_Colour Normal (&Raw_Normal, Ray_Intersection->Object, &Ray_Intersection->IPoint); /* Now, we perform the lighting calculations. */ /* We assume here that Post_Process has propagated all parent textures to the object itself and that everything has some texture. Redirrect to the proper texture if its a material or checker texture */ for (Texture = Ray_Intersection->Object->Texture; Texture->Type != PNF_TEXTURE;) switch (Texture->Type) { case TILE_TEXTURE: Texture = tiles_texture(&Ray_Intersection->IPoint,((TILES *)Texture)); break; case MAT_TEXTURE: Texture = material_map(&Ray_Intersection->IPoint,((MATERIAL *)Texture)); break; default: fprintf(stderr, "Bad texture type: %d\n", Texture->Type); close_all(); /* 16/10/1993 : M.A.: V 1.32: abandon sur erreur propre */ Wait_Keyboard_And_Quit(1); }; Make_ColourA (&Filter_Colour, 1.0, 1.0, 1.0, 1.0); for (layer_number=1 , Layer = Texture; (Layer != NULL) && (fabs(Filter_Colour.Filter) > BLACK_LEVEL); layer_number++, Layer = Layer->Next_Layer) { Make_Colour (&Layer_Pigment_Colour, 0.0, 0.0, 0.0); if (Quality_Flags & Q_QUICKC) Layer_Pigment_Colour = QColour; else Add_Pigment (&Layer_Pigment_Colour, Layer->Pigment, &Ray_Intersection->IPoint); Layer_Normal = Raw_Normal; if ((Quality_Flags & Q_NORMAL) && (Texture->Tnormal != NULL)) Perturb_Normal (&Layer_Normal, Texture->Tnormal, &Ray_Intersection->IPoint); /* If the surface normal points away, flip its direction. */ VDot (Normal_Direction, Layer_Normal, Ray->Direction); if (Normal_Direction > 0.0) { VScaleEq (Layer_Normal, -1.0); } if (layer_number == 1) Top_Normal = Layer_Normal; Compute_Reflected_Colour (Ray, Layer->Finish, Ray_Intersection, &Layer_Pigment_Colour, &Filter_Colour, Colour, &Layer_Normal); Filter_Colour.Red *= Layer_Pigment_Colour.Red; Filter_Colour.Green *= Layer_Pigment_Colour.Green; Filter_Colour.Blue *= Layer_Pigment_Colour.Blue; Filter_Colour.Filter *= Layer_Pigment_Colour.Filter; } Finish = Texture->Finish; if ((fabs(Filter_Colour.Filter) > BLACK_LEVEL) && (Quality_Flags & Q_REFRACT)) { Make_Colour (&Refracted_Colour, 0.0, 0.0, 0.0); if (Finish->Refraction > 0.0) Refract (Texture, &Ray_Intersection -> IPoint, Ray, &Top_Normal, &Refracted_Colour); else Refract (Texture, &Ray_Intersection->IPoint, Ray, NULL, &Refracted_Colour); Colour->Red += Filter_Colour.Red * Refracted_Colour.Red * Filter_Colour.Filter; Colour->Green += Filter_Colour.Green * Refracted_Colour.Green * Filter_Colour.Filter; Colour->Blue += Filter_Colour.Blue * Refracted_Colour.Blue * Filter_Colour.Filter; } if (Frame.Fog_Distance != 0.0) Fog (Ray_Intersection->Depth, &Frame.Fog_Colour, Frame.Fog_Distance, Colour); } void Filter_Shadow_Ray (Ray_Intersection, Colour) INTERSECTION *Ray_Intersection; COLOUR *Colour; { COLOUR Layer_Pigment_Colour, Filter_Colour; TEXTURE *Layer, *Texture; FINISH *Finish; int layer_number; #define QColour Texture->Pigment->Quick_Colour if (!(Quality_Flags & Q_SHADOW)) return; /* Now, we perform the lighting calculations. */ /* We assume here that Post_Process has propagated all parent textures to the object itself and that everything has some texture. Redirrect to the proper texture if its a material or checker texture */ for (Texture = Ray_Intersection->Object->Texture; Texture->Type != PNF_TEXTURE;) switch (Texture->Type) { case TILE_TEXTURE: Texture = tiles_texture(&Ray_Intersection->IPoint,((TILES *)Texture)); break; case MAT_TEXTURE: Texture = material_map(&Ray_Intersection->IPoint,((MATERIAL *)Texture)); break; default: fprintf(stderr, "Bad texture type: %d\n", Texture->Type); close_all(); /* 16/10/1993 : M.A.: V 1.32: abandon sur erreur propre */ Wait_Keyboard_And_Quit(1); }; Make_ColourA (&Filter_Colour, 1.0, 1.0, 1.0, 1.0); for (layer_number=1 , Layer = Texture; (Layer != NULL) && (fabs(Filter_Colour.Filter) > BLACK_LEVEL); layer_number++, Layer = Layer->Next_Layer) { if (Quality_Flags & Q_QUICKC) Layer_Pigment_Colour = QColour; else { Make_Colour (&Layer_Pigment_Colour, 0.0, 0.0, 0.0); Add_Pigment (&Layer_Pigment_Colour, Layer->Pigment, &Ray_Intersection->IPoint); } Filter_Colour.Red *= Layer_Pigment_Colour.Red; Filter_Colour.Green *= Layer_Pigment_Colour.Green; Filter_Colour.Blue *= Layer_Pigment_Colour.Blue; Filter_Colour.Filter *= Layer_Pigment_Colour.Filter; } Finish = Texture->Finish; /* For shadow rays, we have the filter colour now - time to return */ if (fabs(Filter_Colour.Filter) < BLACK_LEVEL) { Make_Colour (Colour, 0.0, 0.0, 0.0); return; } if (Finish->Refraction > 0.0) { Colour->Red *= Filter_Colour.Red * Finish->Refraction * Filter_Colour.Filter; Colour->Green *= Filter_Colour.Green * Finish->Refraction * Filter_Colour.Filter; Colour->Blue *= Filter_Colour.Blue * Finish->Refraction * Filter_Colour.Filter; } else { Colour->Red *= Filter_Colour.Red * Filter_Colour.Filter; Colour->Green *= Filter_Colour.Green * Filter_Colour.Filter; Colour->Blue *= Filter_Colour.Blue * Filter_Colour.Filter; } return; }