home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Resource Library: Graphics
/
graphics-16000.iso
/
msdos
/
raytrace
/
rayshade
/
src
/
shadow.c
< prev
next >
Wrap
Text File
|
1992-04-30
|
8KB
|
317 lines
/*
* shadow.c
*
* Copyright (C) 1989, 1991, Craig E. Kolb
* All rights reserved.
*
* This software may be freely copied, modified, and redistributed
* provided that this copyright notice is preserved on all copies.
*
* You may not distribute this software, in whole or in part, as part of
* any commercial product without the express consent of the authors.
*
* There is no warranty or other guarantee of fitness of this software
* for any purpose. It is provided solely "as is".
*
* $Id: shadow.c,v 4.0.1.1 91/09/29 15:40:57 cek Exp Locker: cek $
*
* $Log: shadow.c,v $
* Revision 4.0.1.1 91/09/29 15:40:57 cek
* patch1: ShadowOptions was incorrectly externed.
*
* Revision 4.0 91/07/17 14:35:34 kolb
* Initial version.
*
*/
#include "geom.h"
#include "surface.h"
#include "light.h"
/*
* Shadow stats.
* External functions have read access via ShadowStats().
*/
static unsigned long ShadowRays, ShadowHits, CacheMisses, CacheHits;
/*
* Options controlling how shadowing information is determined.
* Set by external modules via ShadowSetOptions().
*/
static long ShadowOptions;
void LightCacheHit();
/*
* Trace ray from point of intersection to a light. If an intersection
* occurs at a distance less than "dist" (the distance to the
* light source), then the point is in shadow, and TRUE is returned.
* Otherwise, the brightness/color of the light is computed ('result'),
* and FALSE is returned.
*/
int
Shadowed(result, color, cache, ray, dist, noshadow)
Color *result, *color; /* resultant intensity, light color */
ShadowCache *cache; /* shadow cache for light */
Ray *ray; /* ray, origin on surface, dir towards light */
Float dist; /* distance from pos to light source */
int noshadow; /* If TRUE, no shadow ray is cast. */
{
int i, smooth, enter;
HitList hitlist;
Ray tmpray;
ShadowCache *cp;
Vector hitpos, norm, gnorm;
Surface surf, *sptr, *prevsurf;
Float s, totaldist, statten;
Color res;
if (noshadow || NOSHADOWS(ShadowOptions)) {
*result = *color;
return FALSE;
}
ShadowRays++;
s = dist;
cp = &cache[ray->depth];
/*
* Check shadow cache. SHADOWCACHE() is implied.
*/
if (cp->obj) {
/*
* Transform ray to the space of the cached primitive.
*/
tmpray = *ray;
if (cp->dotrans)
s *= RayTransform(&tmpray, &cp->trans);
/*
* s = distance to light source in 'primitive space'.
* Intersect ray with cached object.
*/
if (cp->obj->animtrans) {
/*
* Geom has animated transformation --
* call intersect so that the transformation
* is resolved properly.
*/
if (intersect(cp->obj, &tmpray, &hitlist,
SHADOW_EPSILON, &s)) {
CacheHits++;
return TRUE;
}
} else if (IsAggregate(cp->obj)) {
if ((*cp->obj->methods->intersect)(cp->obj->obj,
&tmpray, &hitlist, SHADOW_EPSILON, &s)) {
CacheHits++;
return TRUE;
}
} else if ((*cp->obj->methods->intersect)(cp->obj->obj,
&tmpray, SHADOW_EPSILON, &s)) {
/* Hit cached object. */
CacheHits++;
return TRUE;
}
/*
* Did not hit anything -- zero out the cache.
*/
CacheMisses++;
/*
* Transformed -- reset s for use below.
*/
s = dist;
cp->obj = (Geom *)NULL;
cp->dotrans = FALSE;
}
hitlist.nodes = 0;
if (!TraceRay(ray, &hitlist, SHADOW_EPSILON, &s)) {
/* Shadow ray didn't hit anything. */
*result = *color;
return FALSE;
}
/*
* Otherwise, we've hit something.
*/
ShadowHits++;
/*
* If we're not worrying about transparent objects...
* This is ugly due to the fact that we have to find
* the surface associated with the object that was hit.
* GetShadingSurf() will always return a non-null value.
*
* ***NOTE**
* The transparency of the surface is checked below without
* applying textures, if any, to it. This means that if
* an object may be made trasparent by a texture, its
* surface should have non-zero transparency *before* texturing
* as well.
*/
if (!SHADOWTRANSP(ShadowOptions)) {
if (SHADOWCACHE(ShadowOptions))
LightCacheHit(&hitlist, cp);
return TRUE;
}
/*
* We've hit a transparent object. Attenuate the color of the light
* source and continue the ray until we hit background or a
* non-transparent object. Note that this is incorrect if DefIndex or
* any of the indices of refraction of the surfaces differ.
*/
totaldist = 0.;
prevsurf = (Surface *)NULL;
res = *color;
do {
/*
* Get a pointer to the surface to be used
* for shading...
*/
sptr = GetShadingSurf(&hitlist);
if (sptr->transp < EPSILON) {
if (SHADOWCACHE(ShadowOptions))
LightCacheHit(&hitlist, cp);
return TRUE;
}
/*
* Take specular transmission attenuation from
* previous intersection into account.
*/
if (prevsurf) {
if (prevsurf->statten != 1.) {
statten = pow(prevsurf->statten, s - totaldist);
ColorScale(statten, res, &res);
}
}
/*
* Perform texturing and the like in case surface
* transparency is modulated.
*/
/* copy the surface to be used... */
surf = *sptr;
enter = ComputeSurfProps(&hitlist, ray, &hitpos,
&norm, &gnorm, &surf, &smooth);
if (enter)
prevsurf = &surf;
else
prevsurf = (Surface *)NULL;
/*
* Attenuate light source by body color of surface.
*/
ColorScale(surf.transp, res, &res);
ColorMultiply(res, surf.body, &res);
/*
* Return if attenuation becomes large.
* In this case, the light was attenuated to nothing,
* so we can't cache anything...
*/
if (res.r < EPSILON && res.g < EPSILON && res.b < EPSILON)
return TRUE;
/*
* Min distance is previous max.
*/
totaldist = s + EPSILON;
/*
* Max distance is dist to light source
*/
s = dist;
/*
* Trace ray starting at new origin and in the
* same direction.
*/
hitlist.nodes = 0;
} while (TraceRay(ray, &hitlist, totaldist, &s));
*result = res;
return FALSE;
}
void
ShadowStats(shadowrays, shadowhit, cachehit, cachemiss)
unsigned long *shadowrays, *shadowhit, *cachehit, *cachemiss;
{
*shadowrays = ShadowRays;
*shadowhit = ShadowHits;
*cachehit = CacheHits;
*cachemiss = CacheMisses;
}
void
ShadowSetOptions(options)
long options;
{
ShadowOptions = options;
}
void
LightCacheHit(hitlist, cache)
HitList *hitlist;
ShadowCache *cache;
{
HitNode *np;
int i, n;
extern long ShadowOptions;
i = 0;
if (SHADOWCSG(ShadowOptions)) {
/*
* There's possibly a CSG object in the
* hitlist, so we can't simply cache the
* primitive that was hit. Find the
* object lowest in hit that's not part
* of a CSG object, and cache it.
*/
i = FirstCSGGeom(hitlist);
}
if (SHADOWBLUR(ShadowOptions)) {
/*
* Something might be animated --
* gotta cache the puppy.
*/
n = FirstAnimatedGeom(hitlist);
if (n > i)
i = n;
}
/*
* Compute total world-->cached object
* transformation and store in cache->trans.
*/
/*
* Find the first transformation...
*/
np = &hitlist->data[i];
cache->obj = np->obj;
/*
* If the cached object is animated, then we don't
* want to include the object's transformation(s)
* in cache->trans (it's taken care of in shadowed()
* by calling intersect).
*/
if (cache->obj->animtrans) {
i++;
np++;
}
cache->dotrans = FALSE;
while (i < hitlist->nodes -1) {
if (np->obj->trans) {
if (cache->dotrans) {
MatrixMult(
&np->obj->trans->itrans,
&cache->trans,
&cache->trans);
} else {
MatrixCopy(
&np->obj->trans->itrans,
&cache->trans);
cache->dotrans = TRUE;
}
}
i++;
np++;
}
}