/* * F B M _ R E N D E R */ int fbm_render(struct application *ap, struct partition *pp, struct shadework *swp, char *dp) { register struct fbm_specific *fbm_sp = (struct fbm_specific *)dp; vect_t v_noise; point_t pt; if (rdebug&RDEBUG_SHADE) bu_struct_print( "foo", fbm_parse, (char *)fbm_sp ); pt[0] = swp->sw_hit.hit_point[0] * fbm_sp->scale[0]; pt[1] = swp->sw_hit.hit_point[1] * fbm_sp->scale[1]; pt[2] = swp->sw_hit.hit_point[2] * fbm_sp->scale[2]; bn_noise_vec(pt, v_noise); VSCALE(v_noise, v_noise, fbm_sp->distortion); if (rdebug&RDEBUG_SHADE) bu_log("fbm_render: point (%g %g %g) becomes (%g %g %g)\n\tv_noise (%g %g %g)\n", V3ARGS(swp->sw_hit.hit_point), V3ARGS(pt), V3ARGS(v_noise)); VADD2(swp->sw_hit.hit_normal, swp->sw_hit.hit_normal, v_noise); VUNITIZE(swp->sw_hit.hit_normal); return(1); }
/* * This routine is called (at prep time) * once for each region which uses this shader. * Any shader-specific initialization should be done here. * * Returns: * 1 success * 0 success, but delete region * -1 failure */ HIDDEN int toon_setup(register struct region *rp, struct bu_vls *matparm, void **dpp, const struct mfuncs *UNUSED(mfp), struct rt_i *rtip) /* pointer to reg_udata in *rp */ /* New since 4.4 release */ { register struct toon_specific *toon_sp; /* check the arguments */ RT_CHECK_RTI(rtip); BU_CK_VLS(matparm); RT_CK_REGION(rp); if (rdebug&RDEBUG_SHADE) bu_log("toon_setup(%s)\n", rp->reg_name); /* Get memory for the shader parameters and shader-specific data */ BU_GET(toon_sp, struct toon_specific); *dpp = toon_sp; /* initialize the default values for the shader */ memcpy(toon_sp, &toon_defaults, sizeof(struct toon_specific)); /* parse the user's arguments for this use of the shader. */ if (bu_struct_parse(matparm, toon_parse_tab, (char *)toon_sp, NULL) < 0) return -1; if (rdebug&RDEBUG_SHADE) { bu_struct_print(" Parameters:", toon_print_tab, (char *)toon_sp); } return 1; }
HIDDEN void spm_print(register struct region *rp, void *dp) { struct spm_specific *spm; spm = (struct spm_specific *)dp; bu_log("spm_print(rp=%p, dp = %p)\n", (void *)rp, dp); (void)bu_struct_print("spm_print", spm_parse, (char *)dp); if (spm->sp_map) bn_spm_dump(spm->sp_map, 0); }
HIDDEN int txt_setup(register struct region *rp, struct bu_vls *matparm, void **dpp, const struct mfuncs *mfp, struct rt_i *rtip) { register struct txt_specific *tp; int pixelbytes = 3; BU_CK_VLS(matparm); BU_GET(tp, struct txt_specific); *dpp = tp; bu_vls_init(&tp->tx_name); /* defaults */ tp->tx_w = tp->tx_n = -1; tp->tx_trans_valid = 0; tp->tx_scale[X] = 1.0; tp->tx_scale[Y] = 1.0; tp->tx_mirror = 0; tp->tx_datasrc = 0; /* source is auto-located by default */ tp->tx_binunifp = NULL; tp->tx_mp = NULL; /* load given values */ if (bu_struct_parse(matparm, txt_parse, (char *)tp) < 0) { BU_PUT(tp, struct txt_specific); return -1; } /* validate values */ if (tp->tx_w < 0) tp->tx_w = 512; if (tp->tx_n < 0) tp->tx_n = tp->tx_w; if (tp->tx_trans_valid) rp->reg_transmit = 1; BU_CK_VLS(&tp->tx_name); if (bu_vls_strlen(&tp->tx_name) <= 0) return -1; /* !?! if (tp->tx_name[0] == '\0') return -1; */ /* FAIL, no file */ if (BU_STR_EQUAL(mfp->mf_name, "bwtexture")) pixelbytes = 1; /* load the texture from its datasource */ if (txt_load_datasource(tp, rtip->rti_dbip, tp->tx_w * tp->tx_n * pixelbytes)<0) { bu_log("\nERROR: txt_setup() %s %s could not be loaded [source was %s]\n", rp->reg_name, bu_vls_addr(&tp->tx_name), tp->tx_datasrc==TXT_SRC_OBJECT?"object":tp->tx_datasrc==TXT_SRC_FILE?"file":"auto"); return -1; } if (rdebug & RDEBUG_SHADE) { bu_log("txt_setup: texture loaded! type=%s name=%s\n", tp->tx_datasrc==TXT_SRC_AUTO?"auto":tp->tx_datasrc==TXT_SRC_OBJECT?"object":tp->tx_datasrc==TXT_SRC_FILE?"file":"unknown", bu_vls_addr(&tp->tx_name)); bu_struct_print("texture", txt_parse, (char *)tp); } return 1; /* OK */ }
HIDDEN int osl_setup(register struct region *rp, struct bu_vls *matparm, void **dpp, const struct mfuncs *mfp, struct rt_i *rtip) { register struct osl_specific *osl_sp; /* check the arguments */ RT_CHECK_RTI(rtip); BU_CK_VLS(matparm); RT_CK_REGION(rp); if (rdebug&RDEBUG_SHADE) bu_log("osl_setup(%s)\n", rp->reg_name); /* Get memory for the shader parameters and shader-specific data */ BU_GET(osl_sp, struct osl_specific); *dpp = (char *)osl_sp; /* ----------------------------------- * Initialize the osl specific values * ----------------------------------- */ osl_sp->magic = OSL_MAGIC; /* Parse the user's arguments to fill osl specifics */ ShaderGroupInfo group_info; if (osl_parse(matparm, group_info) < 0) { return -1; } /* ----------------------------------- * Initialize osl render system * ----------------------------------- */ /* If OSL system was not initialized yet, do it */ if (oslr == NULL) { oslr = new OSLRenderer(); } /* Add this shader to OSL system */ osl_sp->shader_ref = oslr->AddShader(group_info); if (rdebug&RDEBUG_SHADE) { bu_struct_print(" Parameters:", osl_print_tab, (char *)osl_sp); } return 1; }
/* * This is called (from viewshade() in shade.c) once for each hit point * to be shaded. The purpose here is to fill in values in the shadework * structure. */ int toon_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) { int i; struct toon_specific *toon_sp = (struct toon_specific *)dp; struct light_specific *lp; fastf_t cosi, scale; /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); CK_TOON_SP(toon_sp); if (rdebug&RDEBUG_SHADE) bu_struct_print("toon_render Parameters:", toon_print_tab, (char *)toon_sp); /* if surface normal is nearly orthogonal to the ray, make a black line */ if (VDOT(swp->sw_hit.hit_normal, ap->a_inv_dir) >= 0.8) { VSETALL(swp->sw_color, 0); return 1; } /* probably need to set some swp values here to avoid the infinite recursion * if specified lights exist. */ light_obs(ap, swp, MFI_HIT); /* 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; cosi = VDOT(swp->sw_hit.hit_normal, swp->sw_tolight); if (cosi <= 0.0) scale = 0.0; else if (cosi <= 0.5) scale = 0.5; else if (cosi <= 0.8) scale = 0.8; else scale = 1.0; VSCALE(swp->sw_color, swp->sw_color, scale); return 1; } /* no paths to light source, so just paint it black */ VSETALL(swp->sw_color, 0); if (swp->sw_reflect > 0 || swp->sw_transmit > 0) (void)rr_render(ap, pp, swp); return 1; }
/* * This routine is called (at prep time) once for each region which uses this * shader. The shader specific flat_specific structure is allocated and * default values are set. Then any user-given values override. */ HIDDEN int flat_setup(register struct region *rp, struct bu_vls *matparm, void **dpp, const struct mfuncs *UNUSED(mfp), struct rt_i *rtip) { register struct flat_specific *flat_sp; /* check the arguments */ RT_CHECK_RTI(rtip); BU_CK_VLS(matparm); RT_CK_REGION(rp); if (rdebug&RDEBUG_SHADE) bu_log("flat_setup(%s)\n", rp->reg_name); /* Get memory for the shader parameters and shader-specific data */ BU_GET(flat_sp, struct flat_specific); *dpp = flat_sp; /* color priority: * * priority goes first to the flat shader's color parameter * second priority is to the material color value * third priority is to the flat shader's default color (white) */ /* initialize the default values for the shader */ memcpy(flat_sp, &flat_defaults, sizeof(struct flat_specific)); /* load the material color if it was set */ if (rp->reg_mater.ma_color_valid) { VMOVE(flat_sp->color, rp->reg_mater.ma_color); } /* parse the user's arguments for this use of the shader. */ if (bu_struct_parse(matparm, flat_parse_tab, (char *)flat_sp) < 0) return -1; if (rdebug&RDEBUG_SHADE) { bu_struct_print(" Parameters:", flat_parse_tab, (char *)flat_sp); } return 1; }
/* * F B M _ S E T U P */ HIDDEN int fbm_setup(register struct region *rp, struct bu_vls *matparm, char **dpp) { register struct fbm_specific *fbm; BU_CK_VLS( matparm ); BU_GETSTRUCT( fbm, fbm_specific ); *dpp = (char *)fbm; memcpy(fbm, &fbm_defaults, sizeof(struct fbm_specific)); if (rdebug&RDEBUG_SHADE) bu_log("fbm_setup\n"); if (bu_struct_parse( matparm, fbm_parse, (char *)fbm ) < 0 ) return(-1); if (rdebug&RDEBUG_SHADE) bu_struct_print( rp->reg_name, fbm_parse, (char *)fbm ); return(1); }
/* * This is called (from viewshade() in shade.c) once for each hit point * to be shaded. The purpose here is to fill in values in the shadework * structure. * * The flat shader is probably the second most simple shader (second to * the null/invisible shader -- it does nothing). It shades an object * a constant color. The only complexity comes into play when a * transparency value is set. Then we get the color value behind the * one we are shading and blend accordingly with the flat color. */ int flat_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) { register struct flat_specific *flat_sp = (struct flat_specific *)dp; const point_t unit = {1.0, 1.0, 1.0}; point_t intensity; /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); CK_FLAT_SP(flat_sp); if (rdebug&RDEBUG_SHADE) bu_struct_print("flat_render Parameters:", flat_parse_tab, (char *)flat_sp); /* do the actual flat color shading for the flat object. if the object is * not transparent, just put the color. if the object is transparent, do * a little more work determining the background pixel, and then blend with * the flat foreground object. */ if (VNEAR_ZERO(flat_sp->transparency, SMALL_FASTF)) { /* just put the flat value */ VMOVE(swp->sw_color, flat_sp->color); } else { /* this gets the background pixel value, if the transparency is not 0 */ swp->sw_transmit = 1.0; /*!!! try to remove */ VMOVE(swp->sw_basecolor, flat_sp->transparency); (void)rr_render(ap, pp, swp); /* now blend with the foreground object being shaded */ VSUB2(intensity, unit, flat_sp->transparency); /* inverse transparency is how much we want */ VELMUL(intensity, intensity, flat_sp->color); /* ??? is there a way to merge this mul->add step? */ VADD2(swp->sw_color, swp->sw_color, intensity); } return 1; }
/* * This is called (from viewshade() in shade.c) once for each hit point * to be shaded. The purpose here is to fill in values in the shadework * structure. * * dp is a pointer to the shader-specific struct */ int bbd_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) { register struct bbd_specific *bbd_sp = (struct bbd_specific *)dp; union tree *tp; struct bbd_img *bi; struct imgdist id[MAX_IMAGES]; size_t i; /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); CK_bbd_SP(bbd_sp); if (rdebug&RDEBUG_SHADE) { bu_struct_print("bbd_render Parameters:", bbd_print_tab, (char *)bbd_sp); bu_log("pixel %d %d\n", ap->a_x, ap->a_y); bu_log("bbd region: %s\n", pp->pt_regionp->reg_name); } tp = pp->pt_regionp->reg_treetop; if (tp->tr_a.tu_op != OP_SOLID) { bu_log("%s:%d region %s rendering bbd should have found OP_SOLID, not %d\n", __FILE__, __LINE__, pp->pt_regionp->reg_name, tp->tr_a.tu_op); bu_bomb("\n"); } swp->sw_transmit = 1.0; VSETALL(swp->sw_color, 0.0); VSETALL(swp->sw_basecolor, 1.0); i = 0; for (BU_LIST_FOR(bi, bbd_img, &bbd_sp->imgs)) { /* find out if the ray hits the plane */ id[i].index = i; id[i].bi = bi; id[i].status = bn_isect_line3_plane(&id[i].dist, ap->a_ray.r_pt, ap->a_ray.r_dir, bi->img_plane, &ap->a_rt_i->rti_tol); i++; } bu_sort(id, bbd_sp->img_count, sizeof(id[0]), &imgdist_compare, NULL); for (i = 0; i < bbd_sp->img_count && swp->sw_transmit > 0.0; i++) { if (id[i].status > 0) do_ray_image(ap, pp, swp, bbd_sp, id[i].bi, id[i].dist); } if (rdebug&RDEBUG_SHADE) { bu_log("color %g %g %g\n", V3ARGS(swp->sw_color)); } /* shader must perform transmission/reflection calculations * * 0 < swp->sw_transmit <= 1 causes transmission computations * 0 < swp->sw_reflect <= 1 causes reflection computations */ if (swp->sw_reflect > 0 || swp->sw_transmit > 0) { int level = ap->a_level; ap->a_level = 0; /* Bogus hack to keep rr_render from giving up */ (void)rr_render(ap, pp, swp); ap->a_level = level; } if (rdebug&RDEBUG_SHADE) { bu_log("color %g %g %g\n", V3ARGS(swp->sw_color)); } return 1; }
/* * F B M _ P R I N T */ HIDDEN void fbm_print(register struct region *rp, char *dp) { bu_struct_print( rp->reg_name, fbm_parse, (char *)dp ); }
/* * This routine is called (at prep time) * once for each region which uses this shader. * Any shader-specific initialization should be done here. */ HIDDEN int gauss_setup(register struct region *rp, struct bu_vls *matparm, void **dpp, const struct mfuncs *UNUSED(mfp), struct rt_i *rtip) /* pointer to reg_udata in *rp */ /* New since 4.4 release */ { register struct gauss_specific *gauss_sp; struct tree_bark tb; /* check the arguments */ RT_CHECK_RTI(rtip); BU_CK_VLS(matparm); RT_CK_REGION(rp); if (rdebug&RDEBUG_SHADE) bu_log("gauss_setup(%s)\n", rp->reg_name); if (! rtip->useair) bu_bomb("gauss shader used and useair not set\n"); /* Get memory for the shader parameters and shader-specific data */ BU_GET(gauss_sp, struct gauss_specific); *dpp = gauss_sp; /* initialize the default values for the shader */ memcpy(gauss_sp, &gauss_defaults, sizeof(struct gauss_specific)); /* parse the user's arguments for this use of the shader. */ if (bu_struct_parse(matparm, gauss_parse_tab, (char *)gauss_sp, NULL) < 0) return -1; /* We have to pick up the parameters for the gaussian puff now. * They won't be available later. So what we do is sneak a peak * inside the region, make sure the first item in it is an ellipsoid * solid, and if it is go steal a peek at its balls ... er ... make * that definition/parameters. */ BU_LIST_INIT(&gauss_sp->dbil); tb.l = &gauss_sp->dbil; tb.dbip = rtip->rti_dbip; tb.name = rp->reg_name; tb.gs = gauss_sp; tree_solids (rp->reg_treetop, &tb, OP_UNION, &rt_uniresource); /* XXX If this puppy isn't axis-aligned, we should come up with a * matrix to rotate it into alignment. We're going to have to do * computation in the space defined by this ellipsoid. */ if (rdebug&RDEBUG_SHADE) { bu_struct_print(" Parameters:", gauss_print_tab, (char *)gauss_sp); bn_mat_print("m_to_sh", gauss_sp->gauss_m_to_sh); } return 1; }
HIDDEN void phong_print(register struct region *rp, void *dp) { bu_struct_print(rp->reg_name, phong_parse, (char *)dp); }
/* 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; }
HIDDEN void osl_print(register struct region *rp, void *dp) { bu_struct_print(rp->reg_name, osl_print_tab, (char *)dp); }
HIDDEN void scloud_print(register struct region *rp, void *dp) { (void)bu_struct_print(rp->reg_name, scloud_pr, (char *)dp); }
/* * F I R E _ P R I N T */ HIDDEN void fire_print(register struct region *rp, char *dp) { bu_struct_print( rp->reg_name, fire_print_tab, (char *)dp ); }
/* * T X T _ P R I N T */ HIDDEN void txt_print(register struct region *rp) { bu_struct_print(rp->reg_name, txt_parse, (char *)rp->reg_udata); }
HIDDEN void flat_print(register struct region *rp, void *dp) { bu_struct_print(rp->reg_name, flat_parse_tab, (char *)dp); }
/* F I R E _ S E T U P * * This routine is called (at prep time) * once for each region which uses this shader. * Any shader-specific initialization should be done here. */ HIDDEN int fire_setup(register struct region *rp, struct bu_vls *matparm, char **dpp, struct mfuncs *mfp, struct rt_i *rtip) /* pointer to reg_udata in *rp */ /* New since 4.4 release */ { register struct fire_specific *fire_sp; /* check the arguments */ RT_CHECK_RTI(rtip); BU_CK_VLS( matparm ); RT_CK_REGION(rp); if (rdebug&RDEBUG_SHADE) bu_log("fire_setup(%s)\n", rp->reg_name); /* Get memory for the shader parameters and shader-specific data */ BU_GETSTRUCT( fire_sp, fire_specific ); *dpp = (char *)fire_sp; /* initialize the default values for the shader */ memcpy(fire_sp, &fire_defaults, sizeof(struct fire_specific)); /* parse the user's arguments for this use of the shader. */ if (bu_struct_parse( matparm, fire_parse_tab, (char *)fire_sp ) < 0 ) return(-1); if (fire_sp->noise_size != -1.0) { VSETALL(fire_sp->noise_vscale, fire_sp->noise_size); } /* * The shader needs to operate in a coordinate system which stays * fixed on the region when the region is moved (as in animation). * We need to get a matrix to perform the appropriate transform(s). */ db_shader_mat(fire_sp->fire_m_to_sh, rtip, rp, fire_sp->fire_min, fire_sp->fire_max, &rt_uniresource); /* Build matrix to map shader space to noise space. * XXX If only we could get the frametime at this point * we could factor the flicker of flames into this matrix * rather than having to recompute it on a pixel-by-pixel basis. */ MAT_IDN(fire_sp->fire_sh_to_noise); MAT_DELTAS_VEC(fire_sp->fire_sh_to_noise, fire_sp->noise_delta); MAT_SCALE_VEC(fire_sp->fire_sh_to_noise, fire_sp->noise_vscale); /* get matrix for performing spline of fire colors */ rt_dspline_matrix(fire_sp->fire_colorspline_mat, "Catmull", 0.5, 0.0); if (rdebug&RDEBUG_SHADE || fire_sp->fire_debug ) { bu_struct_print( " FIRE Parameters:", fire_print_tab, (char *)fire_sp ); bn_mat_print( "m_to_sh", fire_sp->fire_m_to_sh ); bn_mat_print( "sh_to_noise", fire_sp->fire_sh_to_noise ); bn_mat_print( "colorspline", fire_sp->fire_colorspline_mat ); } return(1); }
/* * This routine is called (at prep time) * once for each region which uses this shader. * Any shader-specific initialization should be done here. * * Returns: * 1 success * 0 success, but delete region * -1 failure */ HIDDEN int bbd_setup(struct region *rp, struct bu_vls *matparm, void **dpp, const struct mfuncs *mfp, struct rt_i *rtip) { register struct bbd_specific *bbd_sp; struct rt_db_internal intern; struct rt_tgc_internal *tgc; int s; mat_t mat; struct bbd_img *bi; double angle; vect_t vtmp; int img_num; vect_t vv; /* check the arguments */ RT_CHECK_RTI(rtip); BU_CK_VLS(matparm); RT_CK_REGION(rp); if (rdebug&RDEBUG_SHADE) bu_log("bbd_setup(%s)\n", rp->reg_name); RT_CK_TREE(rp->reg_treetop); if (rp->reg_treetop->tr_a.tu_op != OP_SOLID) { bu_log("--- Warning: Region %s shader %s", rp->reg_name, mfp->mf_name); bu_bomb("Shader should be used on region of single (rec/rcc) primitive\n"); } RT_CK_SOLTAB(rp->reg_treetop->tr_a.tu_stp); if (rp->reg_treetop->tr_a.tu_stp->st_id != ID_REC) { bu_log("--- Warning: Region %s shader %s", rp->reg_name, mfp->mf_name); bu_log("Shader should be used on region of single REC/RCC primitive %d\n", rp->reg_treetop->tr_a.tu_stp->st_id); bu_bomb("oops\n"); } /* Get memory for the shader parameters and shader-specific data */ BU_GET(bbd_sp, struct bbd_specific); *dpp = bbd_sp; /* initialize the default values for the shader */ memcpy(bbd_sp, &bbd_defaults, sizeof(struct bbd_specific)); bu_vls_init(&bbd_sp->img_filename); BU_LIST_INIT(&bbd_sp->imgs); bbd_sp->rtip = rtip; /* because new_image() needs this */ bbd_sp->img_count = 0; /* parse the user's arguments for this use of the shader. */ if (bu_struct_parse(matparm, bbd_parse_tab, (char *)bbd_sp, NULL) < 0) return -1; if (bbd_sp->img_count > MAX_IMAGES) { bu_log("too many images (%zu) in shader for %s sb < %d\n", bbd_sp->img_count, rp->reg_name, MAX_IMAGES); bu_bomb("excessive image count\n"); } MAT_IDN(mat); RT_DB_INTERNAL_INIT(&intern); s = rt_db_get_internal(&intern, rp->reg_treetop->tr_a.tu_stp->st_dp, rtip->rti_dbip, mat, &rt_uniresource); if (intern.idb_minor_type != ID_TGC && intern.idb_minor_type != ID_REC) { bu_log("What did I get? %d\n", intern.idb_minor_type); } if (s < 0) { bu_log("%s:%d didn't get internal", __FILE__, __LINE__); bu_bomb(""); } tgc = (struct rt_tgc_internal *)intern.idb_ptr; RT_TGC_CK_MAGIC(tgc); angle = M_PI / (double)bbd_sp->img_count; img_num = 0; VMOVE(vv, tgc->h); VUNITIZE(vv); for (BU_LIST_FOR(bi, bbd_img, &bbd_sp->imgs)) { static const point_t o = VINIT_ZERO; bn_mat_arb_rot(mat, o, vv, angle*img_num); /* compute plane equation */ MAT4X3VEC(bi->img_plane, mat, tgc->a); VUNITIZE(bi->img_plane); bi->img_plane[H] = VDOT(tgc->v, bi->img_plane); MAT4X3VEC(vtmp, mat, tgc->b); VADD2(bi->img_origin, tgc->v, vtmp); /* image origin in 3d space */ /* calculate image u vector */ VREVERSE(bi->img_x, vtmp); VUNITIZE(bi->img_x); bi->img_xlen = MAGNITUDE(vtmp) * 2; /* calculate image v vector */ VMOVE(bi->img_y, tgc->h); VUNITIZE(bi->img_y); bi->img_ylen = MAGNITUDE(tgc->h); if (rdebug&RDEBUG_SHADE) { HPRINT("\nimg_plane", bi->img_plane); VPRINT("vtmp", vtmp); VPRINT("img_origin", bi->img_origin); bu_log("img_xlen:%g ", bi->img_xlen); VPRINT("img_x", bi->img_x); bu_log("img_ylen:%g ", bi->img_ylen); VPRINT("img_y", bi->img_y); } img_num++; } rt_db_free_internal(&intern); if (rdebug&RDEBUG_SHADE) { bu_struct_print(" Parameters:", bbd_print_tab, (char *)bbd_sp); } return 1; }
/* * This is called (from viewshade() in shade.c) once for each hit point * to be shaded. The purpose here is to fill in values in the shadework * structure. */ int gauss_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) /* defined in material.h */ /* ptr to the shader-specific struct */ { register struct gauss_specific *gauss_sp = (struct gauss_specific *)dp; struct seg *seg_p; struct reg_db_internals *dbint_p; double optical_density = 0.0; /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); CK_gauss_SP(gauss_sp); if (rdebug&RDEBUG_SHADE) { bu_struct_print("gauss_render Parameters:", gauss_print_tab, (char *)gauss_sp); bu_log("r_pt(%g %g %g) r_dir(%g %g %g)\n", V3ARGS(ap->a_ray.r_pt), V3ARGS(ap->a_ray.r_dir)); } BU_CK_LIST_HEAD(&swp->sw_segs->l); BU_CK_LIST_HEAD(&gauss_sp->dbil); /* look at each segment that participated in the ray partition(s) */ for (BU_LIST_FOR(seg_p, seg, &swp->sw_segs->l)) { if (rdebug&RDEBUG_SHADE) { bu_log("seg %g -> %g\n", seg_p->seg_in.hit_dist, seg_p->seg_out.hit_dist); } RT_CK_SEG(seg_p); RT_CK_SOLTAB(seg_p->seg_stp); /* check to see if the seg/solid is in this partition */ if (bu_ptbl_locate(&pp->pt_seglist, (long *)seg_p) != -1) { /* XXX You might use a bu_ptbl list of the solid pointers... */ /* check to see if the solid is from this region */ for (BU_LIST_FOR(dbint_p, reg_db_internals, &gauss_sp->dbil)) { CK_DBINT(dbint_p); if (dbint_p->st_p == seg_p->seg_stp) { /* The solid from the region is * the solid from the segment * from the partition */ optical_density += eval_seg(ap, dbint_p, seg_p); break; } } } else { if (rdebug&RDEBUG_SHADE) bu_log("gauss_render() bittest failed\n"); } }
/* * This is called (from viewshade() in shade.c) once for each hit point * to be shaded. The purpose here is to fill in values in the shadework * structure. */ HIDDEN int osl_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) /* defined in ../h/shadework.h */ /* ptr to the shader-specific struct */ { register struct osl_specific *osl_sp = (struct osl_specific *)dp; void * thread_info; int nsamples; /* Number of samples */ /* check the validity of the arguments we got */ RT_AP_CHECK(ap); RT_CHECK_PT(pp); CK_OSL_SP(osl_sp); if (rdebug&RDEBUG_SHADE) bu_struct_print("osl_render Parameters:", osl_print_tab, (char *)osl_sp); bu_semaphore_acquire(BU_SEM_SYSCALL); /* Check if it is the first time this thread is calling this function */ bool visited = false; for (size_t i = 0; i < visited_addrs.size(); i++) { if (ap->a_resource == visited_addrs[i]) { visited = true; thread_info = thread_infos[i]; break; } } if (!visited) { visited_addrs.push_back(ap->a_resource); /* Get thread specific information from OSLRender system */ thread_info = oslr->CreateThreadInfo(); thread_infos.push_back(thread_info); } if (ap->a_level == 0) { default_a_hit = ap->a_hit; /* save the default hit callback (colorview @ rt) */ default_a_miss = ap->a_miss; } bu_semaphore_release(BU_SEM_SYSCALL); Color3 acc_color(0.0f); /* ----------------------------------- * Fill in all necessary information for the OSL renderer * ----------------------------------- */ RenderInfo info; /* Set hit point */ VMOVE(info.P, swp->sw_hit.hit_point); /* Set normal at the point */ VMOVE(info.N, swp->sw_hit.hit_normal); /* Set incidence ray direction */ VMOVE(info.I, ap->a_ray.r_dir); /* U-V mapping stuff */ info.u = swp->sw_uv.uv_u; info.v = swp->sw_uv.uv_v; VSETALL(info.dPdu, 0.0f); VSETALL(info.dPdv, 0.0f); /* x and y pixel coordinates */ info.screen_x = ap->a_x; info.screen_y = ap->a_y; info.depth = ap->a_level; info.surfacearea = 1.0f; info.shader_ref = osl_sp->shader_ref; /* We assume that the only information that will be written is thread_info, so that oslr->QueryColor is thread safe */ info.thread_info = thread_info; // Ray-tracing (local illumination) /* We only perform reflection if application decides to */ info.doreflection = 0; info.out_ray_type = 0; Color3 weight = oslr->QueryColor(&info); /* Fire another ray */ if ((info.out_ray_type & RAY_REFLECT) || (info.out_ray_type & RAY_TRANSMIT)) { struct application new_ap; RT_APPLICATION_INIT(&new_ap); new_ap = *ap; /* struct copy */ new_ap.a_onehit = 1; new_ap.a_hit = default_a_hit; new_ap.a_level = info.depth + 1; new_ap.a_flag = 0; VMOVE(new_ap.a_ray.r_dir, info.out_ray.dir); VMOVE(new_ap.a_ray.r_pt, info.out_ray.origin); /* This next ray represents refraction */ if (info.out_ray_type & RAY_TRANSMIT) { /* Displace the hit point a little bit in the direction of the next ray */ Vec3 tmp; VSCALE(tmp, info.out_ray.dir, 1e-4); VADD2(new_ap.a_ray.r_pt, new_ap.a_ray.r_pt, tmp); new_ap.a_onehit = 1; new_ap.a_refrac_index = 1.5; new_ap.a_flag = 2; /* mark as refraction */ new_ap.a_hit = osl_refraction_hit; } (void)rt_shootray(&new_ap); Color3 rec; VMOVE(rec, new_ap.a_color); Color3 res = rec*weight; VMOVE(swp->sw_color, res); } else { /* Final color */ VMOVE(swp->sw_color, weight); } return 1; }
HIDDEN void bbd_print(struct region *rp, void *dp) { bu_struct_print(rp->reg_name, bbd_print_tab, (char *)dp); }
HIDDEN int scloud_setup(register struct region *rp, struct bu_vls *matparm, void **dpp, const struct mfuncs *mfp, struct rt_i *rtip) /* pointer to reg_udata in *rp */ { register struct scloud_specific *scloud; struct db_full_path full_path; mat_t region_to_model; mat_t model_to_region; mat_t tmp; BU_CK_VLS(matparm); BU_GET(scloud, struct scloud_specific); *dpp = scloud; if (rp->reg_aircode == 0) { bu_log("WARNING(%s): air shader '%s' applied to non-air region.\n%s\n", rp->reg_name, mfp->mf_name, " Set air flag with \"edcodes\" in mged"); bu_bomb(""); } memcpy(scloud, &scloud_defaults, sizeof(struct scloud_specific)); if (rdebug&RDEBUG_SHADE) bu_log("scloud_setup\n"); if (bu_struct_parse(matparm, scloud_parse, (char *)scloud) < 0) return -1; if (rdebug&RDEBUG_SHADE) (void)bu_struct_print(rp->reg_name, scloud_parse, (char *)scloud); /* get transformation between world and "region" coordinates */ if (db_string_to_path(&full_path, rtip->rti_dbip, rp->reg_name)) { /* bad thing */ bu_bomb("db_string_to_path() error"); } if (! db_path_to_mat(rtip->rti_dbip, &full_path, region_to_model, 0, &rt_uniresource)) { /* bad thing */ bu_bomb("db_path_to_mat() error"); } /* get matrix to map points from model space to "region" space */ bn_mat_inv(model_to_region, region_to_model); /* add the noise-space scaling */ MAT_IDN(tmp); if (!EQUAL(scloud->scale, 1.0)) { tmp[0] = tmp[5] = tmp[10] = 1.0 / scloud->scale; } else { tmp[0] = 1.0 / (scloud->vscale[0]); tmp[5] = 1.0 / (scloud->vscale[1]); tmp[10] = 1.0 / (scloud->vscale[2]); } bn_mat_mul(scloud->mtos, tmp, model_to_region); /* add the translation within noise space */ MAT_IDN(tmp); tmp[MDX] = scloud->delta[0]; tmp[MDY] = scloud->delta[1]; tmp[MDZ] = scloud->delta[2]; bn_mat_mul2(tmp, scloud->mtos); bn_mat_inv(scloud->stom, scloud->mtos); return 1; }
HIDDEN void ckr_print(register struct region *rp, void *UNUSED(dp)) { bu_struct_print(rp->reg_name, ckr_parse, (const char *)rp->reg_udata); }
/* * B R D F _ R E N D E R * 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) and normalized by PI : Rd = Rp * cos(I) / PI (4) The specular reflectance is calculated by the product of the specular reflectance coeffient and a term dependent on the surface roughness : Rs = W(I, O) * R(I, O, r) (5) Where, I is the angle of incidence. O is the angle to the observer. r is the standard deviation (RMS) of the surface slope. W returns the specular reflection coefficient as a function of the angle of incidence, and the viewer angle. R is a surface roughness term. */ HIDDEN int brdf_render(register struct application *ap, struct partition *pp, struct shadework *swp, char *dp) { register struct light_specific *lp; register fastf_t *intensity, *to_light; register int i; register fastf_t cosi, cosr; register fastf_t refl; vect_t h_dir; vect_t to_eye; vect_t work; vect_t cprod; /* color product */ point_t matcolor; /* Material color */ struct brdf_specific *ps = (struct brdf_specific *)dp; if (ps->magic != BRDF_MAGIC ) bu_log("brdf_render: bad magic\n"); if (rdebug&RDEBUG_SHADE) bu_struct_print( "brdf_render", brdf_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 (swp->sw_xmitonly ) { if (swp->sw_reflect > 0 || swp->sw_transmit > 0 ) (void)rr_render( ap, pp, swp ); return(1); /* done */ } VMOVE( matcolor, swp->sw_color ); /* Diffuse reflectance from "Ambient" light source (at eye) */ if ((cosr = -VDOT( swp->sw_hit.hit_normal, ap->a_ray.r_dir )) > 0.0 ) { if (cosr > 1.00001 ) { bu_log("cosAmb=1+%g (x%d, y%d, lvl%d)\n", cosr-1, ap->a_x, ap->a_y, ap->a_level); cosr = 1; } refl = cosr * AmbientIntensity; VSCALE( swp->sw_color, matcolor, refl ); } else { VSETALL( swp->sw_color, 0 ); } VREVERSE( to_eye, ap->a_ray.r_dir ); /* Consider effects of each light source */ for ( i=ap->a_rt_i->rti_nlights-1; i>=0; i-- ) { fastf_t cos_tmp; fastf_t tan_sq; double exponent; if ((lp = (struct light_specific *)swp->sw_visible[i]) == LIGHT_NULL ) continue; /* Light is not shadowed -- add this contribution */ intensity = swp->sw_intensity+3*i; to_light = swp->sw_tolight+3*i; if ((cosi = VDOT( swp->sw_hit.hit_normal, to_light )) > 0.0 ) { if (cosi > 1.00001 ) { bu_log("cosI=1+%g (x%d, y%d, lvl%d)\n", cosi-1, ap->a_x, ap->a_y, ap->a_level); cosi = 1; } /* Diffuse reflectance from this light source. */ refl = cosi * lp->lt_fraction * ps->diffuse_refl; VELMUL( work, lp->lt_color, intensity ); VELMUL( cprod, matcolor, work ); VJOIN1( swp->sw_color, swp->sw_color, refl, cprod ); /* Calculate specular reflectance. */ if (NEAR_ZERO( ps->rms_sq, SMALL_FASTF ) ) continue; VADD2( h_dir, to_eye, to_light ) VUNITIZE( h_dir ); cos_tmp = VDOT( h_dir, swp->sw_hit.hit_normal ); if (cos_tmp <= 0.0 ) continue; cos_tmp *= cos_tmp; if (NEAR_ZERO( cos_tmp, SMALL_FASTF ) ) continue; tan_sq = (1.0-cos_tmp)/cos_tmp; exponent = (-tan_sq/ps->rms_sq ); refl = ps->specular_refl * lp->lt_fraction * exp( exponent ) / sqrt( cosi * cosr ) / ps->denom; if (refl > 1.0 ) refl = 1.0; VELMUL( work, lp->lt_color, intensity ); VJOIN1( swp->sw_color, swp->sw_color, refl, work ); } } if (swp->sw_reflect > 0 || swp->sw_transmit > 0 ) (void)rr_render( ap, pp, swp ); return(1); }