/* mix two colors according to weights given -- cres may be c1 or c2 */ void c_cmix(C_COLOR *cres, double w1, C_COLOR *c1, double w2, C_COLOR *c2) { double scale; int i; if (w1 == 0) { *cres = *c2; return; } if (w2 == 0) { *cres = *c1; return; } if ((c1->flags|c2->flags) & C_CDSPEC) { /* spectral mixing */ float cmix[C_CNSS]; c_ccvt(c1, C_CSSPEC|C_CSEFF); c_ccvt(c2, C_CSSPEC|C_CSEFF); if (c1->ssum*c2->ssum == 0) { *cres = c_dfcolor; return; } w1 /= c1->eff*c1->ssum; w2 /= c2->eff*c2->ssum; scale = 0.; for (i = 0; i < C_CNSS; i++) { cmix[i] = w1*c1->ssamp[i] + w2*c2->ssamp[i]; if (cmix[i] > scale) scale = cmix[i]; else if (cmix[i] < -scale) scale = -cmix[i]; } scale = C_CMAXV / scale; cres->ssum = 0; for (i = 0; i < C_CNSS; i++) cres->ssum += cres->ssamp[i] = scale*cmix[i] + frand(); cres->flags = C_CDSPEC|C_CSSPEC; } else { /* CIE xy mixing */ c_ccvt(c1, C_CSXY); c_ccvt(c2, C_CSXY); w1 *= c2->cy; w2 *= c1->cy; scale = w1 + w2; if (scale == 0.) { *cres = c_dfcolor; return; } scale = 1. / scale; cres->cx = (w1*c1->cx + w2*c2->cx) * scale; cres->cy = (w1*c1->cy + w2*c2->cy) * scale; cres->flags = C_CDXY|C_CSXY; } }
/* multiply two colors -- cres may be c1 or c2 */ double c_cmult(C_COLOR *cres, C_COLOR *c1, double y1, C_COLOR *c2, double y2) { double yres; int i; if ((c1->flags|c2->flags) & C_CDSPEC) { long cmix[C_CNSS], cmax = 0; /* spectral multiply */ c_ccvt(c1, C_CSSPEC|C_CSEFF); c_ccvt(c2, C_CSSPEC|C_CSEFF); for (i = 0; i < C_CNSS; i++) { cmix[i] = c1->ssamp[i] * c2->ssamp[i]; if (cmix[i] > cmax) cmax = cmix[i]; else if (cmix[i] < -cmax) cmax = -cmix[i]; } cmax /= C_CMAXV; if (!cmax) { *cres = c_dfcolor; return(0.); } cres->ssum = 0; for (i = 0; i < C_CNSS; i++) cres->ssum += cres->ssamp[i] = cmix[i] / cmax; cres->flags = C_CDSPEC|C_CSSPEC; c_ccvt(cres, C_CSEFF); /* below is correct */ yres = y1 * y2 * c_y31.ssum * C_CLPWM / (c1->eff*c1->ssum * c2->eff*c2->ssum) * cres->eff*( cres->ssum*(double)cmax + C_CNSS/2.0*(cmax-1) ); } else { float rgb1[3], rgb2[3]; /* CIE xy multiply */ c_toSharpRGB(c1, y1, rgb1); c_toSharpRGB(c2, y2, rgb2); rgb2[0] *= rgb1[0]; rgb2[1] *= rgb1[1]; rgb2[2] *= rgb1[2]; yres = c_fromSharpRGB(rgb2, cres); } return(yres); }
/* check if color is grey */ int c_isgrey(C_COLOR *clr) { if (!(clr->flags & (C_CSXY|C_CSSPEC))) return(1); /* no settings == grey */ c_ccvt(clr, C_CSXY); return(clr->cx >= .323 && clr->cx <= .343 && clr->cy >= .323 && clr->cy <= .343); }
void cvtcolor( /* convert a CIE XYZ color to RGB */ COLOR radrgb, register C_COLOR *ciec, double intensity ) { static COLOR ciexyz; c_ccvt(ciec, C_CSXY); /* get xy representation */ ciexyz[1] = intensity; ciexyz[0] = ciec->cx/ciec->cy*ciexyz[1]; ciexyz[2] = ciexyz[1]*(1./ciec->cy - 1.) - ciexyz[0]; cie_rgb(radrgb, ciexyz); }
/* encode (x,y) chromaticity */ C_CHROMA c_encodeChroma(C_COLOR *clr) { double df; int ub, vb; c_ccvt(clr, C_CSXY); df = UV_NORMF/(-2.*clr->cx + 12.*clr->cy + 3.); ub = 4.*clr->cx*df + frand(); if (ub > 0xff) ub = 0xff; else ub *= (ub > 0); vb = 9.*clr->cy*df + frand(); if (vb > 0xff) vb = 0xff; else vb *= (vb > 0); return(vb<<8 | ub); }
void ccy2rgb( /* convert MGF color to RGB */ C_COLOR *cin, /* input MGF chrominance */ double cieY, /* input luminance or reflectance */ COLOR cout /* output RGB color */ ) { double d; COLOR xyz; /* get CIE XYZ representation */ c_ccvt(cin, C_CSXY); d = cin->cx/cin->cy; xyz[CIEX] = d * cieY; xyz[CIEY] = cieY; xyz[CIEZ] = (1./cin->cy - d - 1.) * cieY; cie_rgb(cout, xyz); }
/* convert to sharpened RGB color for low-error operations */ void c_toSharpRGB(C_COLOR *cin, double cieY, float cout[3]) { double xyz[3]; c_ccvt(cin, C_CSXY); xyz[0] = cin->cx/cin->cy * cieY; xyz[1] = cieY; xyz[2] = (1. - cin->cx - cin->cy)/cin->cy * cieY; cout[0] = XYZtoSharp[0][0]*xyz[0] + XYZtoSharp[0][1]*xyz[1] + XYZtoSharp[0][2]*xyz[2]; cout[1] = XYZtoSharp[1][0]*xyz[0] + XYZtoSharp[1][1]*xyz[1] + XYZtoSharp[1][2]*xyz[2]; cout[2] = XYZtoSharp[2][0]*xyz[0] + XYZtoSharp[2][1]*xyz[1] + XYZtoSharp[2][2]*xyz[2]; }
/* Sample an individual BSDF component */ SDError SDsampComponent(SDValue *sv, FVECT ioVec, double randX, SDComponent *sdc) { float coef[SDmaxCh]; SDError ec; FVECT inVec; const SDCDst *cd; double d; int n; /* check arguments */ if ((sv == NULL) | (ioVec == NULL) | (sdc == NULL)) return SDEargument; /* get cumulative distribution */ VCOPY(inVec, ioVec); cd = (*sdc->func->getCDist)(inVec, sdc); if (cd == NULL) return SDEmemory; if (cd->cTotal <= 1e-6) { /* anything to sample? */ sv->spec = c_dfcolor; sv->cieY = .0; memset(ioVec, 0, 3*sizeof(double)); return SDEnone; } sv->cieY = cd->cTotal; /* compute sample direction */ ec = (*sdc->func->sampCDist)(ioVec, randX, cd); if (ec) return ec; /* get BSDF color */ n = (*sdc->func->getBSDFs)(coef, ioVec, inVec, sdc); if (n <= 0) { strcpy(SDerrorDetail, "BSDF sample value error"); return SDEinternal; } sv->spec = sdc->cspec[0]; d = coef[0]; while (--n) { c_cmix(&sv->spec, d, &sv->spec, coef[n], &sdc->cspec[n]); d += coef[n]; } /* make sure everything is set */ c_ccvt(&sv->spec, C_CSXY+C_CSSPEC); return SDEnone; }
/* Return BSDF for the given incident and scattered ray vectors */ SDError SDevalBSDF(SDValue *sv, const FVECT outVec, const FVECT inVec, const SDData *sd) { int inFront, outFront; SDSpectralDF *sdf; float coef[SDmaxCh]; int nch, i; /* check arguments */ if ((sv == NULL) | (outVec == NULL) | (inVec == NULL) | (sd == NULL)) return SDEargument; /* whose side are we on? */ inFront = (inVec[2] > 0); outFront = (outVec[2] > 0); /* start with diffuse portion */ if (inFront & outFront) { *sv = sd->rLambFront; sdf = sd->rf; } else if (!(inFront | outFront)) { *sv = sd->rLambBack; sdf = sd->rb; } else if (inFront) { *sv = sd->tLamb; sdf = (sd->tf != NULL) ? sd->tf : sd->tb; } else /* inBack */ { *sv = sd->tLamb; sdf = (sd->tb != NULL) ? sd->tb : sd->tf; } sv->cieY *= 1./M_PI; /* add non-diffuse components */ i = (sdf != NULL) ? sdf->ncomp : 0; while (i-- > 0) { nch = (*sdf->comp[i].func->getBSDFs)(coef, outVec, inVec, &sdf->comp[i]); while (nch-- > 0) { c_cmix(&sv->spec, sv->cieY, &sv->spec, coef[nch], &sdf->comp[i].cspec[nch]); sv->cieY += coef[nch]; } } /* make sure everything is set */ c_ccvt(&sv->spec, C_CSXY+C_CSSPEC); return SDEnone; }
/* Sample BSDF direction based on the given random variable */ SDError SDsampBSDF(SDValue *sv, FVECT ioVec, double randX, int sflags, const SDData *sd) { SDError ec; FVECT inVec; int inFront; SDSpectralDF *rdf, *tdf; double rdiff; float coef[SDmaxCh]; int i, j, n, nr; SDComponent *sdc; const SDCDst **cdarr = NULL; /* check arguments */ if ((sv == NULL) | (ioVec == NULL) | (sd == NULL) | (randX < 0) | (randX >= 1.)) return SDEargument; /* whose side are we on? */ VCOPY(inVec, ioVec); inFront = (inVec[2] > 0); /* remember diffuse portions */ if (inFront) { *sv = sd->rLambFront; rdf = sd->rf; tdf = (sd->tf != NULL) ? sd->tf : sd->tb; } else /* !inFront */ { *sv = sd->rLambBack; rdf = sd->rb; tdf = (sd->tb != NULL) ? sd->tb : sd->tf; } if ((sflags & SDsampDf+SDsampR) != SDsampDf+SDsampR) sv->cieY = .0; rdiff = sv->cieY; if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT) sv->cieY += sd->tLamb.cieY; /* gather non-diffuse components */ i = nr = (((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR) & (rdf != NULL)) ? rdf->ncomp : 0; j = (((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT) & (tdf != NULL)) ? tdf->ncomp : 0; n = i + j; if (n > 0 && (cdarr = (const SDCDst **)malloc(n*sizeof(SDCDst *))) == NULL) return SDEmemory; while (j-- > 0) { /* non-diffuse transmission */ cdarr[i+j] = (*tdf->comp[j].func->getCDist)(inVec, &tdf->comp[j]); if (cdarr[i+j] == NULL) { free(cdarr); return SDEmemory; } sv->cieY += cdarr[i+j]->cTotal; } while (i-- > 0) { /* non-diffuse reflection */ cdarr[i] = (*rdf->comp[i].func->getCDist)(inVec, &rdf->comp[i]); if (cdarr[i] == NULL) { free(cdarr); return SDEmemory; } sv->cieY += cdarr[i]->cTotal; } if (sv->cieY <= 1e-6) { /* anything to sample? */ sv->cieY = .0; memset(ioVec, 0, 3*sizeof(double)); return SDEnone; } /* scale random variable */ randX *= sv->cieY; /* diffuse reflection? */ if (randX < rdiff) { SDdiffuseSamp(ioVec, inFront, randX/rdiff); goto done; } randX -= rdiff; /* diffuse transmission? */ if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT) { if (randX < sd->tLamb.cieY) { sv->spec = sd->tLamb.spec; SDdiffuseSamp(ioVec, !inFront, randX/sd->tLamb.cieY); goto done; } randX -= sd->tLamb.cieY; } /* else one of cumulative dist. */ for (i = 0; i < n && randX < cdarr[i]->cTotal; i++) randX -= cdarr[i]->cTotal; if (i >= n) return SDEinternal; /* compute sample direction */ sdc = (i < nr) ? &rdf->comp[i] : &tdf->comp[i-nr]; ec = (*sdc->func->sampCDist)(ioVec, randX/cdarr[i]->cTotal, cdarr[i]); if (ec) return ec; /* compute color */ j = (*sdc->func->getBSDFs)(coef, ioVec, inVec, sdc); if (j <= 0) { sprintf(SDerrorDetail, "BSDF \"%s\" sampling value error", sd->name); return SDEinternal; } sv->spec = sdc->cspec[0]; rdiff = coef[0]; while (--j) { c_cmix(&sv->spec, rdiff, &sv->spec, coef[j], &sdc->cspec[j]); rdiff += coef[j]; } done: if (cdarr != NULL) free(cdarr); /* make sure everything is set */ c_ccvt(&sv->spec, C_CSXY+C_CSSPEC); return SDEnone; }