This is shade.c in view mode; [Download] [Up]
/*
* shade.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$
*
* $Log$
*/
#include "rayshade.h"
#include "libtext/texture.h"
#include "libsurf/surface.h"
#include "liblight/light.h"
#include "libsurf/atmosphere.h"
#include "options.h"
#include "stats.h"
Medium TopMedium;
static void shade(), LightRay(), Lighting();
/*
* Calculate color of ray.
*/
void
ShadeRay(hitlist, ray, dist, back, color, contrib)
HitList *hitlist; /* Information about point of intersection. */
Ray *ray; /* Direction and origin of ray. */
Float dist; /* Distance from origin of intersection. */
Color *back; /* "Background" color */
Color *color; /* Color to assign current ray. */
Float contrib; /* Contribution of this ray to final color */
{
Vector norm, gnorm, pos; /* surface normal, point of intersection */
Surface surf; /* surface properties */
Medium *medium; /* medium through which ray is traveling */
int enter, smooth; /* entering ?, gnorm != snorm ?*/
/*
* If ray medium isn't set, use default.
*/
if (ray->media)
medium = ray->media;
else
medium = &TopMedium;
if (hitlist->nodes == 0) {
/*
* No valid intersection. Set distance for atmospheric
* effects and set color of ray to background.
*/
*color = *back;
VecAddScaled(ray->pos, FAR_AWAY, ray->dir, &pos);
if (medium->atmos)
Atmospherics(medium->atmos, ray, FAR_AWAY, &pos, color);
return;
}
/*
* Compute normal, surface properties, etc.
*/
enter = ComputeSurfProps(hitlist, ray, &pos, &norm, &gnorm, &surf,
&smooth);
Stats.HitRays++;
/*
* Calculate ray color.
*/
shade(&pos, ray, &norm, &gnorm, smooth, enter, &surf, back, color,
contrib);
if (medium->atmos)
Atmospherics(medium->atmos, ray, dist, &pos, color);
}
/*
* Perform lighting calculations based on surface normal & other properties,
* incident ray direction and position, and light source properties.
* Spawn any necessary reflected and transmitted rays.
*/
static void
shade(pos, ray, nrm, gnrm, smooth, enter, surf, back, color, contrib)
Vector *pos, *nrm, *gnrm; /* hit pos, shade normal, geo normal */
int smooth; /* true if shading norm and geo norm differ */
int enter; /* TRUE if entering surface */
Ray *ray; /* indicent ray */
Surface *surf; /* properties of hit surface */
Color *back, *color; /* background color, computed color */
Float contrib; /* contribution to final pixel value */
{
Float dist, k; /* hit distance, -ray . normal */
Color newcol; /* color of spawned rays. */
HitList hittmp; /* Object intersection record */
Ray NewRay; /* spawned ray */
Vector refl; /* reflected direction */
int total_int_refl; /* total internal reflection flag */
Float reflectivity; /* effective surface reflectivity */
Light *lp; /* current light source */
extern Light *Lights; /* list of defined sources */
/*
* Ambient color is always included.
*/
*color = surf->amb;
/*
* Calculate direction of reflected ray.
*/
k = -dotp(&ray->dir, nrm);
VecAddScaled(ray->dir, 2.*k, *nrm, &refl);
/*
* Calculate intensity contributed by each light source.
*/
for (lp = Lights; lp; lp = lp->next)
LightRay(lp, pos, nrm, gnrm, smooth, &refl, surf,
ray->depth, ray->sample, color);
if (ray->depth >= Options.maxdepth)
/*
* Don't spawn any transmitted/reflected rays.
*/
return;
/*
* Specular transmission (refraction).
*/
reflectivity = surf->refl;
total_int_refl = FALSE;
if (surf->transp != 0. && contrib * surf->transp > Options.cutoff) {
NewRay.pos = *pos; /* Origin == hit point */
NewRay.media = ray->media; /* Media == old media */
NewRay.sample = ray->sample;
NewRay.depth = ray->depth + 1;
if (enter) {
/*
* Entering surface.
*/
if (Refract(&NewRay.dir,
NewRay.media ? NewRay.media->index :
TopMedium.index, surf->kref, ray->dir, *nrm, k)) {
total_int_refl = TRUE;
} else {
NewRay.media = MediumPush(surf->kref,
surf->atmos, NewRay.media);
}
} else {
/*
* Exiting surface
* Pop medium from stack.
* Hmm, maybe should use
* popped medium rather than surf's kref?
*/
if (NewRay.media != (Medium *)0)
NewRay.media = NewRay.media->next;
if (Refract(&NewRay.dir, surf->kref,
NewRay.media ? NewRay.media->index :
TopMedium.index, ray->dir, *nrm, k)) {
total_int_refl = TRUE;
}
}
if (total_int_refl) {
reflectivity += surf->transp;
} else {
Stats.RefractRays++;
hittmp.nodes = 0;
dist = FAR_AWAY;
(void)TraceRay(&NewRay, &hittmp, EPSILON, &dist);
ShadeRay(&hittmp, &NewRay, dist, back, &newcol,
contrib * surf->transp);
ColorAddScaled(*color, surf->transp, newcol, color);
/* Free pushed medium */
if (enter)
free((voidstar)NewRay.media);
}
}
/*
* Specular reflection.
*/
if (reflectivity > 0. && contrib * reflectivity > Options.cutoff) {
NewRay.pos = *pos; /* Origin == hit point */
NewRay.dir = refl; /* Direction == reflection */
NewRay.media = ray->media; /* Medium == old medium */
NewRay.sample = ray->sample;
NewRay.depth = ray->depth + 1;
Stats.ReflectRays++;
hittmp.nodes = 0;
dist = FAR_AWAY;
(void)TraceRay(&NewRay, &hittmp, EPSILON, &dist);
ShadeRay(&hittmp, &NewRay, dist, back, &newcol,
contrib*reflectivity);
ColorAddScaled(*color, reflectivity, newcol, color);
}
}
/*
* Lighting calculations
*/
static void
LightRay(lp, pos, norm, gnorm, smooth, reflect, surf, depth, samp, color)
Light *lp; /* Light source */
Vector *pos, *norm, *gnorm; /* hit pos, shade norm, geo norm */
int smooth; /* true if shade and geo norm differ */
Vector *reflect; /* reflection direction */
Surface *surf; /* surface characteristics */
int depth, samp; /* ray depth, sample # */
Color *color; /* resulting color */
{
Color lcolor;
Ray newray;
Float costheta, cosalpha, dist;
newray.pos = *pos;
newray.depth = depth;
newray.sample = samp;
newray.media = (Medium *)NULL;
LightDirection(lp, pos, &newray.dir, &dist);
costheta = dotp(&newray.dir, norm);
if (smooth) {
cosalpha = dotp(&newray.dir, gnorm);
/*
* If shading normal indicates self-shadowing
* and geom normal indicates no self-shadowing,
* trust the geom normal.
*/
if (costheta <= 0. && cosalpha > 0.)
costheta = cosalpha;
/*
* If geom normal indicates self-shadowing and
* geom normal doesn't, then have to do something
* clever ala Snyder & Barr.
*/
}
if (costheta <= 0.) {
/*
* Light source is on opposite side of surface.
*/
if (surf->translucency <= 0.)
return;
if (!LightIntens(lp, &newray, dist,
(int)surf->noshadow, &lcolor))
return;
cosalpha = -dotp(reflect, &newray.dir);
/*
* attenuate the light source color by the translucency
*/
ColorScale(surf->translucency, lcolor, &lcolor);
Lighting(-costheta, cosalpha, &lcolor, &surf->diff,
&surf->spec, surf->stcoef, color);
} else {
if (!LightIntens(lp, &newray, dist,
(int)surf->noshadow, &lcolor))
return; /* prim is in shadow w.r.t light source */
cosalpha = dotp(reflect, &newray.dir);
Lighting(costheta, cosalpha, &lcolor, &surf->diff,
&surf->spec, surf->coef, color);
}
}
/*
* Compute shading function (diffuse reflection and specular highlight)
*
* This function *adds* the computed color to "color".
*/
static void
Lighting(costheta, cosalpha, lcolor, diff, spec, coef, color)
Float costheta, cosalpha, coef;
Color *diff, *spec, *color, *lcolor;
{
Float intens;
/*
* Diffuse reflection.
* Falls off as the cosine of the angle between
* the normal and the ray to the light (costheta).
*/
color->r += diff->r * costheta * lcolor->r;
color->g += diff->g * costheta * lcolor->g;
color->b += diff->b * costheta * lcolor->b;
/*
* Specularly reflected highlights.
* Fall off as the cosine of the angle
* between the reflected ray and the ray to the light source.
*/
if (coef < EPSILON || cosalpha <= 0.)
return;
/*
* Specular highlight = cosine of the angle raised to the
* appropriate power.
*/
intens = pow(cosalpha, coef);
color->r += spec->r * intens * lcolor->r;
color->g += spec->g * intens * lcolor->g;
color->b += spec->b * intens * lcolor->b;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.