/* * R T _ S P E C T _ M A K E _ N T S C _ R G B * * Using the "Representative set of camera taking sensitivities" * for a NTSC television camera, from Benson "Television Engineering * Handbook" page 4.58, convert an RGB value in range 0..1 to * a spectral curve also in range 0..1. * * These curves should be used in converting spectral samples * to NTSC RGB values. */ void spect_make_NTSC_RGB(struct bn_tabdata **rp, struct bn_tabdata **gp, struct bn_tabdata **bp, const struct bn_table *tabp) { BN_CK_TABLE(tabp); /* Convert array of number pairs into bn_tabdata & bn_table */ rt_NTSC_r_tabdata = bn_tabdata_from_array(&rt_NTSC_R[0][0]); rt_NTSC_g_tabdata = bn_tabdata_from_array(&rt_NTSC_G[0][0]); rt_NTSC_b_tabdata = bn_tabdata_from_array(&rt_NTSC_B[0][0]); bu_log("ntsc_R: area=%g\n", bn_tabdata_area2(rt_NTSC_r_tabdata)); bn_print_table_and_tabdata("/dev/tty", rt_NTSC_r_tabdata); bu_log("ntsc_G: area=%g\n", bn_tabdata_area2(rt_NTSC_g_tabdata)); bn_print_table_and_tabdata("/dev/tty", rt_NTSC_g_tabdata); bu_log("ntsc_B: area=%g\n", bn_tabdata_area2(rt_NTSC_b_tabdata)); bn_print_table_and_tabdata("/dev/tty", rt_NTSC_b_tabdata); /* Resample original NTSC curves to match given bn_table sampling */ #if 0 /* just to test the routine */ *rp = bn_tabdata_resample_avg(tabp, rt_NTSC_r_tabdata); *gp = bn_tabdata_resample_avg(tabp, rt_NTSC_g_tabdata); *bp = bn_tabdata_resample_avg(tabp, rt_NTSC_b_tabdata); #else /* use this one for real */ *rp = bn_tabdata_resample_max(tabp, rt_NTSC_r_tabdata); *gp = bn_tabdata_resample_max(tabp, rt_NTSC_g_tabdata); *bp = bn_tabdata_resample_max(tabp, rt_NTSC_b_tabdata); #endif }
/** * R T _ S P E C T _ R E F L E C T A N C E _ R G B * * Given reflectance data (in range 0..1) in terms of RGB color, * convert that to a spectral reflectance curve. * * The assumption here is that the spectrum is made up of exactly * three non-overlapping bands, and the reflectance is constant over * each: * * red 572nm to 1, 000, 000nm (includes the full IR band) * green 492nm to 572nm (just green) * blue 1nm to 492nm (includes Ultraviolet) * * As the caller may be doing a lot of this, the caller is expected to * provide a pointer to a valid bn_tabdata structure which is to be * filled in. Allowing caller to re-cycle them rather than doing * constant malloc/free cycle. */ void rt_spect_reflectance_rgb(struct bn_tabdata *curve, const float *rgb) { register int i; register const struct bn_table *tabp; BN_CK_TABDATA(curve); tabp = curve->table; BN_CK_TABLE(tabp); /* Fill in blue values, everything up to but not including 492nm */ for ( i=0; i < tabp->nx; i++ ) { if ( tabp->x[i] >= 492 ) break; curve->y[i] = rgb[2]; } /* Fill in green values, everything up to but not including 572nm */ for (; i < tabp->nx; i++ ) { if ( tabp->x[i] >= 572 ) break; curve->y[i] = rgb[1]; } /* Fill in red values, everything from here up to end of table */ for (; i < tabp->nx; i++ ) { curve->y[i] = rgb[0]; } }
/** * R T _ S P E C T _ B L A C K _ B O D Y * * Integrate Planck's Radiation Formula for a black body radiator * across the given spectrum. Returns radiant emittance in W/cm**2 * for each wavelength interval. * * Based upon code kindly provided by Russ Moulton, Jr., EOSoft Inc. * Compute at 'n-1' wavelengths evenly spaced between ax and bx. */ void rt_spect_black_body(struct bn_tabdata *data, double temp, unsigned int n) /* Degrees Kelvin */ /* # wavelengths to eval at */ { const struct bn_table *tabp; int j; BN_CK_TABDATA(data); tabp = data->table; BN_CK_TABLE(tabp); if (bu_debug&BU_DEBUG_TABDATA) { bu_log("rt_spect_black_body( x%x, %g degK ) %g um to %g um\n", data, temp, tabp->x[0] * 0.001, /* nm to um */ tabp->x[tabp->nx] * 0.001 /* nm to um */ ); } if ( n < 3 ) n = 3; for ( j = 0; j < tabp->nx; j++ ) { double ax; /* starting wavelength, um */ double bx; /* ending wavelength, um */ double dx; /* wavelength interval, um */ double w_sum; /* sum over wavelengths */ double wavlen; /* current wavelength */ unsigned long i; ax = tabp->x[j] * 0.001; /* nm to um */ bx = tabp->x[j+1] * 0.001; /* nm to um */ dx = (bx - ax) / (double)n; w_sum = 0; wavlen = ax; for (i=0; i<n; i++) { w_sum += PLANCK(wavlen, temp); wavlen += dx; } w_sum *= dx; data->y[j] = w_sum; } }
/** * R T _ S P E C T _ B L A C K _ B O D Y _ P O I N T S * * Returns point-sampled values of spectral radiant emittance, in * units of watts/cm**2/um, straight from Planck's black-body * radiation formula. */ void rt_spect_black_body_points(struct bn_tabdata *data, double temp) /* Degrees Kelvin */ { const struct bn_table *tabp; int j; BN_CK_TABDATA(data); tabp = data->table; BN_CK_TABLE(tabp); if (bu_debug&BU_DEBUG_TABDATA) { bu_log("rt_spect_black_body_points( x%x, %g degK )\n", data, temp ); } for ( j = 0; j < tabp->nx; j++ ) { data->y[j] = PLANCK( (tabp->x[j]*0.001), temp ); } }
/** * R T _ S P E C T _ M A K E _ C I E _ X Y Z * * Given as input a spectral sampling distribution, generate the 3 * curves to match the human eye's response in CIE color parameters X, * Y, and Z. XYZ space can be readily converted to RGB with a 3x3 * matrix. * * The tabulated data is linearly interpolated. * * Pointers to the three spectral weighting functions are "returned", * storage for the X, Y, and Z curves is allocated by this routine and * must be freed by the caller. */ void rt_spect_make_CIE_XYZ(struct bn_tabdata **x, struct bn_tabdata **y, struct bn_tabdata **z, const struct bn_table *tabp) { struct bn_tabdata *a, *b, *c; fastf_t xyz_scale; int i; int j; BN_CK_TABLE(tabp); i = bn_table_interval_num_samples( tabp, 430., 650. ); if ( i <= 4 ) bu_log("rt_spect_make_CIE_XYZ: insufficient samples (%d) in visible band\n", i); BN_GET_TABDATA( a, tabp ); BN_GET_TABDATA( b, tabp ); BN_GET_TABDATA( c, tabp ); *x = a; *y = b; *z = c; /* No CIE data below 380 nm */ for ( j=0; tabp->x[j] < 380 && j < tabp->nx; j++ ) { a->y[j] = b->y[j] = c->y[j] = 0; } /* Traverse the CIE table. Produce as many output values as * possible before advancing to next CIE table entry. */ for ( i = 0; i < 81-1; i++ ) { fastf_t fract; /* fraction from [i] to [i+1] */ again: if ( j >= tabp->nx ) break; if ( tabp->x[j] < rt_CIE_XYZ[i][0] ) bu_bomb("rt_spect_make_CIE_XYZ assertion1 failed\n"); if ( tabp->x[j] >= rt_CIE_XYZ[i+1][0] ) continue; /* The CIE table has 5nm spacing */ fract = (tabp->x[j] - rt_CIE_XYZ[i][0] ) / 5; if ( fract < 0 || fract > 1 ) bu_bomb("rt_spect_make_CIE_XYZ assertion2 failed\n"); a->y[j] = (1-fract) * rt_CIE_XYZ[i][1] + fract * rt_CIE_XYZ[i+1][1]; b->y[j] = (1-fract) * rt_CIE_XYZ[i][2] + fract * rt_CIE_XYZ[i+1][2]; c->y[j] = (1-fract) * rt_CIE_XYZ[i][3] + fract * rt_CIE_XYZ[i+1][3]; j++; goto again; } /* No CIE data above 780 nm */ for (; j < tabp->nx; j++ ) { a->y[j] = b->y[j] = c->y[j] = 0; } /* Normalize the curves so that area under Y curve is 1.0 */ xyz_scale = bn_tabdata_area2( b ); if ( fabs(xyz_scale) < VDIVIDE_TOL ) { bu_log("rt_spect_make_CIE_XYZ(): Area = 0 (no luminance) in this part of the spectrum, skipping normalization step\n"); return; } xyz_scale = 1 / xyz_scale; bn_tabdata_scale( a, a, xyz_scale ); bn_tabdata_scale( b, b, xyz_scale ); bn_tabdata_scale( c, c, xyz_scale ); }