spectrum.c

Go to the documentation of this file.
00001 /*                      S P E C T R U M . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 2004-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** @addtogroup librt */
00023 /*@{*/
00024 /** @file spectrum.c
00025  *  An application of the 'tabdata' package to spectral data.
00026  *
00027  *  (Note that there is also a rttherm/spectrum.c)
00028  *
00029  *  Author -
00030  *      Michael John Muuss
00031  *
00032  *  Source -
00033  *      The U. S. Army Research Laboratory
00034  *      Aberdeen Proving Ground, Maryland  21005-5068  USA
00035  *
00036  *
00037  *  Inspired by -
00038  *      Roy Hall and his book "Illumination and Color in Computer
00039  *      Generated Imagery", Springer Verlag, New York, 1989.
00040  *      ISBN 0-387-96774-5
00041  *
00042  *  With thanks to Russ Moulton Jr, EOSoft Inc. for his "rad.c" module.
00043  */
00044 /*@}*/
00045 
00046 #ifndef lint
00047 static const char RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/spectrum.c,v 14.10 2006/09/16 02:04:26 lbutler Exp $ (ARL)";
00048 #endif
00049 
00050 #include "common.h"
00051 
00052 #include <stdio.h>
00053 #include <math.h>
00054 #include "machine.h"
00055 #include "vmath.h"
00056 #include "raytrace.h"
00057 #include "spectrum.h"
00058 
00059 /*
00060  *                      R T _ C I E _ X Y Z
00061  *
00062  *  This is the data for the CIE_XYZ curves take from Judd and
00063  *  Wyszecki (1975), table 2.6, these are for the 1931 standard
00064  *  observer with a 2-degree visual field.
00065  *  From Roy Hall, pg 228.
00066  */
00067 const double    rt_CIE_XYZ[81][4] = {
00068     {380, 0.0014, 0.0000, 0.0065}, {385, 0.0022, 0.0001, 0.0105},
00069     {390, 0.0042, 0.0001, 0.0201}, {395, 0.0076, 0.0002, 0.0362},
00070     {400, 0.0143, 0.0004, 0.0679}, {405, 0.0232, 0.0006, 0.1102},
00071     {410, 0.0435, 0.0012, 0.2074}, {415, 0.0776, 0.0022, 0.3713},
00072     {420, 0.1344, 0.0040, 0.6456}, {425, 0.2148, 0.0073, 1.0391},
00073     {430, 0.2839, 0.0116, 1.3856}, {435, 0.3285, 0.0168, 1.6230},
00074     {440, 0.3483, 0.0230, 1.7471}, {445, 0.3481, 0.0298, 1.7826},
00075     {450, 0.3362, 0.0380, 1.7721}, {455, 0.3187, 0.0480, 1.7441},
00076     {460, 0.2908, 0.0600, 1.6692}, {465, 0.2511, 0.0739, 1.5281},
00077     {470, 0.1954, 0.0910, 1.2876}, {475, 0.1421, 0.1126, 1.0419},
00078     {480, 0.0956, 0.1390, 0.8130}, {485, 0.0580, 0.1693, 0.6162},
00079     {490, 0.0320, 0.2080, 0.4652}, {495, 0.0147, 0.2586, 0.3533},
00080     {500, 0.0049, 0.3230, 0.2720}, {505, 0.0024, 0.4073, 0.2123},
00081     {510, 0.0093, 0.5030, 0.1582}, {515, 0.0291, 0.6082, 0.1117},
00082     {520, 0.0633, 0.7100, 0.0782}, {525, 0.1096, 0.7932, 0.0573},
00083     {530, 0.1655, 0.8620, 0.0422}, {535, 0.2257, 0.9149, 0.0298},
00084     {540, 0.2904, 0.9540, 0.0203}, {545, 0.3597, 0.9803, 0.0134},
00085     {550, 0.4334, 0.9950, 0.0087}, {555, 0.5121, 1.0000, 0.0057},
00086     {560, 0.5945, 0.9950, 0.0039}, {565, 0.6784, 0.9786, 0.0027},
00087     {570, 0.7621, 0.9520, 0.0021}, {575, 0.8425, 0.9154, 0.0018},
00088     {580, 0.9163, 0.8700, 0.0017}, {585, 0.9786, 0.8163, 0.0014},
00089     {590, 1.0263, 0.7570, 0.0011}, {595, 1.0567, 0.6949, 0.0010},
00090     {600, 1.0622, 0.6310, 0.0008}, {605, 1.0456, 0.5668, 0.0006},
00091     {610, 1.0026, 0.5030, 0.0003}, {615, 0.9384, 0.4412, 0.0002},
00092     {620, 0.8544, 0.3810, 0.0002}, {625, 0.7514, 0.3210, 0.0001},
00093     {630, 0.6424, 0.2650, 0.0000}, {635, 0.5419, 0.2170, 0.0000},
00094     {640, 0.4479, 0.1750, 0.0000}, {645, 0.3608, 0.1382, 0.0000},
00095     {650, 0.2835, 0.1070, 0.0000}, {655, 0.2187, 0.0816, 0.0000},
00096     {660, 0.1649, 0.0610, 0.0000}, {665, 0.1212, 0.0446, 0.0000},
00097     {670, 0.0874, 0.0320, 0.0000}, {675, 0.0636, 0.0232, 0.0000},
00098     {680, 0.0468, 0.0170, 0.0000}, {685, 0.0329, 0.0119, 0.0000},
00099     {690, 0.0227, 0.0082, 0.0000}, {695, 0.0158, 0.0057, 0.0000},
00100     {700, 0.0114, 0.0041, 0.0000}, {705, 0.0081, 0.0029, 0.0000},
00101     {710, 0.0058, 0.0021, 0.0000}, {715, 0.0041, 0.0015, 0.0000},
00102     {720, 0.0029, 0.0010, 0.0000}, {725, 0.0020, 0.0007, 0.0000},
00103     {730, 0.0014, 0.0005, 0.0000}, {735, 0.0010, 0.0004, 0.0000},
00104     {740, 0.0007, 0.0002, 0.0000}, {745, 0.0005, 0.0002, 0.0000},
00105     {750, 0.0003, 0.0001, 0.0000}, {755, 0.0002, 0.0001, 0.0000},
00106     {760, 0.0002, 0.0001, 0.0000}, {765, 0.0001, 0.0000, 0.0000},
00107     {770, 0.0001, 0.0000, 0.0000}, {775, 0.0001, 0.0000, 0.0000},
00108     {780, 0.0000, 0.0000, 0.0000}
00109 };
00110 
00111 /*
00112  *                      R T _ S P E C T _ M A K E _ C I E _ X Y Z
00113  *
00114  *  Given as input a spectral sampling distribution,
00115  *  generate the 3 curves to match the human eye's response
00116  *  in CIE color parameters X, Y, and Z.
00117  *  XYZ space can be readily converted to RGB with a 3x3 matrix.
00118  *
00119  *  The tabulated data is linearly interpolated.
00120  *
00121  *  Pointers to the three spectral weighting functions are "returned",
00122  *  storage for the X, Y, and Z curves is allocated by this routine
00123  *  and must be freed by the caller.
00124  */
00125 void
00126 rt_spect_make_CIE_XYZ(struct bn_tabdata **x, struct bn_tabdata **y, struct bn_tabdata **z, const struct bn_table *tabp)
00127 {
00128         struct bn_tabdata       *a, *b, *c;
00129         fastf_t xyz_scale;
00130         int     i;
00131         int     j;
00132 
00133         BN_CK_TABLE(tabp);
00134 
00135         i = bn_table_interval_num_samples( tabp, 430., 650. );
00136         if( i <= 4 )  bu_log("rt_spect_make_CIE_XYZ: insufficient samples (%d) in visible band\n", i);
00137 
00138         BN_GET_TABDATA( a, tabp );
00139         BN_GET_TABDATA( b, tabp );
00140         BN_GET_TABDATA( c, tabp );
00141         *x = a;
00142         *y = b;
00143         *z = c;
00144 
00145         /* No CIE data below 380 nm */
00146         for( j=0; tabp->x[j] < 380 && j < tabp->nx; j++ )  {
00147                 a->y[j] = b->y[j] = c->y[j] = 0;
00148         }
00149 
00150         /* Traverse the CIE table.  Produce as many output values as possible
00151          * before advancing to next CIE table entry.
00152          */
00153         for( i = 0; i < 81-1; i++ )  {
00154                 FAST fastf_t    fract;          /* fraction from [i] to [i+1] */
00155 
00156 again:
00157                 if( j >= tabp->nx )  break;
00158                 if( tabp->x[j] < rt_CIE_XYZ[i][0] ) rt_bomb("rt_spect_make_CIE_XYZ assertion1 failed\n");
00159                 if( tabp->x[j] >= rt_CIE_XYZ[i+1][0] )  continue;
00160                 /* The CIE table has 5nm spacing */
00161                 fract = (tabp->x[j] - rt_CIE_XYZ[i][0] ) / 5;
00162                 if( fract < 0 || fract > 1 )  rt_bomb("rt_spect_make_CIE_XYZ assertion2 failed\n");
00163                 a->y[j] = (1-fract) * rt_CIE_XYZ[i][1] + fract * rt_CIE_XYZ[i+1][1];
00164                 b->y[j] = (1-fract) * rt_CIE_XYZ[i][2] + fract * rt_CIE_XYZ[i+1][2];
00165                 c->y[j] = (1-fract) * rt_CIE_XYZ[i][3] + fract * rt_CIE_XYZ[i+1][3];
00166                 j++;
00167                 goto again;
00168         }
00169 
00170         /* No CIE data above 780 nm */
00171         for( ; j < tabp->nx; j++ )  {
00172                 a->y[j] = b->y[j] = c->y[j] = 0;
00173         }
00174 
00175         /* Normalize the curves so that area under Y curve is 1.0 */
00176         xyz_scale = bn_tabdata_area2( b );
00177         if( fabs(xyz_scale) < VDIVIDE_TOL )  {
00178                 rt_log("rt_spect_make_CIE_XYZ(): Area = 0 (no luminance) in this part of the spectrum, skipping normalization step\n");
00179                 return;
00180         }
00181         xyz_scale = 1 / xyz_scale;
00182         bn_tabdata_scale( a, a, xyz_scale );
00183         bn_tabdata_scale( b, b, xyz_scale );
00184         bn_tabdata_scale( c, c, xyz_scale );
00185 }
00186 
00187 /*
00188  *                      R T _ S P E C T _ R E F L E C T A N C E _ R G B
00189  *
00190  *  Given reflectance data (in range 0..1) in terms of RGB color,
00191  *  convert that to a spectral reflectance curve.
00192  *
00193  *  The assumption here is that the spectrum is made up of exactly three
00194  *  non-overlapping bands, and the reflectance is constant over each:
00195  *
00196  *      red     572nm to 1,000,000nm    (includes the full IR band)
00197  *      green   492nm to 572nm          (just green)
00198  *      blue    1nm to 492nm            (includes Ultraviolet)
00199  *
00200  *  As the caller may be doing a lot of this, the caller is expected
00201  *  to provide a pointer to a valid bn_tabdata structure which is
00202  *  to be filled in.  Allowing caller to re-cycle them rather than
00203  *  doing constant malloc/free cycle.
00204  */
00205 void
00206 rt_spect_reflectance_rgb(struct bn_tabdata *curve, const float *rgb)
00207 {
00208         register int    i;
00209         register const struct bn_table  *tabp;
00210 
00211         BN_CK_TABDATA(curve);
00212         tabp = curve->table;
00213         BN_CK_TABLE(tabp);
00214 
00215         /* Fill in blue values, everything up to but not including 492nm */
00216         for( i=0; i < tabp->nx; i++ )  {
00217                 if( tabp->x[i] >= 492 )  break;
00218                 curve->y[i] = rgb[2];
00219         }
00220 
00221         /* Fill in green values, everything up to but not including 572nm */
00222         for( ; i < tabp->nx; i++ )  {
00223                 if( tabp->x[i] >= 572 )  break;
00224                 curve->y[i] = rgb[1];
00225         }
00226 
00227         /* Fill in red values, everything from here up to end of table */
00228         for( ; i < tabp->nx; i++ )  {
00229                 curve->y[i] = rgb[0];
00230         }
00231 }
00232 
00233 #define PLANCK_C1       37415           /* watts um^4 cm^-2 */
00234 #define PLANCK_C2       14387.86        /* um K */
00235 /* Russ gives these values at 37,415 and 14,388 */
00236 /* Handbook of Physics and Chem gives these values as 37,403 and 14,384 */
00237 /* Aircraft Combat Surv gives these values as 37,483.2 and 14,387.86 */
00238 
00239 /* Requires wavelength _w in um, not nm, returns units: W / cm**2 / um */
00240 #define PLANCK(_w,_tempK)       \
00241         (PLANCK_C1/(_w*_w*_w*_w*_w*(exp(PLANCK_C2/(_w*_tempK))-1)))
00242 
00243 /*
00244  *                      R T _ S P E C T _ B L A C K _ B O D Y
00245  *
00246  *  Integrate Planck's Radiation Formula for a black body radiator
00247  *  across the given spectrum.
00248  *  Returns radiant emittance in W/cm**2 for each wavelength interval.
00249  *
00250  *  Based upon code kindly provided by Russ Moulton, Jr., EOSoft Inc.
00251  *  Compute at 'n-1' wavelengths evenly spaced between ax and bx.
00252  */
00253 void
00254 rt_spect_black_body(struct bn_tabdata *data, double temp, unsigned int n)
00255 
00256                                         /* Degrees Kelvin */
00257                                         /* # wavelengths to eval at */
00258 {
00259         const struct bn_table   *tabp;
00260         int                             j;
00261 
00262         BN_CK_TABDATA(data);
00263         tabp = data->table;
00264         BN_CK_TABLE(tabp);
00265 
00266         if(bu_debug&BU_DEBUG_TABDATA)  {
00267                 bu_log("rt_spect_black_body( x%x, %g degK ) %g um to %g um\n",
00268                         data, temp,
00269                         tabp->x[0] * 0.001,     /* nm to um */
00270                         tabp->x[tabp->nx] * 0.001       /* nm to um */
00271                 );
00272         }
00273 
00274         if( n < 3 )  n = 3;
00275 
00276         for( j = 0; j < tabp->nx; j++ )  {
00277                 double  ax;             /* starting wavelength, um */
00278                 double  bx;             /* ending wavelength, um */
00279                 double  dx;             /* wavelength interval, um */
00280                 double  w_sum;          /* sum over wavelengths */
00281                 double  wavlen;         /* current wavelength */
00282                 unsigned long i;
00283 
00284                 ax = tabp->x[j] * 0.001;        /* nm to um */
00285                 bx = tabp->x[j+1] * 0.001;      /* nm to um */
00286                 dx = (bx - ax) / (double)n;
00287 
00288                 w_sum = 0;
00289                 wavlen = ax;
00290                 for (i=0; i<n; i++)  {
00291                         w_sum += PLANCK(wavlen, temp);
00292                         wavlen += dx;
00293                 }
00294                 w_sum *= dx;
00295 
00296                 data->y[j] = w_sum;
00297         }
00298 }
00299 
00300 /*
00301  *                      R T _ S P E C T _ B L A C K _ B O D Y _ F A S T
00302  *
00303  *  Returns radiant emittance for each spectral interval in the given
00304  *  spectrum in units of watts/cm**2.
00305  *  Integrate each wavelength interval of spectral radiant emittance,
00306  *  by fitting with a rectangle (approximating curve with a horizontal line).
00307  *  For narrow spacing in wavelength this is OK, but with large spacing
00308  *  this tends to over-predict the power by 20%, due to the sharp
00309  *  (exponential) slope of the curve.
00310  *  With coarse spacing, or when unsure, use rt_spect_black_body().
00311  */
00312 void
00313 rt_spect_black_body_fast(struct bn_tabdata *data, double temp)
00314 
00315                                         /* Degrees Kelvin */
00316 {
00317         const struct bn_table   *tabp;
00318         int                             j;
00319 
00320         BN_CK_TABDATA(data);
00321         tabp = data->table;
00322         BN_CK_TABLE(tabp);
00323 
00324         if(bu_debug&BU_DEBUG_TABDATA)  {
00325                 bu_log("rt_spect_black_body_fast( x%x, %g degK )\n",
00326                         data, temp );
00327         }
00328 
00329         for( j = 0; j < tabp->nx; j++ )  {
00330                 data->y[j] = PLANCK( (tabp->x[j]*0.001), temp ) *
00331                         (tabp->x[j+1] - tabp->x[j]) * 0.001;
00332         }
00333 }
00334 
00335 /*
00336  *                      R T _ S P E C T _ B L A C K _ B O D Y _ P O I N T S
00337  *
00338  *  Returns point-sampled values of spectral radiant emittance,
00339  *  in units of watts/cm**2/um,
00340  *  straight from Planck's black-body radiation formula.
00341  */
00342 void
00343 rt_spect_black_body_points(struct bn_tabdata *data, double temp)
00344 
00345                                         /* Degrees Kelvin */
00346 {
00347         const struct bn_table   *tabp;
00348         int                             j;
00349 
00350         BN_CK_TABDATA(data);
00351         tabp = data->table;
00352         BN_CK_TABLE(tabp);
00353 
00354         if(bu_debug&BU_DEBUG_TABDATA)  {
00355                 bu_log("rt_spect_black_body_points( x%x, %g degK )\n",
00356                         data, temp );
00357         }
00358 
00359         for( j = 0; j < tabp->nx; j++ )  {
00360                 data->y[j] = PLANCK( (tabp->x[j]*0.001), temp );
00361         }
00362 }
00363 
00364 /*
00365  * Local Variables:
00366  * mode: C
00367  * tab-width: 8
00368  * c-basic-offset: 4
00369  * indent-tabs-mode: t
00370  * End:
00371  * ex: shiftwidth=4 tabstop=8
00372  */

Generated on Mon Sep 18 01:24:56 2006 for BRL-CAD by  doxygen 1.4.6