| 1 |
greg |
2.1 |
#ifndef lint |
| 2 |
greg |
2.5 |
static const char RCSid[] = "$Id: m_wgmdf.c,v 2.4 2024/12/11 18:32:26 greg Exp $"; |
| 3 |
greg |
2.1 |
#endif |
| 4 |
|
|
/* |
| 5 |
|
|
* Shading function for programmable Ward-Geisler-Moroder-Duer material. |
| 6 |
|
|
*/ |
| 7 |
|
|
|
| 8 |
|
|
#include "copyright.h" |
| 9 |
|
|
|
| 10 |
|
|
#include "ray.h" |
| 11 |
|
|
#include "ambient.h" |
| 12 |
|
|
#include "otypes.h" |
| 13 |
|
|
#include "rtotypes.h" |
| 14 |
|
|
#include "source.h" |
| 15 |
|
|
#include "func.h" |
| 16 |
|
|
#include "random.h" |
| 17 |
|
|
#include "pmapmat.h" |
| 18 |
|
|
|
| 19 |
|
|
#ifndef MAXITER |
| 20 |
|
|
#define MAXITER 10 /* maximum # specular ray attempts */ |
| 21 |
|
|
#endif |
| 22 |
|
|
/* estimate of Fresnel function */ |
| 23 |
|
|
#define FRESNE(ci) (exp(-5.85*(ci)) - 0.00202943064) |
| 24 |
|
|
#define FRESTHRESH 0.017999 /* minimum specularity for approx. */ |
| 25 |
|
|
|
| 26 |
|
|
/* |
| 27 |
|
|
* This routine implements the anisotropic Gaussian |
| 28 |
|
|
* model described by Ward in a 1992 Siggraph article and updated by |
| 29 |
|
|
* Geisler-Moroder and Duer in a 2010 article in High Performance Graphics. |
| 30 |
|
|
* We do not reorient incoming ray, using side in part to determine |
| 31 |
|
|
* reflectance values. Most parameters are programmable with their own |
| 32 |
|
|
* modifiers and/or value expressions. |
| 33 |
|
|
* |
| 34 |
|
|
* Arguments for MAT_WGMDF are: |
| 35 |
|
|
* 13+ rs_mod rs rs_urough rs_vrough |
| 36 |
|
|
* ts_mod ts ts_urough ts_vrough |
| 37 |
|
|
* td_mod |
| 38 |
|
|
* ux uy uz funcfile transform |
| 39 |
|
|
* 0 |
| 40 |
|
|
* 9+ rfdif gfdif bfdif |
| 41 |
|
|
* rbdif gbdif bbdif |
| 42 |
|
|
* rtdif gtdif btdif |
| 43 |
|
|
* A10 .. |
| 44 |
|
|
* |
| 45 |
|
|
* Where the rs_urough or rs_vrough expression yields zero, mirror-Fresnel |
| 46 |
|
|
* effects are computed, similar to MAT_PLASTIC and MAT_METAL. The |
| 47 |
|
|
* rs* expressions should not vary with incident angle, or the material |
| 48 |
|
|
* will not be physically valid. Similarly, the ts* expressions should |
| 49 |
|
|
* give the same value for coincident direction vectors from either side. |
| 50 |
|
|
* There are independent modifiers for specular reflection, |
| 51 |
|
|
* transmission, and diffuse transmission. Diffuse reflection |
| 52 |
|
|
* applies the material's main modifier, which doesn't apply to |
| 53 |
|
|
* anything else by default. However, any of the modifiers may be |
| 54 |
|
|
* ALIASMOD, which will use the main material modifier, or VOIDID, |
| 55 |
|
|
* which will just be white. |
| 56 |
|
|
* Diffuse reflection and transmission colors and patterns add to |
| 57 |
|
|
* the specular components, and are only adjusted with mirror-Fresnel |
| 58 |
|
|
* reflection if specular reflection is greater than FRESHTHRESH. The |
| 59 |
|
|
* specular transmission is likewise adjusted in such cases. Specified |
| 60 |
|
|
* values for all components should sum to less than 1, but like other |
| 61 |
|
|
* Radiance materials, this is not enforced, nor is a warning issued. |
| 62 |
|
|
*/ |
| 63 |
|
|
/* specularity flags */ |
| 64 |
|
|
#define SP_REFL 01 /* has reflected specular component */ |
| 65 |
|
|
#define SP_TRAN 02 /* has transmitted specular */ |
| 66 |
|
|
#define SP_RPURE 04 /* mirror reflection */ |
| 67 |
|
|
#define SP_TPURE 010 /* has view component */ |
| 68 |
|
|
#define SP_FLAT 020 /* flat reflecting surface */ |
| 69 |
|
|
#define SP_RBLT 040 /* reflection below sample threshold */ |
| 70 |
|
|
#define SP_TBLT 0100 /* transmission below threshold */ |
| 71 |
|
|
|
| 72 |
|
|
typedef struct { |
| 73 |
|
|
char *nam; /* modifier name */ |
| 74 |
|
|
int hastexture; /* has a texture? */ |
| 75 |
|
|
FVECT pnorm; /* perturbed normal direction */ |
| 76 |
|
|
double pdot; /* perturbed dot product */ |
| 77 |
|
|
SCOLOR pcol; /* pattern color */ |
| 78 |
|
|
} MODVAL; /* modifier-derived values */ |
| 79 |
|
|
|
| 80 |
|
|
typedef struct { |
| 81 |
|
|
MODVAL mo; /* modifier parameters */ |
| 82 |
|
|
SCOLOR scol; /* modified diffuse color */ |
| 83 |
|
|
} DCOMP; /* diffuse component parameters */ |
| 84 |
|
|
|
| 85 |
|
|
typedef struct { |
| 86 |
|
|
MODVAL mo; /* modifier parameters */ |
| 87 |
|
|
SCOLOR scol; /* modified specular color */ |
| 88 |
|
|
FVECT u, v; /* u and v in-plane vectors */ |
| 89 |
|
|
double u_alpha; /* u roughness */ |
| 90 |
|
|
double v_alpha; /* v roughness */ |
| 91 |
|
|
} SCOMP; /* specular component parameters */ |
| 92 |
|
|
|
| 93 |
|
|
typedef struct { |
| 94 |
|
|
RAY *rp; /* ray pointer */ |
| 95 |
|
|
OBJREC *mtp; /* material pointer */ |
| 96 |
|
|
MFUNC *mf; /* pointer to expression list */ |
| 97 |
|
|
int specfl; /* specularity flags, defined above */ |
| 98 |
|
|
FVECT ulocal; /* u-vector in local coordinates */ |
| 99 |
|
|
DCOMP rd, td; /* diffuse component params */ |
| 100 |
|
|
SCOMP rs, ts; /* specular component params */ |
| 101 |
|
|
FVECT prdir; /* vector in transmitted direction */ |
| 102 |
|
|
} WGMDDAT; /* WGMD material data */ |
| 103 |
|
|
|
| 104 |
|
|
#define clr_comps(wp) ((wp)->specfl = 0, \ |
| 105 |
|
|
(wp)->rd.mo.nam = (wp)->td.mo.nam = \ |
| 106 |
|
|
(wp)->rs.mo.nam = (wp)->ts.mo.nam = "") |
| 107 |
|
|
|
| 108 |
|
|
/* assign modifier values */ |
| 109 |
|
|
static int |
| 110 |
|
|
set_modval(MODVAL *mp, OBJECT omod, const RAY *r) |
| 111 |
|
|
{ |
| 112 |
|
|
RAY tr; |
| 113 |
|
|
|
| 114 |
|
|
if (!mp->nam[0]) |
| 115 |
|
|
mp->nam = (omod == OVOID) ? VOIDID : objptr(omod)->oname; |
| 116 |
|
|
else if (!strcmp(mp->nam, VOIDID)) |
| 117 |
|
|
omod = OVOID; |
| 118 |
|
|
else if (omod == OVOID) |
| 119 |
|
|
return(0); |
| 120 |
|
|
tr = *r; /* independent modifier */ |
| 121 |
|
|
raytexture(&tr, omod); |
| 122 |
|
|
if (DOT(tr.pert,tr.pert) > FTINY*FTINY) { |
| 123 |
|
|
mp->pdot = raynormal(mp->pnorm, &tr); |
| 124 |
|
|
mp->hastexture = 1; |
| 125 |
|
|
} else { |
| 126 |
|
|
VCOPY(mp->pnorm, tr.ron); |
| 127 |
|
|
mp->pdot = tr.rod; |
| 128 |
|
|
mp->hastexture = 0; |
| 129 |
|
|
} |
| 130 |
|
|
copyscolor(mp->pcol, tr.pcol); |
| 131 |
|
|
return(1); |
| 132 |
|
|
} |
| 133 |
|
|
|
| 134 |
|
|
/* fill modifier values, using previous setting if found */ |
| 135 |
|
|
static int |
| 136 |
|
|
fill_modval(MODVAL *mp, const WGMDDAT *wp) |
| 137 |
|
|
{ |
| 138 |
|
|
if (mp == &wp->rd.mo) { /* special case (should be first) */ |
| 139 |
|
|
set_modval(mp, wp->mtp->omod, wp->rp); |
| 140 |
|
|
return(1); |
| 141 |
|
|
} /* use main modifier? */ |
| 142 |
|
|
if (!strcmp(mp->nam, ALIASMOD) || !strcmp(mp->nam, wp->rd.mo.nam)) { |
| 143 |
|
|
*mp = wp->rd.mo; |
| 144 |
|
|
return(1); |
| 145 |
|
|
} /* check others */ |
| 146 |
|
|
if (mp != &wp->td.mo && !strcmp(mp->nam, wp->td.mo.nam)) { |
| 147 |
|
|
*mp = wp->td.mo; |
| 148 |
|
|
return(1); |
| 149 |
|
|
} |
| 150 |
|
|
if (mp != &wp->rs.mo && !strcmp(mp->nam, wp->rs.mo.nam)) { |
| 151 |
|
|
*mp = wp->rs.mo; |
| 152 |
|
|
return(1); |
| 153 |
|
|
} |
| 154 |
|
|
if (mp != &wp->ts.mo && !strcmp(mp->nam, wp->ts.mo.nam)) { |
| 155 |
|
|
*mp = wp->ts.mo; |
| 156 |
|
|
return(1); |
| 157 |
|
|
} /* new modifier */ |
| 158 |
|
|
return(set_modval(mp, lastmod(objndx(wp->mtp), mp->nam), wp->rp)); |
| 159 |
|
|
} |
| 160 |
|
|
|
| 161 |
greg |
2.5 |
static int |
| 162 |
|
|
setWGMDfunc(MODVAL *mp, const WGMDDAT *wp) |
| 163 |
|
|
{ |
| 164 |
|
|
static char lastMod[MAXSTR] = ""; |
| 165 |
|
|
double sf; |
| 166 |
|
|
FVECT vec; |
| 167 |
|
|
|
| 168 |
|
|
if (setfunc(wp->mtp, wp->rp) == 0 && |
| 169 |
|
|
!strcmp(mp->nam, lastMod)) |
| 170 |
|
|
return(0); /* already set */ |
| 171 |
|
|
/* else (re)assign special variables */ |
| 172 |
|
|
strcpy(lastMod, mp->nam); |
| 173 |
|
|
sf = (wp->rp->rod > 0) ? 1. : -1.; |
| 174 |
|
|
varset("RdotP`", '=', (-1. < mp->pdot) & (mp->pdot < 1.) |
| 175 |
|
|
? sf*mp->pdot : 1.); |
| 176 |
|
|
sf /= funcxf.sca; |
| 177 |
|
|
multv3(vec, mp->pnorm, funcxf.xfm); |
| 178 |
|
|
varset("NxP`", '=', vec[0]*sf); |
| 179 |
|
|
varset("NyP`", '=', vec[1]*sf); |
| 180 |
|
|
varset("NzP`", '=', vec[2]*sf); |
| 181 |
|
|
return(1); |
| 182 |
|
|
} |
| 183 |
|
|
|
| 184 |
greg |
2.1 |
/* assign indicated diffuse component (do !trans first) */ |
| 185 |
|
|
static void |
| 186 |
|
|
set_dcomp(WGMDDAT *wp, int trans) |
| 187 |
|
|
{ |
| 188 |
|
|
DCOMP *dp = trans ? &wp->td : &wp->rd; |
| 189 |
|
|
const int offs = trans ? 6 : 3*(wp->rp->rod < 0); |
| 190 |
|
|
|
| 191 |
|
|
if (trans) { /* transmitted diffuse? */ |
| 192 |
|
|
if (intens(wp->mtp->oargs.farg+offs) <= FTINY) { |
| 193 |
|
|
scolorblack(dp->scol); |
| 194 |
|
|
return; |
| 195 |
|
|
} |
| 196 |
|
|
dp->mo.nam = wp->mtp->oargs.sarg[8]; |
| 197 |
|
|
if (!fill_modval(&dp->mo, wp)) { |
| 198 |
|
|
sprintf(errmsg, |
| 199 |
|
|
"unknown diffuse transmission modifier '%s'", |
| 200 |
|
|
dp->mo.nam); |
| 201 |
|
|
objerror(wp->mtp, USER, errmsg); |
| 202 |
|
|
} |
| 203 |
|
|
} else /* no priors for main mod */ |
| 204 |
|
|
fill_modval(&dp->mo, wp); |
| 205 |
|
|
|
| 206 |
|
|
setscolor(dp->scol, wp->mtp->oargs.farg[offs], |
| 207 |
|
|
wp->mtp->oargs.farg[offs+1], |
| 208 |
|
|
wp->mtp->oargs.farg[offs+2]); |
| 209 |
|
|
smultscolor(dp->scol, dp->mo.pcol); |
| 210 |
|
|
} |
| 211 |
|
|
|
| 212 |
|
|
/* assign indicated specular component */ |
| 213 |
|
|
static void |
| 214 |
|
|
set_scomp(WGMDDAT *wp, int trans) |
| 215 |
|
|
{ |
| 216 |
|
|
SCOMP *sp = trans ? &wp->ts : &wp->rs; |
| 217 |
|
|
const int eoff = 3*(trans != 0); |
| 218 |
|
|
double coef; |
| 219 |
greg |
2.5 |
/* constant zero check */ |
| 220 |
|
|
if (wp->mf->ep[eoff]->type == NUM && |
| 221 |
|
|
wp->mf->ep[eoff]->v.num <= FTINY) { |
| 222 |
|
|
scolorblack(sp->scol); |
| 223 |
|
|
return; |
| 224 |
|
|
} /* need modifier */ |
| 225 |
|
|
sp->mo.nam = wp->mtp->oargs.sarg[4*(trans != 0)]; |
| 226 |
|
|
if (!fill_modval(&sp->mo, wp)) { |
| 227 |
|
|
sprintf(errmsg, "unknown specular %s modifier '%s'", |
| 228 |
|
|
trans ? "transmission" : "reflection", sp->mo.nam); |
| 229 |
|
|
objerror(wp->mtp, USER, errmsg); |
| 230 |
|
|
} |
| 231 |
|
|
setWGMDfunc(&sp->mo, wp); |
| 232 |
greg |
2.1 |
errno = 0; |
| 233 |
|
|
coef = evalue(wp->mf->ep[eoff]); |
| 234 |
|
|
if ((errno == EDOM) | (errno == ERANGE)) { |
| 235 |
|
|
objerror(wp->mtp, WARNING, "specular compute error"); |
| 236 |
|
|
scolorblack(sp->scol); |
| 237 |
|
|
return; |
| 238 |
|
|
} |
| 239 |
|
|
if (coef <= FTINY) { /* negligible value? */ |
| 240 |
|
|
scolorblack(sp->scol); |
| 241 |
|
|
return; |
| 242 |
|
|
} |
| 243 |
|
|
copyscolor(sp->scol, sp->mo.pcol); |
| 244 |
|
|
scalescolor(sp->scol, coef); |
| 245 |
|
|
if (sintens(sp->scol) <= FTINY) { |
| 246 |
|
|
scolorblack(sp->scol); |
| 247 |
|
|
return; /* got black pattern */ |
| 248 |
|
|
} |
| 249 |
greg |
2.5 |
errno = 0; /* else get roughness */ |
| 250 |
greg |
2.1 |
sp->u_alpha = evalue(wp->mf->ep[eoff+1]); |
| 251 |
|
|
sp->v_alpha = (sp->u_alpha > FTINY) ? evalue(wp->mf->ep[eoff+2]) : 0.0; |
| 252 |
|
|
if ((errno == EDOM) | (errno == ERANGE)) { |
| 253 |
|
|
objerror(wp->mtp, WARNING, "roughness compute error"); |
| 254 |
|
|
scolorblack(sp->scol); |
| 255 |
|
|
return; |
| 256 |
|
|
} /* we have something... */ |
| 257 |
|
|
wp->specfl |= trans ? SP_TRAN : SP_REFL; |
| 258 |
|
|
if (sp->v_alpha <= FTINY) { /* is it pure specular? */ |
| 259 |
|
|
wp->specfl |= trans ? SP_TPURE : SP_RPURE; |
| 260 |
|
|
sp->u_alpha = sp->v_alpha = 0.0; |
| 261 |
|
|
return; |
| 262 |
|
|
} |
| 263 |
|
|
/* get anisotropic coordinates */ |
| 264 |
|
|
fcross(sp->v, sp->mo.pnorm, wp->ulocal); |
| 265 |
|
|
if (normalize(sp->v) == 0.0) { /* orientation vector==normal? */ |
| 266 |
|
|
if (fabs(sp->u_alpha - sp->v_alpha) > 0.001) |
| 267 |
|
|
objerror(wp->mtp, WARNING, "bad orientation vector"); |
| 268 |
|
|
getperpendicular(sp->u, sp->mo.pnorm, 1); /* punting */ |
| 269 |
|
|
fcross(sp->v, sp->mo.pnorm, sp->u); |
| 270 |
|
|
sp->u_alpha = sp->v_alpha = sqrt( 0.5 * |
| 271 |
|
|
(sp->u_alpha*sp->u_alpha + sp->v_alpha*sp->v_alpha) ); |
| 272 |
|
|
} else |
| 273 |
|
|
fcross(sp->u, sp->v, sp->mo.pnorm); |
| 274 |
|
|
} |
| 275 |
|
|
|
| 276 |
|
|
/* sample anisotropic Gaussian specular */ |
| 277 |
|
|
static void |
| 278 |
|
|
agaussamp(WGMDDAT *wp) |
| 279 |
|
|
{ |
| 280 |
|
|
RAY sr; |
| 281 |
|
|
FVECT h; |
| 282 |
|
|
double rv[2]; |
| 283 |
|
|
double d, sinp, cosp; |
| 284 |
|
|
int maxiter, ntrials, nstarget, nstaken; |
| 285 |
|
|
int i; |
| 286 |
|
|
/* compute reflection */ |
| 287 |
|
|
if ((wp->specfl & (SP_REFL|SP_RPURE|SP_RBLT)) == SP_REFL && |
| 288 |
|
|
rayorigin(&sr, RSPECULAR, wp->rp, wp->rs.scol) == 0) { |
| 289 |
|
|
SCOLOR scol; |
| 290 |
|
|
nstarget = 1; |
| 291 |
|
|
if (specjitter > 1.5) { /* multiple samples? */ |
| 292 |
|
|
nstarget = specjitter*wp->rp->rweight + .5; |
| 293 |
|
|
if (sr.rweight <= minweight*nstarget) |
| 294 |
|
|
nstarget = sr.rweight/minweight; |
| 295 |
|
|
if (nstarget > 1) { |
| 296 |
|
|
d = 1./nstarget; |
| 297 |
|
|
scalescolor(sr.rcoef, d); |
| 298 |
|
|
sr.rweight *= d; |
| 299 |
|
|
} else |
| 300 |
|
|
nstarget = 1; |
| 301 |
|
|
} |
| 302 |
|
|
scolorblack(scol); |
| 303 |
|
|
dimlist[ndims++] = (int)(size_t)wp->mtp; |
| 304 |
|
|
maxiter = MAXITER*nstarget; |
| 305 |
|
|
for (nstaken = ntrials = 0; (nstaken < nstarget) & |
| 306 |
|
|
(ntrials < maxiter); ntrials++) { |
| 307 |
|
|
if (ntrials) |
| 308 |
|
|
d = frandom(); |
| 309 |
|
|
else |
| 310 |
|
|
d = urand(ilhash(dimlist,ndims)+samplendx); |
| 311 |
|
|
multisamp(rv, 2, d); |
| 312 |
|
|
d = 2.0*PI * rv[0]; |
| 313 |
|
|
cosp = tcos(d) * wp->rs.u_alpha; |
| 314 |
|
|
sinp = tsin(d) * wp->rs.v_alpha; |
| 315 |
|
|
d = 1./sqrt(cosp*cosp + sinp*sinp); |
| 316 |
|
|
cosp *= d; |
| 317 |
|
|
sinp *= d; |
| 318 |
|
|
if ((0. <= specjitter) & (specjitter < 1.)) |
| 319 |
|
|
rv[1] = 1.0 - specjitter*rv[1]; |
| 320 |
|
|
d = (rv[1] <= FTINY) ? 1.0 : sqrt( -log(rv[1]) / |
| 321 |
|
|
(cosp*cosp/(wp->rs.u_alpha*wp->rs.u_alpha) + |
| 322 |
|
|
sinp*sinp/(wp->rs.v_alpha*wp->rs.v_alpha)) ); |
| 323 |
|
|
for (i = 0; i < 3; i++) |
| 324 |
|
|
h[i] = wp->rs.mo.pnorm[i] + |
| 325 |
|
|
d*(cosp*wp->rs.u[i] + sinp*wp->rs.v[i]); |
| 326 |
|
|
d = -2.0 * DOT(h, wp->rp->rdir) / (1.0 + d*d); |
| 327 |
|
|
VSUM(sr.rdir, wp->rp->rdir, h, d); |
| 328 |
|
|
/* sample rejection test */ |
| 329 |
|
|
d = DOT(sr.rdir, wp->rp->ron); |
| 330 |
|
|
if ((d > 0) ^ (wp->rp->rod > 0)) |
| 331 |
|
|
continue; |
| 332 |
|
|
checknorm(sr.rdir); |
| 333 |
|
|
if (nstarget > 1) { /* W-G-M-D adjustment */ |
| 334 |
|
|
if (nstaken) rayclear(&sr); |
| 335 |
|
|
rayvalue(&sr); |
| 336 |
|
|
d = 2./(1. + wp->rp->rod/d); |
| 337 |
|
|
scalescolor(sr.rcol, d); |
| 338 |
|
|
saddscolor(scol, sr.rcol); |
| 339 |
|
|
} else { |
| 340 |
|
|
rayvalue(&sr); |
| 341 |
|
|
smultscolor(sr.rcol, sr.rcoef); |
| 342 |
|
|
saddscolor(wp->rp->rcol, sr.rcol); |
| 343 |
|
|
} |
| 344 |
|
|
++nstaken; |
| 345 |
|
|
} |
| 346 |
|
|
if (nstarget > 1) { /* final W-G-M-D weighting */ |
| 347 |
|
|
smultscolor(scol, sr.rcoef); |
| 348 |
|
|
d = (double)nstarget/ntrials; |
| 349 |
|
|
scalescolor(scol, d); |
| 350 |
|
|
saddscolor(wp->rp->rcol, scol); |
| 351 |
|
|
} |
| 352 |
|
|
ndims--; |
| 353 |
|
|
} |
| 354 |
|
|
/* compute transmission */ |
| 355 |
|
|
if ((wp->specfl & (SP_TRAN|SP_TPURE|SP_TBLT)) == SP_TRAN && |
| 356 |
|
|
rayorigin(&sr, TSPECULAR, wp->rp, wp->ts.scol) == 0) { |
| 357 |
|
|
nstarget = 1; |
| 358 |
|
|
if (specjitter > 1.5) { /* multiple samples? */ |
| 359 |
|
|
nstarget = specjitter*wp->rp->rweight + .5; |
| 360 |
|
|
if (sr.rweight <= minweight*nstarget) |
| 361 |
|
|
nstarget = sr.rweight/minweight; |
| 362 |
|
|
if (nstarget > 1) { |
| 363 |
|
|
d = 1./nstarget; |
| 364 |
|
|
scalescolor(sr.rcoef, d); |
| 365 |
|
|
sr.rweight *= d; |
| 366 |
|
|
} else |
| 367 |
|
|
nstarget = 1; |
| 368 |
|
|
} |
| 369 |
|
|
dimlist[ndims++] = (int)(size_t)wp->mtp; |
| 370 |
|
|
maxiter = MAXITER*nstarget; |
| 371 |
|
|
for (nstaken = ntrials = 0; (nstaken < nstarget) & |
| 372 |
|
|
(ntrials < maxiter); ntrials++) { |
| 373 |
|
|
if (ntrials) |
| 374 |
|
|
d = frandom(); |
| 375 |
|
|
else |
| 376 |
|
|
d = urand(ilhash(dimlist,ndims)+1823+samplendx); |
| 377 |
|
|
multisamp(rv, 2, d); |
| 378 |
|
|
d = 2.0*PI * rv[0]; |
| 379 |
|
|
cosp = tcos(d) * wp->ts.u_alpha; |
| 380 |
|
|
sinp = tsin(d) * wp->ts.v_alpha; |
| 381 |
|
|
d = 1./sqrt(cosp*cosp + sinp*sinp); |
| 382 |
|
|
cosp *= d; |
| 383 |
|
|
sinp *= d; |
| 384 |
|
|
if ((0. <= specjitter) & (specjitter < 1.)) |
| 385 |
|
|
rv[1] = 1.0 - specjitter*rv[1]; |
| 386 |
|
|
if (rv[1] <= FTINY) |
| 387 |
|
|
d = 1.0; |
| 388 |
|
|
else |
| 389 |
|
|
d = sqrt(-log(rv[1]) / |
| 390 |
|
|
(cosp*cosp/(wp->ts.u_alpha*wp->ts.u_alpha) + |
| 391 |
|
|
sinp*sinp/(wp->ts.v_alpha*wp->ts.v_alpha))); |
| 392 |
|
|
for (i = 0; i < 3; i++) |
| 393 |
|
|
sr.rdir[i] = wp->prdir[i] + |
| 394 |
|
|
d*(cosp*wp->ts.u[i] + sinp*wp->ts.v[i]); |
| 395 |
|
|
/* rejection test */ |
| 396 |
|
|
if ((DOT(sr.rdir,wp->rp->ron) > 0) == (wp->rp->rod > 0)) |
| 397 |
|
|
continue; |
| 398 |
|
|
normalize(sr.rdir); /* OK, normalize */ |
| 399 |
|
|
if (nstaken) /* multi-sampling? */ |
| 400 |
|
|
rayclear(&sr); |
| 401 |
|
|
rayvalue(&sr); |
| 402 |
|
|
smultscolor(sr.rcol, sr.rcoef); |
| 403 |
|
|
saddscolor(wp->rp->rcol, sr.rcol); |
| 404 |
|
|
++nstaken; |
| 405 |
|
|
} |
| 406 |
|
|
ndims--; |
| 407 |
|
|
} |
| 408 |
|
|
} |
| 409 |
|
|
|
| 410 |
|
|
/* compute source contribution for MAT_WGMDF */ |
| 411 |
|
|
static void |
| 412 |
|
|
dirwgmdf(SCOLOR scval, void *uwp, FVECT ldir, double omega) |
| 413 |
|
|
{ |
| 414 |
|
|
WGMDDAT *wp = (WGMDDAT *)uwp; |
| 415 |
|
|
const int hitfront = (wp->rp->rod > 0); |
| 416 |
|
|
double fresadj = 1.; |
| 417 |
|
|
double ldot; |
| 418 |
|
|
double dtmp, dtmp1, dtmp2; |
| 419 |
|
|
FVECT h; |
| 420 |
|
|
double au2, av2; |
| 421 |
|
|
SCOLOR sctmp; |
| 422 |
|
|
|
| 423 |
|
|
scolorblack(scval); /* will add component coefficients */ |
| 424 |
|
|
|
| 425 |
|
|
/* XXX ignores which side is lit */ |
| 426 |
|
|
if (wp->specfl & SP_RPURE && pbright(wp->rs.scol) >= FRESTHRESH) |
| 427 |
|
|
fresadj = 1. - FRESNE(fabs(DOT(wp->rs.mo.pnorm,ldir))); |
| 428 |
|
|
|
| 429 |
|
|
if (sintens(wp->rd.scol) > FTINY && |
| 430 |
|
|
((ldot = DOT(wp->rd.mo.pnorm,ldir)) > 0) == hitfront) { |
| 431 |
|
|
/* |
| 432 |
|
|
* Compute diffuse reflection coefficient for source. |
| 433 |
|
|
*/ |
| 434 |
|
|
copyscolor(sctmp, wp->rd.scol); |
| 435 |
|
|
dtmp = fabs(ldot) * omega * (1.0/PI) * fresadj; |
| 436 |
|
|
scalescolor(sctmp, dtmp); |
| 437 |
|
|
saddscolor(scval, sctmp); |
| 438 |
|
|
} |
| 439 |
|
|
if (sintens(wp->td.scol) > FTINY && |
| 440 |
|
|
((ldot = DOT(wp->td.mo.pnorm,ldir)) > 0) ^ hitfront) { |
| 441 |
|
|
/* |
| 442 |
|
|
* Compute diffuse transmission coefficient for source. |
| 443 |
|
|
*/ |
| 444 |
|
|
copyscolor(sctmp, wp->td.scol); |
| 445 |
|
|
dtmp = fabs(ldot) * omega * (1.0/PI) * fresadj; |
| 446 |
|
|
scalescolor(sctmp, dtmp); |
| 447 |
|
|
saddscolor(scval, sctmp); |
| 448 |
|
|
} |
| 449 |
|
|
#if 0 /* XXX not yet implemented */ |
| 450 |
|
|
if (ambRayInPmap(wp->rp)) |
| 451 |
|
|
return; /* specular accounted for in photon map */ |
| 452 |
|
|
#endif |
| 453 |
|
|
if ((wp->specfl & (SP_REFL|SP_RPURE)) == SP_REFL && |
| 454 |
|
|
((ldot = DOT(wp->rs.mo.pnorm,ldir)) > 0) == hitfront) { |
| 455 |
|
|
/* |
| 456 |
|
|
* Compute specular reflection coefficient for source using |
| 457 |
|
|
* anisotropic Gaussian distribution model. |
| 458 |
|
|
*/ |
| 459 |
|
|
/* add source width if flat */ |
| 460 |
|
|
if (wp->specfl & SP_FLAT) |
| 461 |
|
|
au2 = av2 = omega * (0.25/PI); |
| 462 |
|
|
else |
| 463 |
|
|
au2 = av2 = 0.0; |
| 464 |
|
|
au2 += wp->rs.u_alpha*wp->rs.u_alpha; |
| 465 |
|
|
av2 += wp->rs.v_alpha*wp->rs.v_alpha; |
| 466 |
|
|
/* half vector */ |
| 467 |
|
|
VSUB(h, ldir, wp->rp->rdir); |
| 468 |
|
|
/* ellipse */ |
| 469 |
|
|
dtmp1 = DOT(wp->rs.u, h); |
| 470 |
|
|
dtmp1 *= dtmp1 / au2; |
| 471 |
|
|
dtmp2 = DOT(wp->rs.v, h); |
| 472 |
|
|
dtmp2 *= dtmp2 / av2; |
| 473 |
|
|
/* W-G-M-D model */ |
| 474 |
|
|
dtmp = DOT(wp->rs.mo.pnorm, h); |
| 475 |
|
|
dtmp *= dtmp; |
| 476 |
|
|
dtmp1 = (dtmp1 + dtmp2) / dtmp; |
| 477 |
|
|
dtmp = exp(-dtmp1) * DOT(h,h) / |
| 478 |
|
|
(PI * dtmp*dtmp * sqrt(au2*av2)); |
| 479 |
|
|
|
| 480 |
|
|
if (dtmp > FTINY) { /* worth using? */ |
| 481 |
|
|
copyscolor(sctmp, wp->rs.scol); |
| 482 |
|
|
dtmp *= fabs(ldot) * omega; |
| 483 |
|
|
scalescolor(sctmp, dtmp); |
| 484 |
|
|
saddscolor(scval, sctmp); |
| 485 |
|
|
} |
| 486 |
|
|
} |
| 487 |
|
|
if ((wp->specfl & (SP_TRAN|SP_TPURE)) == SP_TRAN && |
| 488 |
|
|
((ldot = DOT(wp->ts.mo.pnorm,ldir)) > 0) ^ hitfront) { |
| 489 |
|
|
/* |
| 490 |
|
|
* Compute specular transmission coefficient for source. |
| 491 |
|
|
*/ |
| 492 |
|
|
/* roughness + source */ |
| 493 |
|
|
au2 = av2 = omega * (1.0/PI); |
| 494 |
|
|
au2 += wp->ts.u_alpha*wp->ts.u_alpha; |
| 495 |
|
|
av2 += wp->ts.v_alpha*wp->ts.v_alpha; |
| 496 |
|
|
/* "half vector" */ |
| 497 |
|
|
VSUB(h, ldir, wp->prdir); |
| 498 |
|
|
dtmp = DOT(h,h); |
| 499 |
|
|
if (dtmp > FTINY*FTINY) { |
| 500 |
|
|
dtmp1 = DOT(h,wp->ts.mo.pnorm); |
| 501 |
|
|
dtmp = 1.0 - dtmp1*dtmp1/dtmp; |
| 502 |
|
|
} |
| 503 |
|
|
if (dtmp > FTINY*FTINY) { |
| 504 |
|
|
dtmp1 = DOT(h,wp->ts.u); |
| 505 |
|
|
dtmp1 *= dtmp1 / au2; |
| 506 |
|
|
dtmp2 = DOT(h,wp->ts.v); |
| 507 |
|
|
dtmp2 *= dtmp2 / av2; |
| 508 |
|
|
dtmp = (dtmp1 + dtmp2) / dtmp; |
| 509 |
|
|
dtmp = exp(-dtmp); |
| 510 |
|
|
} else |
| 511 |
|
|
dtmp = 1.0; |
| 512 |
|
|
/* Gaussian */ |
| 513 |
|
|
dtmp *= (1.0/PI) * sqrt(-ldot/(wp->ts.mo.pdot*au2*av2)); |
| 514 |
|
|
|
| 515 |
|
|
if (dtmp > FTINY) { /* worth using? */ |
| 516 |
|
|
copyscolor(sctmp, wp->ts.scol); |
| 517 |
|
|
dtmp *= omega; |
| 518 |
|
|
scalescolor(sctmp, dtmp); |
| 519 |
|
|
saddscolor(scval, sctmp); |
| 520 |
|
|
} |
| 521 |
|
|
} |
| 522 |
|
|
} |
| 523 |
|
|
|
| 524 |
|
|
/* color a ray that hit a programmable WGMD material */ |
| 525 |
|
|
int |
| 526 |
|
|
m_wgmdf(OBJREC *m, RAY *r) |
| 527 |
|
|
{ |
| 528 |
|
|
RAY lr; |
| 529 |
|
|
WGMDDAT wd; |
| 530 |
|
|
SCOLOR sctmp; |
| 531 |
|
|
FVECT anorm; |
| 532 |
|
|
int i; |
| 533 |
|
|
|
| 534 |
|
|
if (!backvis & (r->rod < 0.0)) { |
| 535 |
|
|
raytrans(r); |
| 536 |
|
|
return(1); /* backside invisible */ |
| 537 |
|
|
} |
| 538 |
|
|
if ((m->oargs.nsargs < 13) | (m->oargs.nfargs < 9)) |
| 539 |
|
|
objerror(m, USER, "bad number of arguments"); |
| 540 |
greg |
2.2 |
|
| 541 |
|
|
if (r->crtype & SHADOW && !strcmp(m->oargs.sarg[5], "0")) |
| 542 |
|
|
return(1); /* first shadow test */ |
| 543 |
greg |
2.1 |
clr_comps(&wd); |
| 544 |
|
|
wd.rp = r; |
| 545 |
|
|
wd.mtp = m; |
| 546 |
|
|
wd.mf = getfunc(m, 12, 0xEEE, 1); |
| 547 |
greg |
2.5 |
set_dcomp(&wd, 0); /* gets main modifier */ |
| 548 |
|
|
setWGMDfunc(&wd.rd.mo, &wd); /* get local u vector */ |
| 549 |
greg |
2.1 |
errno = 0; |
| 550 |
|
|
for (i = 0; i < 3; i++) |
| 551 |
|
|
wd.ulocal[i] = evalue(wd.mf->ep[6+i]); |
| 552 |
|
|
if ((errno == EDOM) | (errno == ERANGE)) |
| 553 |
|
|
wd.ulocal[0] = wd.ulocal[1] = wd.ulocal[2] = 0.0; |
| 554 |
|
|
else if (wd.mf->fxp != &unitxf) |
| 555 |
|
|
multv3(wd.ulocal, wd.ulocal, wd.mf->fxp->xfm); |
| 556 |
|
|
|
| 557 |
|
|
set_scomp(&wd, 1); /* sets SP_TPURE */ |
| 558 |
|
|
if (r->crtype & SHADOW && !(wd.specfl & SP_TPURE)) |
| 559 |
greg |
2.2 |
return(1); /* second shadow test */ |
| 560 |
|
|
set_dcomp(&wd, 1); |
| 561 |
greg |
2.1 |
set_scomp(&wd, 0); |
| 562 |
|
|
wd.specfl |= SP_FLAT*(r->ro != NULL && isflat(r->ro->otype)); |
| 563 |
|
|
/* apply Fresnel adjustments? */ |
| 564 |
|
|
if (wd.specfl & SP_RPURE && pbright(wd.rs.scol) >= FRESTHRESH) { |
| 565 |
|
|
const double fest = FRESNE(fabs(wd.rs.mo.pdot)); |
| 566 |
|
|
for (i = NCSAMP; i--; ) |
| 567 |
|
|
wd.rs.scol[i] += fest*(1. - wd.rs.scol[i]); |
| 568 |
greg |
2.4 |
scalescolor(wd.rd.scol, 1.-fest); |
| 569 |
greg |
2.1 |
scalescolor(wd.ts.scol, 1.-fest); |
| 570 |
|
|
scalescolor(wd.td.scol, 1.-fest); |
| 571 |
|
|
} |
| 572 |
|
|
/* check specular thresholds */ |
| 573 |
|
|
wd.specfl |= SP_RBLT*((wd.specfl & (SP_REFL|SP_RPURE)) == SP_REFL && |
| 574 |
|
|
specthresh >= pbright(wd.rs.scol)-FTINY); |
| 575 |
|
|
wd.specfl |= SP_TBLT*((wd.specfl & (SP_TRAN|SP_TPURE)) == SP_TRAN && |
| 576 |
|
|
specthresh >= pbright(wd.ts.scol)-FTINY); |
| 577 |
|
|
/* get through direction */ |
| 578 |
|
|
if (wd.specfl & SP_TRAN && wd.ts.mo.hastexture && |
| 579 |
|
|
!(r->crtype & (SHADOW|AMBIENT))) { |
| 580 |
|
|
for (i = 0; i < 3; i++) /* perturb */ |
| 581 |
|
|
wd.prdir[i] = r->rdir[i] - wd.ts.mo.pnorm[i] + r->ron[i]; |
| 582 |
|
|
if ((DOT(wd.prdir,r->ron) > 0) ^ (r->rod > 0)) |
| 583 |
|
|
normalize(wd.prdir); /* OK */ |
| 584 |
|
|
else /* too much */ |
| 585 |
|
|
VCOPY(wd.prdir, r->rdir); |
| 586 |
|
|
} else |
| 587 |
|
|
VCOPY(wd.prdir, r->rdir); |
| 588 |
|
|
/* transmitted view ray? */ |
| 589 |
|
|
if ((wd.specfl & (SP_TRAN|SP_TPURE|SP_TBLT)) == (SP_TRAN|SP_TPURE) && |
| 590 |
|
|
rayorigin(&lr, TRANS, r, wd.ts.scol) == 0) { |
| 591 |
|
|
VCOPY(lr.rdir, wd.prdir); |
| 592 |
|
|
rayvalue(&lr); |
| 593 |
|
|
smultscolor(lr.rcol, lr.rcoef); |
| 594 |
|
|
saddscolor(r->rcol, lr.rcol); |
| 595 |
|
|
if (scolor_mean(wd.ts.scol) >= 0.999) { |
| 596 |
|
|
/* completely transparent */ |
| 597 |
|
|
smultscolor(lr.mcol, lr.rcoef); |
| 598 |
|
|
copyscolor(r->mcol, lr.mcol); |
| 599 |
|
|
r->rmt = r->rot + lr.rmt; |
| 600 |
|
|
r->rxt = r->rot + lr.rxt; |
| 601 |
|
|
} else if (pbright(wd.ts.scol) > |
| 602 |
|
|
pbright(wd.td.scol) + pbright(wd.rd.scol)) |
| 603 |
|
|
r->rxt = r->rot + raydistance(&lr); |
| 604 |
|
|
} |
| 605 |
|
|
if (r->crtype & SHADOW) |
| 606 |
|
|
return(1); /* the rest is shadow */ |
| 607 |
|
|
/* mirror ray? */ |
| 608 |
|
|
if ((wd.specfl & (SP_REFL|SP_RPURE|SP_RBLT)) == (SP_REFL|SP_RPURE) && |
| 609 |
|
|
rayorigin(&lr, REFLECTED, r, wd.rs.scol) == 0) { |
| 610 |
|
|
VSUM(lr.rdir, r->rdir, wd.rs.mo.pnorm, 2.*wd.rs.mo.pdot); |
| 611 |
|
|
/* fall back if would penetrate */ |
| 612 |
|
|
if (wd.rs.mo.hastexture && |
| 613 |
|
|
(DOT(lr.rdir,r->ron) > 0) ^ (r->rod > 0)) |
| 614 |
|
|
VSUM(lr.rdir, r->rdir, r->ron, 2.*r->rod); |
| 615 |
|
|
checknorm(lr.rdir); |
| 616 |
|
|
rayvalue(&lr); |
| 617 |
|
|
smultscolor(lr.rcol, lr.rcoef); |
| 618 |
|
|
copyscolor(r->mcol, lr.rcol); |
| 619 |
|
|
saddscolor(r->rcol, lr.rcol); |
| 620 |
|
|
r->rmt = r->rot; |
| 621 |
|
|
if (wd.specfl & SP_FLAT && |
| 622 |
|
|
!wd.rs.mo.hastexture | (r->crtype & AMBIENT)) |
| 623 |
|
|
r->rmt += raydistance(&lr); |
| 624 |
|
|
} |
| 625 |
|
|
if (wd.specfl & (SP_REFL|SP_TRAN)) /* specularly scattered rays */ |
| 626 |
|
|
agaussamp(&wd); /* checks *BLT flags */ |
| 627 |
|
|
|
| 628 |
|
|
if (sintens(wd.rd.scol) > FTINY) { /* ambient from this side */ |
| 629 |
|
|
if (r->rod > 0) { |
| 630 |
|
|
VCOPY(anorm, wd.rd.mo.pnorm); |
| 631 |
|
|
} else { |
| 632 |
|
|
anorm[0] = -wd.rd.mo.pnorm[0]; |
| 633 |
|
|
anorm[1] = -wd.rd.mo.pnorm[1]; |
| 634 |
|
|
anorm[2] = -wd.rd.mo.pnorm[2]; |
| 635 |
|
|
} |
| 636 |
|
|
copyscolor(sctmp, wd.rd.scol); |
| 637 |
|
|
if (wd.specfl & SP_RBLT) /* add in specular as well? */ |
| 638 |
|
|
saddscolor(sctmp, wd.rs.scol); |
| 639 |
|
|
multambient(sctmp, r, anorm); |
| 640 |
|
|
saddscolor(r->rcol, sctmp); /* add to returned color */ |
| 641 |
|
|
} |
| 642 |
|
|
if (sintens(wd.td.scol) > FTINY) { /* ambient from other side */ |
| 643 |
|
|
if (r->rod > 0) { |
| 644 |
|
|
anorm[0] = -wd.td.mo.pnorm[0]; |
| 645 |
|
|
anorm[1] = -wd.td.mo.pnorm[1]; |
| 646 |
|
|
anorm[2] = -wd.td.mo.pnorm[2]; |
| 647 |
|
|
} else { |
| 648 |
|
|
VCOPY(anorm, wd.td.mo.pnorm); |
| 649 |
|
|
} |
| 650 |
|
|
copyscolor(sctmp, wd.td.scol); |
| 651 |
|
|
if (wd.specfl & SP_TBLT) /* add in specular as well? */ |
| 652 |
|
|
saddscolor(sctmp, wd.ts.scol) |
| 653 |
|
|
multambient(sctmp, r, anorm); |
| 654 |
|
|
saddscolor(r->rcol, sctmp); |
| 655 |
|
|
} |
| 656 |
|
|
direct(r, dirwgmdf, &wd); /* add direct component last */ |
| 657 |
|
|
return(1); |
| 658 |
|
|
} |