// 鏡面反射 Color PathTracer::Radiance_Specular(const Scene &scene, const Ray &ray, Random &rnd, const int depth, Scene::IntersectionInformation &intersect, const Vector3 &normal, double russian_roulette_prob) { Vector3 reflected_dir(ray.dir - normal*2*ray.dir.dot(normal)); reflected_dir.normalize(); // 間接光の評価 Ray newray(intersect.hit.position, reflected_dir); Color income = Radiance(scene, newray, rnd, depth+1); // 直接光の評価 Scene::IntersectionInformation newhit; if (m_performNextEventEstimation && scene.CheckIntersection(newray, newhit)) { income += newhit.object->material.emission; } Color weight = intersect.texturedHitpointColor / russian_roulette_prob; return Vector3(weight.x*income.x, weight.y*income.y, weight.z*income.z); }
vec3f RayTracer::traceRay( Scene *scene, const ray& r, const vec3f& thresh, int depth, isect& i, vector<const SceneObject*>& stack ) { if( depth>=0 && thresh[0] > threshold - RAY_EPSILON && thresh[1] > threshold - RAY_EPSILON && thresh[2] > threshold - RAY_EPSILON && scene->intersect( r, i ) ) { // YOUR CODE HERE // An intersection occured! We've got work to do. For now, // this code gets the material for the surface that was intersected, // and asks that material to provide a color for the ray. // This is a great place to insert code for recursive ray tracing. // Instead of just returning the result of shade(), add some // more steps: add in the contributions from reflected and refracted // rays. const Material& m = i.getMaterial(); vec3f color = m.shade(scene, r, i); //calculate the reflected ray vec3f d = r.getDirection(); vec3f position = r.at(i.t); vec3f direction = d - 2 * i.N * d.dot(i.N); ray newray(position, direction); if(!m.kr.iszero()) { vec3f reflect = m.kr.multiply(traceRay(scene, newray, thresh.multiply(m.kr), depth-1, stack).clamp()); color += reflect; } //calculate the refracted ray double ref_ratio; double sin_ang = d.cross(i.N).length(); vec3f N = i.N; //Decide going in or out const SceneObject *mi = NULL, *mt = NULL; int stack_idx = -1; vector<const SceneObject*>::reverse_iterator itr; //1 use the normal to decide whether to go in or out //0: travel through, 1: in, 2: out char travel = 0; if(i.N.dot(d) <= -RAY_EPSILON) { //from outer surface in //test whether the object has two face ray test_ray(r.at(i.t) + d * 2 * RAY_EPSILON, -d); isect test_i; if(i.obj->intersect(r, test_i) && test_i.N.dot(N) > -RAY_EPSILON) { //has interior travel = 1; } } else { travel = 2; } if(travel == 1) { if(!stack.empty()) { mi = stack.back(); } mt = i.obj; stack.push_back(mt); } else if(travel == 2) { //if it is in our stack, then we must pop it for(itr = stack.rbegin(); itr != stack.rend(); ++itr) { if(*itr == i.obj) { mi = *itr; vector<const SceneObject*>::iterator ii = itr.base() - 1; stack_idx = ii - stack.begin(); stack.erase(ii); break; } } if(!stack.empty()) { mt = stack.back(); } } if(N.dot(d) >= RAY_EPSILON) { N = -N; } ref_ratio = (mi?(mi->getMaterial().index):1.0) / (mt?(mt->getMaterial().index):1.0); if(!m.kt.iszero() && (ref_ratio < 1.0 + RAY_EPSILON || sin_ang < 1.0 / ref_ratio + RAY_EPSILON)) { //No total internal reflection //We do refraction now double c = N.dot(-d); direction = (ref_ratio * c - sqrt(1 - ref_ratio * ref_ratio * (1 - c * c))) * N + ref_ratio * d; newray = ray(position, direction); vec3f refraction = m.kt.multiply(traceRay(scene, newray, thresh.multiply(m.kt), depth-1, stack).clamp()); color += refraction; } if(travel == 1) { stack.pop_back(); } else if(travel == 2) { if(mi) { stack.insert(stack.begin() + stack_idx, mi); } } return color; } else { // No intersection. This ray travels to infinity, so we color // it according to the background color, which in this (simple) case // is just black. if(m_bBackground && bg) { double u, v; angleToSphere(r.getDirection(), u, v); //Scale to [0, 1]; u /= 2 * M_PI; v /= M_PI; int tx = int(u * bg_width), ty = bg_height - int(v * bg_height); return vec3f(bg[3 * (ty * bg_width + tx)] / 255.0, bg[3 * (ty * bg_width + tx) + 1] / 255.0, bg[3 * (ty * bg_width + tx) + 2] / 255.0); } else { return vec3f( 0.0, 0.0, 0.0 ); } } }