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