Color ray_shade(int level, Real w, Ray v, RContext *rc, Object *ol) { Inode *i = ray_intersect(ol, v); if (i != NULL) { Light *l; Real wf; Material *m = i->m; Vector3 p = ray_point(v, i->t); Cone recv = cone_make(p, i->n, PIOVER2); Color c = c_mult(m->c, c_scale(m->ka, ambient(rc))); rc->p = p; for (l = rc->l; l != NULL; l = l->next) if ((*l->transport)(l, recv, rc) && (wf = shadow(l, p, ol)) > RAY_WF_MIN) c = c_add(c, c_mult(m->c, c_scale(wf * m->kd * v3_dot(l->outdir,i->n), l->outcol))); if (level++ < MAX_RAY_LEVEL) { if ((wf = w * m->ks) > RAY_WF_MIN) { Ray r = ray_make(p, reflect_dir(v.d, i->n)); c = c_add(c, c_mult(m->s, c_scale(m->ks, ray_shade(level, wf, r, rc, ol)))); } if ((wf = w * m->kt) > RAY_WF_MIN) { Ray t = ray_make(p, refract_dir(v.d, i->n, (i->enter)? 1/m->ir: m->ir)); if (v3_sqrnorm(t.d) > 0) { c = c_add(c, c_mult(m->s, c_scale(m->kt, ray_shade(level, wf, t, rc, ol)))); } } } inode_free(i); return c; } else { return BG_COLOR; } }
// 屈折面 Color PathTracer::Radiance_Refraction(const Scene &scene, const Ray &ray, Random &rnd, const int depth, Scene::IntersectionInformation &intersect, const Vector3 &normal, double russian_roulette_prob) { bool into = intersect.hit.normal.dot(normal) > 0.0; Vector3 reflect_dir = ray.dir - normal*2*ray.dir.dot(normal); reflect_dir.normalize(); double n_vacuum = REFRACTIVE_INDEX_VACUUM; double n_obj = intersect.object->material.refraction_rate; double n_ratio = into ? n_vacuum/n_obj : n_obj/n_vacuum; double dot = ray.dir.dot(normal); double cos2t = 1-n_ratio*n_ratio*(1-dot*dot); // 反射方向の直接光の評価 Color reflect_direct; Ray reflect_ray(intersect.hit.position, reflect_dir); Scene::IntersectionInformation reflected_hit; if (m_performNextEventEstimation && scene.CheckIntersection(reflect_ray, reflected_hit)) { reflect_direct = reflected_hit.object->material.emission; } if (cos2t < 0) { // 全反射 Color income = reflect_direct + Radiance(scene, Ray(intersect.hit.position, reflect_dir), rnd, depth+1); Color weight = intersect.object->material.color / russian_roulette_prob; return Vector3(weight.x*income.x, weight.y*income.y, weight.z*income.z); } // 屈折方向 Vector3 refract_dir( ray.dir*n_ratio - intersect.hit.normal * (into ? 1.0 : -1.0) * (dot*n_ratio + sqrt(cos2t)) ); refract_dir.normalize(); const Ray refract_ray(intersect.hit.position, refract_dir); // 屈折方向の直接光の評価 Color refract_direct; if (m_performNextEventEstimation && scene.CheckIntersection(refract_ray, reflected_hit)) { refract_direct = reflected_hit.object->material.emission; } // Fresnel の式 double F0 = (n_obj-n_vacuum)*(n_obj-n_vacuum)/((n_obj+n_vacuum)*(n_obj+n_vacuum)); double c = 1 - ( into ? -dot : -refract_dir.dot(normal) ); // 1-cosθ double Fr = F0 + (1-F0)*pow(c, 5.0); // Fresnel (反射の割合) double n_ratio2 = n_ratio*n_ratio; // 屈折前後での放射輝度の変化率 double Tr = (1-Fr)*n_ratio2; // 屈折直後→直前の割合 Color income, weight; if (depth > 2) { // 反射 or 屈折のみ追跡 const double reflect_prob = 0.1 + 0.8 * Fr; if (rnd.nextDouble() < reflect_prob) { // 反射 income = (reflect_direct + Radiance(scene, Ray(intersect.hit.position, reflect_dir), rnd, depth+1)) * Fr; weight = intersect.texturedHitpointColor / (russian_roulette_prob * reflect_prob); } else { // 屈折 income = (refract_direct + Radiance(scene, refract_ray, rnd, depth+1)) * Tr; weight = intersect.texturedHitpointColor / (russian_roulette_prob * (1 - reflect_prob)); } } else { // 反射と屈折両方追跡 m_omittedRayCount++; income = (reflect_direct + Radiance(scene, Ray(intersect.hit.position, reflect_dir), rnd, depth+1)) * Fr + (refract_direct + Radiance(scene, refract_ray, rnd, depth + 1)) *Tr; weight = intersect.texturedHitpointColor / russian_roulette_prob; } return /*intersect.object->material.emission +*/ Vector3(weight.x*income.x, weight.y*income.y, weight.z*income.z); }