/* * 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; }
/* 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; }
int scloud_render(struct application *ap, const struct partition *pp, struct shadework *swp, void *dp) { register struct scloud_specific *scloud_sp = (struct scloud_specific *)dp; point_t in_pt; /* point where ray enters scloud solid */ point_t out_pt; /* point where ray leaves scloud solid */ point_t pt; vect_t v_cloud;/* vector representing ray/solid intersection */ double thickness; /* magnitude of v_cloud (distance through solid) */ int steps; /* # of samples along ray/solid intersection */ double step_delta;/* distance between sample points, texture space */ int i; double val; double trans; point_t incident_light = VINIT_ZERO; double delta_dpmm; double density; struct shadework sub_sw; struct light_specific *lp; RT_CHECK_PT(pp); RT_AP_CHECK(ap); RT_CK_REGION(pp->pt_regionp); /* compute the ray/solid in and out points, * and transform them into "shader space" coordinates */ VJOIN1(pt, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir); MAT4X3PNT(in_pt, scloud_sp->mtos, pt); VJOIN1(pt, ap->a_ray.r_pt, pp->pt_outhit->hit_dist, ap->a_ray.r_dir); MAT4X3PNT(out_pt, scloud_sp->mtos, pt); /* get ray/solid intersection vector (in noise space) * and compute thickness of solid (in noise space) along ray path */ VSUB2(v_cloud, out_pt, in_pt); thickness = MAGNITUDE(v_cloud); /* The noise field used by the bn_noise_turb and bn_noise_fbm routines * has a maximum frequency of about 1 cycle per integer step in * noise space. Each octave increases this frequency by the * "lacunarity" factor. To sample this space adequately we need * * 4 samples per integer step for the first octave, * lacunarity * 4 samples/step for the second octave, * lacunarity^2 * 4 samples/step for the third octave, * lacunarity^3 * 4 samples/step for the forth octave, * * so for a computation with 4 octaves we need something on the * order of lacunarity^3 * 4 samples per integer step in noise space. */ steps = pow(scloud_sp->lacunarity, scloud_sp->octaves-1) * 4; step_delta = thickness / (double)steps; if (rdebug&RDEBUG_SHADE) bu_log("steps=%d delta=%g thickness=%g\n", steps, step_delta, thickness); VUNITIZE(v_cloud); VMOVE(pt, in_pt); trans = 1.0; delta_dpmm = scloud_sp->max_d_p_mm - scloud_sp->min_d_p_mm; sub_sw = *swp; /* struct copy */ sub_sw.sw_inputs = MFI_HIT; for (i=0; i < steps; i++) { /* compute the next point in the cloud space */ VJOIN1(pt, in_pt, i*step_delta, v_cloud); /* get turbulence value (0 .. 1) */ val = bn_noise_turb(pt, scloud_sp->h_val, scloud_sp->lacunarity, scloud_sp->octaves); density = scloud_sp->min_d_p_mm + val * delta_dpmm; val = exp(- density * step_delta); trans *= val; if (swp->sw_xmitonly) continue; /* need to set the hit in our fake shadework structure */ MAT4X3PNT(sub_sw.sw_hit.hit_point, scloud_sp->stom, pt); sub_sw.sw_transmit = trans; sub_sw.sw_inputs = MFI_HIT; light_obs(ap, &sub_sw, swp->sw_inputs); /* now we know how much light has arrived from each * light source to this point */ for (i=ap->a_rt_i->rti_nlights-1; i >= 0; i--) { lp = (struct light_specific *)swp->sw_visible[i]; if (lp == LIGHT_NULL) continue; /* compute how much light has arrived at * this location */ incident_light[0] += sub_sw.sw_intensity[3*i+0] * lp->lt_color[0] * sub_sw.sw_lightfract[i]; incident_light[1] += sub_sw.sw_intensity[3*i+1] * lp->lt_color[1] * sub_sw.sw_lightfract[i]; incident_light[2] += sub_sw.sw_intensity[3*i+2] * lp->lt_color[2] * sub_sw.sw_lightfract[i]; } VSCALE(incident_light, incident_light, trans); } /* scloud is basically a white object with partial transparency */ swp->sw_transmit = trans; if (swp->sw_xmitonly) return 1; /* * At the point of maximum opacity, check light visibility * for light color and cloud shadowing. * OOPS: Don't use an interior point, or light_visibility() * will see an attenuated light source. */ swp->sw_hit.hit_dist = pp->pt_inhit->hit_dist; VJOIN1(swp->sw_hit.hit_point, ap->a_ray.r_pt, swp->sw_hit.hit_dist, ap->a_ray.r_dir); VREVERSE(swp->sw_hit.hit_normal, ap->a_ray.r_dir); swp->sw_inputs |= MFI_HIT | MFI_NORMAL; light_obs(ap, swp, swp->sw_inputs); VSETALL(incident_light, 0); for (i=ap->a_rt_i->rti_nlights-1; i>=0; i--) { struct light_specific *lp2; if ((lp2 = (struct light_specific *)swp->sw_visible[i]) == LIGHT_NULL) continue; /* XXX don't have a macro for this */ incident_light[0] += swp->sw_intensity[3*i+0] * lp2->lt_color[0]; incident_light[1] += swp->sw_intensity[3*i+1] * lp2->lt_color[1]; incident_light[2] += swp->sw_intensity[3*i+2] * lp2->lt_color[2]; } VELMUL(swp->sw_color, swp->sw_color, incident_light); if (rdebug&RDEBUG_SHADE) { pr_shadework("scloud: after light vis, before rr_render", swp); } if (swp->sw_reflect > 0 || swp->sw_transmit > 0) (void)rr_render(ap, pp, swp); return 1; }
/** * Calculate a bounding RPP for a superell */ int rt_superell_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *UNUSED(tol)) { struct rt_superell_internal *eip; fastf_t magsq_a, magsq_b, magsq_c; vect_t Au, Bu, Cu; mat_t R; vect_t w1, w2, P; /* used for bounding RPP */ fastf_t f; eip = (struct rt_superell_internal *)ip->idb_ptr; RT_SUPERELL_CK_MAGIC(eip); magsq_a = MAGSQ(eip->a); magsq_b = MAGSQ(eip->b); magsq_c = MAGSQ(eip->c); /* Create unit length versions of A, B, C */ f = 1.0/sqrt(magsq_a); VSCALE(Au, eip->a, f); f = 1.0/sqrt(magsq_b); VSCALE(Bu, eip->b, f); f = 1.0/sqrt(magsq_c); VSCALE(Cu, eip->c, f); MAT_IDN(R); VMOVE(&R[0], Au); VMOVE(&R[4], Bu); VMOVE(&R[8], Cu); /* Compute bounding RPP */ VSET(w1, magsq_a, magsq_b, magsq_c); /* X */ VSET(P, 1.0, 0, 0); /* bounding plane normal */ MAT3X3VEC(w2, R, P); /* map plane to local coord syst */ VELMUL(w2, w2, w2); /* square each term */ f = VDOT(w1, w2); f = sqrt(f); (*min)[X] = eip->v[X] - f; /* V.P +/- f */ (*max)[X] = eip->v[X] + f; /* Y */ VSET(P, 0, 1.0, 0); /* bounding plane normal */ MAT3X3VEC(w2, R, P); /* map plane to local coord syst */ VELMUL(w2, w2, w2); /* square each term */ f = VDOT(w1, w2); f = sqrt(f); (*min)[Y] = eip->v[Y] - f; /* V.P +/- f */ (*max)[Y] = eip->v[Y] + f; /* Z */ VSET(P, 0, 0, 1.0); /* bounding plane normal */ MAT3X3VEC(w2, R, P); /* map plane to local coord syst */ VELMUL(w2, w2, w2); /* square each term */ f = VDOT(w1, w2); f = sqrt(f); (*min)[Z] = eip->v[Z] - f; /* V.P +/- f */ (*max)[Z] = eip->v[Z] + f; return 0; }
/* * 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); }
/* * 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 {