/************************************************************************ * * * Copyright (c) 1987, David B. Wecker * * All Rights Reserved * * * * This file is part of DBW_Render * * * * DBW_Render is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY. No author or distributor accepts * * responsibility to anyone for the consequences of using it or for * * whether it serves any particular purpose or works at all, unless * * he says so in writing. Refer to the DBW_Render General Public * * License for full details. * * * * Everyone is granted permission to copy, modify and redistribute * * DBW_Render, but only under the conditions described in the * * DBW_Render General Public License. A copy of this license is * * supposed to have been given to you along with DBW_Render so you * * can know your rights and responsibilities. It should be in a file * * named COPYING. Among other things, the copyright notice and this * * notice must be preserved on all copies. * ************************************************************************ * * * Authors: * * DBW - David B. Wecker * * * * Versions: * * V1.0 870125 DBW - First released version * * * ************************************************************************/ #define MODULE_VAL #include "ray.h" void getval(val,np,p,d,atten,ambientlight) vector val, p, d; node *np; float atten; int ambientlight; { vector pseudodirection, pseudointensity, tp, lightdir, lightint, penumbra, transparency, best_p, nval, n, r, temp, specintensity, diffuse, v1; float best_t, ndotdir, t, texture, td, pointdist, umbscale; float ndotd, ambientscale; int hitnext, lit, i, itd; node *bouncenp, *occlude; vector light_atten[MAXLIT]; int l_objcounter; float l_distances[MAXOBJ]; node *l_objpairs[MAXOBJ]; vector l_points[MAXOBJ]; if (allopaque == 0) { /* If not opaque,then save the ray intersection list */ l_objcounter = g_objcounter; for (i = 0; i < l_objcounter; i++) { l_distances[i] = g_distances[i]; l_objpairs[i] = g_objpairs[i]; veccopy(g_points[i],l_points[i]); } } veczero(val); /* start with output value of zero */ findnormal(np,p,n); ndotd = DOT(n,d); if (ndotd > 0.0) { /* We're looking at surface's back side */ ndotd = -ndotd; n[0] = -n[0]; /* Flip the normal */ n[1] = -n[1]; n[2] = -n[2]; } /* Initialize the light attenuation values with 'not-yet-computed' flag */ for (lit = 0; lit < numlits; lit++) light_atten[lit][0] = -99.9; /* Apply procedural texture */ gettex(diffuse,np,p,n); /*---------------------------------------------------------------------*/ /* Compute diffuse reflection intensity */ if (diffuse[0] > 0.01 || diffuse[1] > 0.01 || diffuse[2] > 0.01) { /* First,compute light shining directly on this point */ for (lit = 0; lit < numlits; lit++) { veccopy(light[lit].direction,lightdir); veccopy(light[lit].intensity,lightint); pointdist = 0.0; if (light[lit].kind != 0) { /* If point source,compute local direction */ vecsub(lightdir,p,lightdir); pointdist = NORM(lightdir); /* distance to point light source */ if (pointdist == 0.0) pointdist = 0.01; umbscale = light[lit].distscale; umbscale /= pointdist * pointdist; vecscale(umbscale,lightint,lightint); CV(rnd(),rnd(),rnd(),penumbra); if (rnd() < 0.5) penumbra[0] = -penumbra[0]; if (rnd() < 0.5) penumbra[1] = -penumbra[1]; if (rnd() < 0.5) penumbra[2] = -penumbra[2]; normalize(penumbra); /* penumbra is now random unit vector */ umbscale = rnd(); umbscale -= 0.05; umbscale *= light[lit].radius; vecscale(umbscale,penumbra,penumbra); vecsum(penumbra,lightdir,lightdir); normalize(lightdir); } /* Now that we know where the light is shining from,compute diffuse (lamertian) reflection */ ndotdir = DOT(n,lightdir); if (ndotdir > 0.0) { vecmul(lightint,diffuse,nval); vecscale(ndotdir,nval,nval); } else { veczero(nval); /* self shadowing -- backside of spheres,etc. */ } /* If the computed diffuse surface brightness is above a certain threshold level,then compute the shadow attenuation */ if (nval[0] > 0.01 || nval[1] > 0.01 || nval[2] > 0.01) { getatten(light_atten[lit],p,lightdir,lit,pointdist); vecmul(nval,light_atten[lit],nval); /* attenuate */ } vecsum(nval,val,val); /* Sum up for all light sources */ } /* for */ /*---------------------------------------------------------------------*/ /* Next,compute light shining indirectly from any surfaces on this */ /* point. This is done to approximate true ambient illumination. */ for (lit = 0; lit < ambientlight; lit++) { ambientscale = 1.0 / (float) ambientlight; /* ?? not sure how to factor in distance from diffuse reflector */ CV(rnd(),rnd(),rnd(),lightdir); if (rnd() < 0.5) lightdir[0] = -lightdir[0]; if (rnd() < 0.5) lightdir[1] = -lightdir[1]; if (rnd() < 0.5) lightdir[2] = -lightdir[2]; normalize(lightdir); /* lightdir is now random unit vector */ ndotdir = DOT(n,lightdir); if (ndotdir < 0.0) { /* if not in upper hemisphere,invert it */ lightdir[0] = -lightdir[0]; lightdir[1] = -lightdir[1]; lightdir[2] = -lightdir[2]; ndotdir = -ndotdir; } /* okay,now the direction is known. What is visible there? */ dodirection(nval,p,lightdir,ambientscale, ambientlight * amblitnum / amblitdenom); val[0] += nval[0] * ndotdir * diffuse[0]; val[1] += nval[1] * ndotdir * diffuse[1]; val[2] += nval[2] * ndotdir * diffuse[2]; } /* for */ /*---------------------------------------------------------------------*/ /* Next,compute light shining indirectly from mirrors on this point */ if (dopseudo) { for (lit = 0; lit < numlits; lit++) { /* for each true light... */ bouncenp = root; while (bouncenp) { /* for each possible mirror... */ if (bouncelighting(pseudointensity,pseudodirection,bouncenp,lit) && bouncenp != np) { /* don't check reflections from ourselves */ /* Okay,this object reflects light. Does any of it reach us? */ CV(1.0,1.0,1.0,transparency); #ifdef MCH_AMIGA occlude = 1L; #else occlude = (node *)1L; #endif hitnext = 0; all_intersects(p,pseudodirection,0); while (occlude && (transparency[0] > 0.01 || transparency[1] > 0.01 || transparency[2] > 0.01)) { if (occlude = get_next_intersection(hitnext,best_p,&best_t)) { /* pseudolight intersects an object,see if any passes through */ if (occlude == bouncenp) occlude = 0; /* hit the mirror causing the pseudolight,stop */ else { vecmul(transparency,occlude->attr.tra, transparency); hitnext++; } } else { /* no more objects to intersect -- we didn't intersect the mirror that is causing the pseudolight,so cancel the pseudolight */ veczero(transparency); } } if (transparency[0] > 0.01 || transparency[1] > 0.01 || transparency[2] > 0.01) { ndotdir = DOT(n,pseudodirection); if (ndotdir > 0.0) { for (i = 0; i < 3; i++) val[i] += transparency[i] * pseudointensity[i] * ndotdir * diffuse[i]; } } } /* if */ bouncenp = bouncenp->next; /* next possible mirror */ } /* while */ } /* for */ } /* if dopseudo */ } /* if any diffuse reflectivity */ /*---------------------------------------------------------------------*/ /* Compute mirror & specular reflection intensity */ atten *= np->attr.ref; /* factor in the object's relative reflectivity */ if (atten > 0.01) { /* Is object meaningfully reflective still */ vecscale(2 * ndotd,n,r); vecsub(d,r,r); dodirection(nval,p,r,atten,ambientlight); /* Mirror reflection */ vecscale(atten,nval,nval); /* attenuate by reflectivity coeff */ /* We really want Fresnel angle factor here,but... */ if (np->kind == SPHERE) { /* Add sparkle */ t = DOT(n,r); if (t >= 0.0) { veccopy(nval,specintensity); t = 1.0 - t; t = t * t * t * t * t; vecscale(t,specintensity,specintensity); vecsum(nval,specintensity,nval); } } /* Compute specular reflection intensity */ for (lit = 0; lit < numlits; lit++) { veccopy(light[lit].direction,lightdir); veccopy(light[lit].intensity,lightint); pointdist = 0.0; if (light[lit].kind != 0) { /* If point source,compute local direction */ vecsub(lightdir,p,lightdir); pointdist = NORM(lightdir); /* distance to point light source */ if (pointdist == 0.0) pointdist = 0.01; umbscale = light[lit].distscale; umbscale /= pointdist * pointdist; vecscale(umbscale,lightint,lightint); normalize(lightdir); } t = DOT(lightdir,r); if (t > 0.0) { /* Check self-shadowing */ if (np->attr.fuz == 0.0) td = 1000.0; /* extremely smooth mirror */ else { td = 2.0/np->attr.fuz; /* calc specular decay rate based on fuz */ if (td < 1.0) td = 1.0; } t = pow(t,td) /* calc reflectivity for desired gloss */ * atten; /* attenuate specular reflections by reflectivity coeff */ if (t > 0.01) { /* specular coefficient is meaningful,add it */ /* first compute shadowing of light source causing specular refl. */ if (light_atten[lit][0] < 0.0) /* Not already computed,compute */ getatten(light_atten[lit],p,lightdir,lit,pointdist); nval[0] += lightint[0] * light_atten[lit][0] * t; nval[1] += lightint[1] * light_atten[lit][1] * t; nval[2] += lightint[2] * light_atten[lit][2] * t; } } } vecsum(nval,val,val); /* Add mirror/specular reflections to running total */ } /* if reflective atten is non-zero */ /*----------------------------------------------------------------------*/ /* Add in the object's ambient intensity */ if (ambscale < 0.0) { vecsum(np->attr.amb,val,val); } else { /* use specified computed ambient */ val[0] += diffuse[0] * ambscale; val[1] += diffuse[1] * ambscale; val[2] += diffuse[2] * ambscale; } /*---------------------------------------------------------------------*/ /* Apply any post-process global texture functions */ if (numhazes > 0) { /* Blend the final color toward the specified haze color */ vecsub(p,eye,temp); texture = NORM(temp); /* distance to point from eye */ texture /= haze[numhazes-1].distscale; /* scaled according to user */ blendcolor(val,haze[numhazes-1].color,texture,val); } if (allopaque == 0) { /* If not opaque,then restore the ray intersection list */ g_objcounter = l_objcounter; for (i = 0; i < g_objcounter; i++) { g_distances[i] = l_distances[i]; g_objpairs[i] = l_objpairs[i]; veccopy(l_points[i],g_points[i]); } } }