static int test_bn_tabdata_scale(int argc, char *argv[]) { struct bn_table *tab_in; struct bn_tabdata *td; struct bn_tabdata *expected; struct bn_tabdata *actual; fastf_t scale; if (argc != 6) { bu_exit(1, "<args> format: table tabdata scale expected_tabdata [%s]\n", argv[0]); } scan_tab_args(argv[2], &tab_in); scan_tabdata_args(argv[3], &td, tab_in); sscanf(argv[4], "%lg", &scale); scan_tabdata_args(argv[5], &expected, tab_in); BN_GET_TABDATA(actual, tab_in); bn_tabdata_scale(actual, td, scale); bn_pr_tabdata("Result", actual); return !tabdata_equal(expected, actual); }
/* Color pixel based on the energy of a point light source (Eps) plus some diffuse illumination (Epd) reflected from the point <x, y> : E = Epd + Eps (1) The energy reflected from diffuse illumination is the product of the reflectance coefficient at point P (Rp) and the diffuse illumination (Id) : Epd = Rp * Id (2) The energy reflected from the point light source is calculated by the sum of the diffuse reflectance (Rd) and the specular reflectance (Rs), multiplied by the intensity of the light source (Ips) : Eps = (Rd + Rs) * Ips (3) The diffuse reflectance is calculated by the product of the reflectance coefficient (Rp) and the cosine of the angle of incidence (I) : Rd = Rp * cos(I) (4) The specular reflectance is calculated by the product of the specular reflectance coefficient and (the cosine of the angle (S) raised to the nth power) : Rs = W(I) * cos(S)**n (5) Where, I is the angle of incidence. S is the angle between the reflected ray and the observer. W returns the specular reflection coefficient as a function of the angle of incidence. n (roughly 1 to 10) represents the shininess of the surface. * This is the heart of the lighting model which is based on a model developed by Bui-Tuong Phong, [see Wm M. Newman and R. F. Sproull, "Principles of Interactive Computer Graphics", McGraw-Hill, 1979] Er = Ra(m)*cos(Ia) + Rd(m)*cos(I1) + W(I1, m)*cos(s)^^n where, Er is the energy reflected in the observer's direction. Ra is the diffuse reflectance coefficient at the point of intersection due to ambient lighting. Ia is the angle of incidence associated with the ambient light source (angle between ray direction (negated) and surface normal). Rd is the diffuse reflectance coefficient at the point of intersection due to primary lighting. I1 is the angle of incidence associated with the primary light source (angle between light source direction and surface normal). m is the material identification code. W is the specular reflectance coefficient, a function of the angle of incidence, range 0.0 to 1.0, for the material. s is the angle between the reflected ray and the observer. ` n 'Shininess' of the material, range 1 to 10. */ HIDDEN int phong_render(register struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) { struct light_specific *lp; #ifndef RT_MULTISPECTRAL fastf_t *intensity; fastf_t dist; point_t pt; vect_t color; #endif fastf_t *to_light; fastf_t cosine; fastf_t refl; int i; vect_t reflected; vect_t work; #ifdef RT_MULTISPECTRAL struct bn_tabdata *ms_matcolor = BN_TABDATA_NULL; #else point_t matcolor; /* Material color */ #endif struct phong_specific *ps = (struct phong_specific *)dp; if (!ps || ps->magic != PL_MAGIC) bu_bomb("phong_render: bad magic\n"); if (pp == NULL) bu_bomb("phong_render: bad partition\n"); if (rdebug&RDEBUG_SHADE) bu_struct_print("phong_render", phong_parse, (char *)ps); swp->sw_transmit = ps->transmit; swp->sw_reflect = ps->reflect; swp->sw_refrac_index = ps->refrac_index; swp->sw_extinction = ps->extinction; #if SW_SET_TRANSMIT if (swp->sw_phong_set_vector & SW_SET_TRANSMIT) swp->sw_transmit = swp->sw_phong_transmit; if (swp->sw_phong_set_vector & SW_SET_REFLECT) swp->sw_reflect = swp->sw_phong_reflect; if (swp->sw_phong_set_vector & SW_SET_REFRAC_INDEX) swp->sw_refrac_index = swp->sw_phong_ri; if (swp->sw_phong_set_vector & SW_SET_EXTINCTION) swp->sw_extinction = swp->sw_phong_extinction; #endif /* SW_SET_TRANSMIT */ if (swp->sw_xmitonly) { if (swp->sw_xmitonly > 1) return 1; /* done -- wanted parameters only */ if (swp->sw_reflect > 0 || swp->sw_transmit > 0) { if (rdebug&RDEBUG_SHADE) bu_log("calling rr_render from phong, sw_xmitonly\n"); (void)rr_render(ap, pp, swp); } return 1; /* done */ } #ifdef RT_MULTISPECTRAL ms_matcolor = bn_tabdata_dup(swp->msw_color); #else VMOVE(matcolor, swp->sw_color); #endif /* Photon Mapping */ #ifndef RT_MULTISPECTRAL color[0]= swp->sw_color[0]; color[1]= swp->sw_color[1]; color[2]= swp->sw_color[2]; #endif #ifndef RT_MULTISPECTRAL if (!PM_Visualize) #endif { /* Diffuse reflectance from "Ambient" light source (at eye) */ if ((cosine = -VDOT(swp->sw_hit.hit_normal, ap->a_ray.r_dir)) > 0.0) { if (cosine > 1.00001) { bu_log("cosAmb=1+%g %s surfno=%d (x%d, y%d, lvl%d)\n", cosine-1, pp->pt_inseg->seg_stp->st_dp->d_namep, swp->sw_hit.hit_surfno, ap->a_x, ap->a_y, ap->a_level); VPRINT(" normal", swp->sw_hit.hit_normal); VPRINT(" r_dir ", ap->a_ray.r_dir); cosine = 1; } #if SW_SET_TRANSMIT if (swp->sw_phong_set_vector & SW_SET_AMBIENT) { cosine *= swp->sw_phong_ambient; } else { cosine *= AmbientIntensity; } #else cosine *= AmbientIntensity; #endif #ifdef RT_MULTISPECTRAL bn_tabdata_scale(swp->msw_color, ms_matcolor, cosine); #else VSCALE(swp->sw_color, matcolor, cosine); #endif } else { #ifdef RT_MULTISPECTRAL bn_tabdata_constval(swp->msw_color, 0.0); #else VSETALL(swp->sw_color, 0); #endif } /* Emission. 0..1 is normal range, -1..0 sucks light out, like OpenGL */ #ifdef RT_MULTISPECTRAL { float emission[3]; struct bn_tabdata *ms_emission = BN_TABDATA_NULL; VMOVE(emission, ps->emission); #if SW_SET_TRANSMIT if (swp->sw_phong_set_vector & SW_SET_EMISSION) { VSETALL(emission, swp->sw_phong_emission); } #endif /* XXX Really should get a curve at prep, not expand RGB samples */ BN_GET_TABDATA(ms_emission, spectrum); rt_spect_reflectance_rgb(ms_emission, emission); bn_tabdata_add(swp->msw_color, swp->msw_color, ms_emission); bn_tabdata_free(ms_emission); } #else #if SW_SET_TRANSMIT if (swp->sw_phong_set_vector & SW_SET_EMISSION) { vect_t tmp; VSETALL(tmp, swp->sw_phong_emission); VADD2(swp->sw_color, swp->sw_color, tmp); } else { VADD2(swp->sw_color, swp->sw_color, ps->emission); } #else VADD2(swp->sw_color, swp->sw_color, ps->emission); #endif /* SW_SET_TRANSMIT */ #endif /* With the advent of procedural shaders, the caller can no longer * provide us reliable light visibility information. The hit point * may have been changed by another shader in a stack. There is no * way that anyone else can tell us whether lights are visible. */ light_obs(ap, swp, ps->mfp->mf_inputs); /* Consider effects of each light source */ for (i=ap->a_rt_i->rti_nlights-1; i>=0; i--) { if ((lp = (struct light_specific *)swp->sw_visible[i]) == LIGHT_NULL) continue; if (rdebug & RDEBUG_LIGHT) { bu_log("phong_render light=%s lightfract=%g\n", lp->lt_name, swp->sw_lightfract[i]); } /* Light is not shadowed -- add this contribution */ #ifndef RT_MULTISPECTRAL intensity = swp->sw_intensity+3*i; #endif to_light = swp->sw_tolight+3*i; /* Diffuse reflectance from this light source. */ if ((cosine=VDOT(swp->sw_hit.hit_normal, to_light)) > 0.0) { if (cosine > 1.00001) { bu_log("cosI=1+%g (x%d, y%d, lvl%d)\n", cosine-1, ap->a_x, ap->a_y, ap->a_level); cosine = 1; } /* Get Obj Hit Point For Attenuation */ #ifndef RT_MULTISPECTRAL if (PM_Activated) { VJOIN1(pt, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir); dist= sqrt((pt[0]-lp->lt_pos[0])*(pt[0]-lp->lt_pos[0]) + (pt[1]-lp->lt_pos[1])*(pt[1]-lp->lt_pos[1]) + (pt[2]-lp->lt_pos[2])*(pt[2]-lp->lt_pos[2]))/1000.0; dist= (1.0/(0.1 + 1.0*dist + 0.01*dist*dist)); refl= dist * ps->wgt_diffuse * cosine * swp->sw_lightfract[i] * lp->lt_intensity; /* bu_log("pt: [%.3f][%.3f, %.3f, %.3f]\n", dist, pt[0], pt[1], pt[2]);*/ } else #endif { refl= ps->wgt_diffuse * swp->sw_lightfract[i] * cosine * lp->lt_fraction; } #ifdef RT_MULTISPECTRAL bn_tabdata_incr_mul3_scale(swp->msw_color, lp->lt_spectrum, swp->msw_intensity[i], ms_matcolor, refl); #else VELMUL3(work, matcolor, lp->lt_color, intensity); VJOIN1(swp->sw_color, swp->sw_color, refl, work); #endif } /* Calculate specular reflectance. * Reflected ray = (2 * cos(i) * Normal) - Incident ray. * Cos(s) = Reflected ray DOT Incident ray. */ cosine *= 2; VSCALE(work, swp->sw_hit.hit_normal, cosine); VSUB2(reflected, work, to_light); if ((cosine = -VDOT(reflected, ap->a_ray.r_dir)) > 0) { if (cosine > 1.00001) { bu_log("cosS=1+%g (x%d, y%d, lvl%d)\n", cosine-1, ap->a_x, ap->a_y, ap->a_level); cosine = 1; } refl = ps->wgt_specular * swp->sw_lightfract[i] * lp->lt_fraction * #ifdef PHAST_PHONG /* It is unnecessary to compute the actual * exponential here since phong is just a * gross hack. We approximate re: * Graphics Gems IV "A Fast Alternative to * Phong's Specular Model" Pg 385 */ cosine / (ps->shine - ps->shine*cosine + cosine); #else phg_ipow(cosine, ps->shine); #endif /* PHAST_PHONG */ #ifdef RT_MULTISPECTRAL bn_tabdata_incr_mul2_scale(swp->msw_color, lp->lt_spectrum, swp->msw_intensity[i], refl); #else VELMUL(work, lp->lt_color, intensity); VJOIN1(swp->sw_color, swp->sw_color, refl, work); #endif } } #ifndef RT_MULTISPECTRAL if (PM_Activated) { IrradianceEstimate(ap, work, swp->sw_hit.hit_point, swp->sw_hit.hit_normal); VELMUL(work, work, color); VADD2(swp->sw_color, work, swp->sw_color); if (swp->sw_color[0] > 1.0) swp->sw_color[0]= 1.0; if (swp->sw_color[1] > 1.0) swp->sw_color[1]= 1.0; if (swp->sw_color[2] > 1.0) swp->sw_color[2]= 1.0; } } else { if (PM_Activated) { /* IrradianceEstimate(work, swp->sw_hit.hit_point, swp->sw_hit.hit_normal); VELMUL(swp->sw_color, work, color);*/ IrradianceEstimate(ap, swp->sw_color, swp->sw_hit.hit_point, swp->sw_hit.hit_normal); if (swp->sw_color[0] > 1.0) swp->sw_color[0]= 1.0; if (swp->sw_color[1] > 1.0) swp->sw_color[1]= 1.0; if (swp->sw_color[2] > 1.0) swp->sw_color[2]= 1.0; } #endif } if (swp->sw_reflect > 0 || swp->sw_transmit > 0) (void)rr_render(ap, pp, swp); #ifdef RT_MULTISPECTRAL bn_tabdata_free(ms_matcolor); #endif return 1; }
/* * R R _ R E N D E R */ int rr_render(register struct application *ap, const struct partition *pp, struct shadework *swp) { struct application sub_ap; vect_t work; vect_t incident_dir; fastf_t shader_fract; fastf_t reflect; fastf_t transmit; #ifdef RT_MULTISPECTRAL struct bn_tabdata *ms_filter_color = BN_TABDATA_NULL; struct bn_tabdata *ms_shader_color = BN_TABDATA_NULL; struct bn_tabdata *ms_reflect_color = BN_TABDATA_NULL; struct bn_tabdata *ms_transmit_color = BN_TABDATA_NULL; #else vect_t filter_color; vect_t shader_color; vect_t reflect_color; vect_t transmit_color; #endif fastf_t attenuation; vect_t to_eye; int code; RT_AP_CHECK(ap); RT_APPLICATION_INIT(&sub_ap); #ifdef RT_MULTISPECTRAL sub_ap.a_spectrum = BN_TABDATA_NULL; ms_reflect_color = bn_tabdata_get_constval(0.0, spectrum); #endif /* * sw_xmitonly is set primarily for light visibility rays. * Need to compute (partial) transmission through to the light, * or procedural shaders won't be able to cast shadows * and light won't be able to get through glass * (including "stained glass" and "filter glass"). * * On the other hand, light visibility rays shouldn't be refracted, * it is pointless to shoot at where the light isn't. */ if (swp->sw_xmitonly) { /* Caller wants transmission term only, don't fire reflected rays */ transmit = swp->sw_transmit + swp->sw_reflect; /* Don't loose energy */ reflect = 0; } else { reflect = swp->sw_reflect; transmit = swp->sw_transmit; } if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render(%s) START: lvl=%d reflect=%g, transmit=%g, xmitonly=%d\n", pp->pt_regionp->reg_name, ap->a_level, reflect, transmit, swp->sw_xmitonly); } if (reflect <= 0 && transmit <= 0) goto out; if (ap->a_level > max_bounces) { /* Nothing more to do for this ray */ static long count = 0; /* Not PARALLEL, should be OK */ if ((R_DEBUG&(RDEBUG_SHOWERR|RDEBUG_REFRACT)) && ( count++ < MSG_PROLOGUE || (count%MSG_INTERVAL) == 3 )) { bu_log("rr_render: %d, %d MAX BOUNCES=%d: %s\n", ap->a_x, ap->a_y, ap->a_level, pp->pt_regionp->reg_name); } /* * Return the basic color of the object, ignoring the * the fact that it is supposed to be * filtering or reflecting light here. * This is much better than returning just black, * but something better might be done. */ #ifdef RT_MULTISPECTRAL BN_CK_TABDATA(swp->msw_color); BN_CK_TABDATA(swp->msw_basecolor); bn_tabdata_copy(swp->msw_color, swp->msw_basecolor); #else VMOVE(swp->sw_color, swp->sw_basecolor); #endif ap->a_cumlen += pp->pt_inhit->hit_dist; goto out; } #ifdef RT_MULTISPECTRAL BN_CK_TABDATA(swp->msw_basecolor); ms_filter_color = bn_tabdata_dup(swp->msw_basecolor); #else VMOVE(filter_color, swp->sw_basecolor); #endif if ((swp->sw_inputs & (MFI_HIT|MFI_NORMAL)) != (MFI_HIT|MFI_NORMAL)) shade_inputs(ap, pp, swp, MFI_HIT|MFI_NORMAL); /* * If this ray is being fired from the exit point of * an object, and is directly entering another object, * (i.e., there is no intervening air-gap), and * the two refractive indices match, then do not fire a * reflected ray -- just take the transmission contribution. * This is important, e.g., for glass gun tubes projecting * through a glass armor plate. :-) */ if (NEAR_ZERO(pp->pt_inhit->hit_dist, AIR_GAP_TOL) && ZERO(ap->a_refrac_index - swp->sw_refrac_index)) { transmit += reflect; reflect = 0; } /* * Diminish base color appropriately, and add in * contributions from mirror reflection & transparency */ shader_fract = 1 - (reflect + transmit); if (shader_fract < 0) { shader_fract = 0; } else if (shader_fract >= 1) { goto out; } if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render: lvl=%d start shader=%g, reflect=%g, transmit=%g %s\n", ap->a_level, shader_fract, reflect, transmit, pp->pt_regionp->reg_name); } #ifdef RT_MULTISPECTRAL BN_GET_TABDATA(ms_shader_color, swp->msw_color->table); bn_tabdata_scale(ms_shader_color, swp->msw_color, shader_fract); #else VSCALE(shader_color, swp->sw_color, shader_fract); #endif /* * Compute transmission through an object. * There may be a mirror reflection, which will be handled * by the reflection code later */ if (transmit > 0) { if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render: lvl=%d transmit=%g. Calculate refraction at entrance to %s.\n", ap->a_level, transmit, pp->pt_regionp->reg_name); } /* * Calculate refraction at entrance. */ sub_ap = *ap; /* struct copy */ #ifdef RT_MULTISPECTRAL sub_ap.a_spectrum = bn_tabdata_dup((struct bn_tabdata *)ap->a_spectrum); #endif sub_ap.a_level = 0; /* # of internal reflections */ sub_ap.a_cumlen = 0; /* distance through the glass */ sub_ap.a_user = -1; /* sanity */ sub_ap.a_rbeam = ap->a_rbeam + swp->sw_hit.hit_dist * ap->a_diverge; sub_ap.a_diverge = 0.0; sub_ap.a_uptr = (genptr_t)(pp->pt_regionp); VMOVE(sub_ap.a_ray.r_pt, swp->sw_hit.hit_point); VMOVE(incident_dir, ap->a_ray.r_dir); /* If there is an air gap, reset ray's RI to air */ if (pp->pt_inhit->hit_dist > AIR_GAP_TOL) sub_ap.a_refrac_index = RI_AIR; if (!ZERO(sub_ap.a_refrac_index - swp->sw_refrac_index) && !rr_refract(incident_dir, /* input direction */ swp->sw_hit.hit_normal, /* exit normal */ sub_ap.a_refrac_index, /* current RI */ swp->sw_refrac_index, /* next RI */ sub_ap.a_ray.r_dir /* output direction */ )) { /* * Ray was mirror reflected back outside solid. * Just add contribution to reflection, * and quit. */ reflect += transmit; transmit = 0; #ifdef RT_MULTISPECTRAL ms_transmit_color = bn_tabdata_get_constval(0.0, spectrum); #else VSETALL(transmit_color, 0); #endif if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render: lvl=%d change xmit into reflection %s\n", ap->a_level, pp->pt_regionp->reg_name); } goto do_reflection; } if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render: lvl=%d begin transmission through %s.\n", ap->a_level, pp->pt_regionp->reg_name); } /* * Find new exit point from the inside. * We will iterate, but not recurse, due to the special * (non-recursing) hit and miss routines used here for * internal reflection. * * a_onehit is set to 3, so that where possible, * rr_hit() will be given three accurate hit points: * the entry and exit points of this glass region, * and the entry point into the next region. * This permits calculation of the departing * refraction angle based on the RI of the current and * *next* regions along the ray. */ sub_ap.a_purpose = "rr first glass transmission ray"; sub_ap.a_flag = 0; do_inside: sub_ap.a_hit = rr_hit; sub_ap.a_miss = rr_miss; sub_ap.a_logoverlap = ap->a_logoverlap; sub_ap.a_onehit = 3; sub_ap.a_rbeam = ap->a_rbeam + swp->sw_hit.hit_dist * ap->a_diverge; sub_ap.a_diverge = 0.0; switch (code = rt_shootray(&sub_ap)) { case 3: /* More glass to come. * uvec=exit_pt, vvec=N, a_refrac_index = next RI. */ break; case 2: /* No more glass to come. * uvec=exit_pt, vvec=N, a_refrac_index = next RI. */ break; case 1: /* Treat as escaping ray */ if (R_DEBUG&RDEBUG_REFRACT) bu_log("rr_refract: Treating as escaping ray\n"); goto do_exit; case 0: default: /* Dreadful error */ #ifdef RT_MULTISPECTRAL bu_bomb("rr_refract: Stuck in glass. Very green pixel, unsupported in multi-spectral mode\n"); #else VSET(swp->sw_color, 0, 99, 0); /* very green */ #endif goto out; /* abandon hope */ } if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render: calculating refraction @ exit from %s (green)\n", pp->pt_regionp->reg_name); bu_log("Start point to exit point:\n\ vdraw open rr;vdraw params c 00ff00; vdraw write n 0 %g %g %g; vdraw wwrite n 1 %g %g %g; vdraw send\n", V3ARGS(sub_ap.a_ray.r_pt), V3ARGS(sub_ap.a_uvec)); } /* NOTE: rr_hit returns EXIT Point in sub_ap.a_uvec, * and returns EXIT Normal in sub_ap.a_vvec, * and returns next RI in sub_ap.a_refrac_index */ if (R_DEBUG&RDEBUG_RAYWRITE) { wraypts(sub_ap.a_ray.r_pt, sub_ap.a_ray.r_dir, sub_ap.a_uvec, 2, ap, stdout); /* 2 = ?? */ } if (R_DEBUG&RDEBUG_RAYPLOT) { /* plotfp */ bu_semaphore_acquire(BU_SEM_SYSCALL); pl_color(stdout, 0, 255, 0); pdv_3line(stdout, sub_ap.a_ray.r_pt, sub_ap.a_uvec); bu_semaphore_release(BU_SEM_SYSCALL); } /* Advance. Exit point becomes new start point */ VMOVE(sub_ap.a_ray.r_pt, sub_ap.a_uvec); VMOVE(incident_dir, sub_ap.a_ray.r_dir); /* * Calculate refraction at exit point. * Use "look ahead" RI value from rr_hit. */ if (!ZERO(sub_ap.a_refrac_index - swp->sw_refrac_index) && !rr_refract(incident_dir, /* input direction */ sub_ap.a_vvec, /* exit normal */ swp->sw_refrac_index, /* current RI */ sub_ap.a_refrac_index, /* next RI */ sub_ap.a_ray.r_dir /* output direction */ )) { static long count = 0; /* not PARALLEL, should be OK */ /* Reflected internally -- keep going */ if ((++sub_ap.a_level) <= max_ireflect) { sub_ap.a_purpose = "rr reflected internal ray, probing for glass exit point"; sub_ap.a_flag = 0; goto do_inside; } /* * Internal Reflection limit exceeded -- just let * the ray escape, continuing on current course. * This will cause some energy from somewhere in the * scene to be received through this glass, * which is much better than just returning * grey or black, as before. */ if ((R_DEBUG&(RDEBUG_SHOWERR|RDEBUG_REFRACT)) && ( count++ < MSG_PROLOGUE || (count%MSG_INTERVAL) == 3 )) { bu_log("rr_render: %d, %d Int.reflect=%d: %s lvl=%d\n", sub_ap.a_x, sub_ap.a_y, sub_ap.a_level, pp->pt_regionp->reg_name, ap->a_level); } VMOVE(sub_ap.a_ray.r_dir, incident_dir); goto do_exit; } do_exit: /* * Compute internal spectral transmittance. * Bouger's law. pg 30 of "color science" * * Apply attenuation factor due to thickness of the glass. * sw_extinction is in terms of fraction of light absorbed * per linear meter of glass. a_cumlen is in mm. */ /* XXX extinction should be a spectral curve, not scalor */ if (swp->sw_extinction > 0 && sub_ap.a_cumlen > 0) { attenuation = pow(10.0, -1.0e-3 * sub_ap.a_cumlen * swp->sw_extinction); } else { attenuation = 1; } /* * Process the escaping refracted ray. * This is the only place we might recurse dangerously, * so we are careful to use our caller's recursion level+1. * NOTE: point & direction already filled in */ sub_ap.a_hit = ap->a_hit; sub_ap.a_miss = ap->a_miss; sub_ap.a_logoverlap = ap->a_logoverlap; sub_ap.a_onehit = ap->a_onehit; sub_ap.a_level = ap->a_level+1; sub_ap.a_uptr = ap->a_uptr; sub_ap.a_rbeam = ap->a_rbeam + swp->sw_hit.hit_dist * ap->a_diverge; sub_ap.a_diverge = 0.0; if (code == 3) { sub_ap.a_purpose = "rr recurse on next glass"; sub_ap.a_flag = 0; } else { sub_ap.a_purpose = "rr recurse on escaping internal ray"; sub_ap.a_flag = 1; sub_ap.a_onehit = sub_ap.a_onehit > -3 ? -3 : sub_ap.a_onehit; } /* sub_ap.a_refrac_index was set to RI of next material by rr_hit(). */ sub_ap.a_cumlen = 0; (void) rt_shootray(&sub_ap); /* a_user has hit/miss flag! */ if (sub_ap.a_user == 0) { #ifdef RT_MULTISPECTRAL ms_transmit_color = bn_tabdata_dup(background); #else VMOVE(transmit_color, background); #endif sub_ap.a_cumlen = 0; } else { #ifdef RT_MULTISPECTRAL ms_transmit_color = bn_tabdata_dup(sub_ap.a_spectrum); #else VMOVE(transmit_color, sub_ap.a_color); #endif } transmit *= attenuation; #ifdef RT_MULTISPECTRAL bn_tabdata_mul(ms_transmit_color, ms_filter_color, ms_transmit_color); #else VELMUL(transmit_color, filter_color, transmit_color); #endif if (R_DEBUG&RDEBUG_REFRACT) { bu_log("rr_render: lvl=%d end of xmit through %s\n", ap->a_level, pp->pt_regionp->reg_name); } } else {
/** * 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 ); }