1 /*
2  * shade.c
3  *
4  * Copyright (C) 1989, 1991, Craig E. Kolb
5  * All rights reserved.
6  *
7  * This software may be freely copied, modified, and redistributed
8  * provided that this copyright notice is preserved on all copies.
9  *
10  * You may not distribute this software, in whole or in part, as part of
11  * any commercial product without the express consent of the authors.
12  *
13  * There is no warranty or other guarantee of fitness of this software
14  * for any purpose.  It is provided solely "as is".
15  *
16  * $Id: shade.c,v 4.0 91/07/17 14:47:36 kolb Exp Locker: kolb $
17  *
18  * $Log:	shade.c,v $
19  * Revision 4.0  91/07/17  14:47:36  kolb
20  * Initial version.
21  *
22  */
23 #include "rayshade.h"
24 #include "libtext/texture.h"
25 #include "libsurf/surface.h"
26 #include "liblight/light.h"
27 #include "libsurf/atmosphere.h"
28 #include "options.h"
29 #include "stats.h"
30 
31 Medium	TopMedium;
32 Atmosphere *AtmosEffects;
33 
34 static void shade(), LightRay(), Lighting(), ReflectRay();
35 static int TransmitRay();
36 
37 /*
38  * Calculate color of ray.
39  */
40 void
ShadeRay(hitlist,ray,dist,back,color,contrib)41 ShadeRay(hitlist, ray, dist, back, color, contrib)
42 HitList *hitlist;		/* Information about point of intersection. */
43 Ray *ray;			/* Direction and origin of ray. */
44 Float dist;			/* Distance from origin of intersection. */
45 Color	*back,			/* "Background" color */
46 	*color,			/* Color to assign current ray. */
47 	*contrib;		/* Contribution of this ray to final color */
48 {
49 	Vector norm, gnorm, pos; /* surface normal, point of intersection */
50 	Surface surf, *stmp;	/* surface properties */
51 	int enter, smooth;	/* entering ?, gnorm != snorm ?*/
52 
53 	if (hitlist->nodes == 0) {
54 		/*
55 		 * No valid intersection.  Set distance for atmospheric
56 		 * effects and set color of ray to background.
57 		 */
58 		*color = *back;
59 		VecAddScaled(ray->pos, FAR_AWAY, ray->dir, &pos);
60 		if (!ray->media && AtmosEffects)
61 			Atmospherics(AtmosEffects, ray, FAR_AWAY, &pos, color);
62 		return;
63 	}
64 
65 	/*
66 	 * Compute normal, surface properties, etc.
67 	 */
68 	stmp = GetShadingSurf(hitlist);
69 	surf = *stmp;
70 	enter = ComputeSurfProps(hitlist, ray, &pos, &norm, &gnorm, &surf,
71 			&smooth);
72 	Stats.HitRays++;
73 
74 	/*
75 	 * Calculate ray color.
76 	 */
77 	shade(&pos, ray, &norm, &gnorm, smooth, enter, &surf, back, color,
78 			contrib);
79 	if (!ray->media && AtmosEffects)
80 		Atmospherics(AtmosEffects, ray, dist, &pos, color);
81 }
82 
83 /*
84  * Perform lighting calculations based on surface normal & other properties,
85  * incident ray direction and position, and light source properties.
86  * Spawn any necessary reflected and transmitted rays.
87  */
88 static void
shade(pos,ray,nrm,gnrm,smooth,enter,surf,back,color,contrib)89 shade(pos, ray, nrm, gnrm, smooth, enter, surf, back, color, contrib)
90 Vector *pos, *nrm, *gnrm;	/* hit pos, shade normal, geo normal */
91 int smooth;			/* true if shading norm and geo norm differ */
92 int enter;			/* TRUE if entering surface */
93 Ray *ray;			/* indicent ray */
94 Surface *surf;			/* properties of hit surface */
95 Color *back, *color;		/* background color, computed color */
96 Color *contrib;			/* contribution to final pixel value */
97 {
98 	Float	k;		/* -ray . normal */
99 	Color	newcontrib;
100 	Vector	refl;		/* reflected direction */
101 	Color	reflectivity,	/* effective surface reflectivity */
102 		intens;		/* reflected/transmitted intensity */
103 	Light *lp;		/* current light source */
104 	extern Light *Lights;	/* list of defined sources */
105 
106 	/*
107 	 * Ambient color is always included.
108 	 */
109 	ColorMultiply(surf->amb, Options.ambient, color);
110 
111 	/*
112 	 * Calculate direction of reflected ray.
113 	 */
114 	k = -dotp(&ray->dir, nrm);
115 	VecAddScaled(ray->dir, 2.*k, *nrm, &refl);
116 
117 	/*
118 	 * Calculate intensity contributed by each light source.
119 	 */
120 	for (lp = Lights; lp; lp = lp->next)
121 		LightRay(lp, pos, nrm, gnrm, smooth, &refl, surf,
122 				ray->depth, ray->sample, ray->time, color);
123 
124 	if (ray->depth >= Options.maxdepth)
125 		/*
126 		 * Don't spawn any transmitted/reflected rays.
127 		 */
128 		return;
129 	/*
130 	 * Specular transmission (refraction).
131 	 */
132 	ColorScale(surf->reflect, surf->spec, &reflectivity);
133 
134 	if (surf->transp > EPSILON) {
135 		ColorScale(surf->transp, surf->body, &intens);
136 		ColorMultiply(intens, *contrib, &newcontrib);
137 		if (newcontrib.r > Options.cutoff.r ||
138 		    newcontrib.g > Options.cutoff.g ||
139 		    newcontrib.b > Options.cutoff.b)
140 			/*
141 			 * Transmit ray.  If TIR occurs, add transmitted
142 			 * component to reflected component.  Kinda strange, but...
143 			 */
144 			if (TransmitRay(ray, pos, nrm, k, surf->index,
145 			    surf->statten, enter, back, &newcontrib, &intens, color))
146 				ColorAdd(reflectivity, intens, &reflectivity);
147 	}
148 
149 	if (reflectivity.r > EPSILON ||
150 	    reflectivity.g > EPSILON ||
151 	    reflectivity.b > EPSILON) {
152 		ColorMultiply(reflectivity, *contrib, &newcontrib);
153 		if (newcontrib.r > Options.cutoff.r ||
154 		    newcontrib.g > Options.cutoff.g ||
155 		    newcontrib.b > Options.cutoff.b)
156 			ReflectRay(ray, pos, &refl, back, &reflectivity,
157 				&newcontrib, color);
158 	}
159 }
160 
161 /*
162  * Lighting calculations
163  */
164 static void
LightRay(lp,pos,norm,gnorm,smooth,reflect,surf,depth,samp,time,color)165 LightRay(lp, pos, norm, gnorm, smooth, reflect, surf, depth, samp, time, color)
166 Light *lp;			/* Light source */
167 Vector *pos, *norm, *gnorm;	/* hit pos, shade norm, geo norm */
168 int smooth;			/* true if shade and geo norm differ */
169 Vector *reflect;		/* reflection direction */
170 Surface *surf;			/* surface characteristics */
171 int depth, samp;		/* ray depth, sample # */
172 Float time;
173 Color *color;			/* resulting color */
174 {
175 	Color lcolor;
176 	Ray newray;
177 	Float costheta, cosalpha, dist;
178 
179 	newray.pos = *pos;
180 	newray.depth = depth;
181 	newray.sample = samp;
182 	newray.time = time;
183 	newray.media = (Medium *)NULL;
184 
185 	LightDirection(lp, pos, &newray.dir, &dist);
186 
187 	costheta = dotp(&newray.dir, norm);
188 
189 	if (smooth) {
190 		cosalpha = dotp(&newray.dir, gnorm);
191 		/*
192 		 * If shading normal indicates self-shadowing
193 		 * and geom normal indicates no self-shadowing,
194 		 * trust the geom normal.
195 		 */
196 		if (costheta <= 0. && cosalpha > 0.)
197 			costheta = cosalpha;
198 		/*
199 		 * If geom normal indicates self-shadowing and
200 		 * geom normal doesn't, then have to do something
201 		 * clever ala Snyder & Barr.
202 		 */
203 	}
204 
205 	if (costheta <= 0.) {
206 		/*
207 		 * Light source is on opposite side of surface,
208 		 * hence light must be transmitted through...
209 		 */
210 		if (surf->translucency < EPSILON)
211 			return;
212 		if (!LightIntens(lp, &newray, dist,
213 			(int)surf->noshadow, &lcolor))
214 			return;
215 		cosalpha = -dotp(reflect, &newray.dir);
216 		Lighting(-costheta, cosalpha, &lcolor, &surf->translu,
217 				&surf->body, surf->stexp, color);
218 		ColorScale(surf->translucency, *color, color);
219 	} else {
220 		if (!LightIntens(lp, &newray, dist,
221 			(int)surf->noshadow, &lcolor))
222 			return;  /* prim is in shadow w.r.t light source */
223 
224 		cosalpha = dotp(reflect, &newray.dir);
225 		Lighting(costheta, cosalpha, &lcolor, &surf->diff,
226 				&surf->spec, surf->srexp, color);
227 	}
228 }
229 
230 /*
231  * Compute shading function (diffuse reflection and specular highlight)
232  *
233  * This function *adds* the computed color to "color".
234  */
235 static void
Lighting(costheta,cosalpha,lcolor,diff,spec,coef,color)236 Lighting(costheta, cosalpha, lcolor, diff, spec, coef, color)
237 Float costheta, cosalpha, coef;
238 Color *diff, *spec, *color, *lcolor;
239 {
240 	Float intens;
241 
242 	/*
243 	 * Diffuse reflection.
244 	 * Falls off as the cosine of the angle between
245 	 * the normal and the ray to the light (costheta).
246 	 */
247 	color->r += diff->r * costheta * lcolor->r;
248 	color->g += diff->g * costheta * lcolor->g;
249 	color->b += diff->b * costheta * lcolor->b;
250 	/*
251 	 * Specularly reflected highlights.
252 	 * Fall off as the cosine of the angle
253 	 * between the reflected ray and the ray to the light source.
254 	 */
255 	if (coef < EPSILON || cosalpha <= 0.)
256 		return;
257 	/*
258 	 * Specular highlight = cosine of the angle raised to the
259 	 * appropriate power.
260 	 */
261 	intens = pow(cosalpha, coef);
262 	color->r += spec->r * intens * lcolor->r;
263 	color->g += spec->g * intens * lcolor->g;
264 	color->b += spec->b * intens * lcolor->b;
265 }
266 
267 /*
268  * Spawn a transmitted ray.  Returns TRUE if total internal reflection
269  * occurs, FALSE otherwise.
270  */
271 static int
TransmitRay(ray,pos,norm,k,index,statten,enter,back,contrib,intens,color)272 TransmitRay(ray, pos, norm, k, index, statten, enter, back, contrib, intens, color)
273 Ray *ray;
274 Vector *pos, *norm;
275 Float k, index, statten;
276 int enter;
277 Color *back, *contrib, *intens, *color;
278 {
279 	int total_int_refl = FALSE;
280 	Ray NewRay;
281 	Float dist;
282 	Color newcol;
283 	HitList hittmp;		/* Geom intersection record */
284 
285 	NewRay.pos = *pos;		/* Origin == hit point */
286 	NewRay.media = ray->media;	/* Media == old media */
287 	NewRay.sample = ray->sample;
288 	NewRay.time = ray->time;
289 	NewRay.depth = ray->depth + 1;
290 
291 	if (enter) {
292 		/*
293 		 * Entering surface.
294 		 */
295 		if (Refract(&NewRay.dir,
296 		    NewRay.media ? NewRay.media->index :
297 		    TopMedium.index, index, &ray->dir, norm, k)) {
298 			total_int_refl = TRUE;
299 		} else {
300 			/*
301 			 * Push information for new medium.
302 			 */
303 			NewRay.media = MediumPush(index, statten, NewRay.media);
304 		}
305 	} else {
306 		/*
307 		 * Exiting surface
308 		 * Pop medium from stack.
309 		 */
310 		if (NewRay.media != (Medium *)0)
311 			NewRay.media = NewRay.media->next;
312 		if (Refract(&NewRay.dir, index,
313 		    NewRay.media ? NewRay.media->index :
314 		    TopMedium.index, &ray->dir, norm, k)) {
315 			total_int_refl = TRUE;
316 		}
317 	}
318 
319 	/*
320 	 * At this point, NewRay.media is the medium into which
321 	 * the new ray is entering.
322 	 */
323 
324 	if (!total_int_refl) {
325 		Stats.RefractRays++;
326 		hittmp.nodes = 0;
327 		dist = FAR_AWAY;
328 		TraceRay(&NewRay, &hittmp, EPSILON, &dist);
329 		ShadeRay(&hittmp, &NewRay, dist, back, &newcol, contrib);
330 		ColorMultiply(newcol, *intens, &newcol);
331 		/*
332 		 * Attenuate transmitted color.  Note that
333 		 * if the transmitted ray hit nothing, we still
334 		 * perform this computation, as it's possible
335 		 * that 'air' has a non-unit statten.
336 		 */
337 		statten = NewRay.media ? NewRay.media->statten :
338 			TopMedium.statten;
339 		if (statten != 1.0) {
340 			statten = pow(statten, dist);
341 			ColorScale(statten, newcol, &newcol);
342 		}
343 		ColorAdd(*color, newcol, color);
344 		/* Free pushed medium */
345 		if (enter)
346 			free((voidstar)NewRay.media);
347 	}
348 
349 	return total_int_refl;
350 }
351 
352 static void
ReflectRay(ray,pos,dir,back,intens,contrib,color)353 ReflectRay(ray, pos, dir, back, intens, contrib, color)
354 Ray *ray;
355 Vector *pos, *dir;
356 Color *back, *intens, *contrib, *color;
357 {
358 	Ray NewRay;
359 	HitList hittmp;		/* Geom intersection record */
360 	Color newcol;
361 	Float dist;
362 
363 	NewRay.pos = *pos;		/* Origin == hit point */
364 	NewRay.dir = *dir;		/* Direction == reflection */
365 	NewRay.media = ray->media;	/* Medium == old medium */
366 	NewRay.sample = ray->sample;
367 	NewRay.time = ray->time;
368 	NewRay.depth = ray->depth + 1;
369 	Stats.ReflectRays++;
370 	hittmp.nodes = 0;
371 	dist = FAR_AWAY;
372 	(void)TraceRay(&NewRay, &hittmp, EPSILON, &dist);
373 	ShadeRay(&hittmp, &NewRay, dist, back, &newcol, contrib);
374 	ColorMultiply(newcol, *intens, &newcol);
375 	ColorAdd(*color, newcol, color);
376 }
377