Color PathTracer::glossyBRDF(const Shape &obj, const Ray & ray, const Vector & hitpoint, const Vector & nl, int depth, int E) const { //cosine distribution?? const double r1 = 2.0 * M_PI * drand48(); const double r2 = pow(drand48(), 1.0 / (100.0 + 1.0)); const double r2s = sqrt(1.0 - r2 * r2); //same as above Vector sw = (ray.dir - nl * 2.0 * nl.Dot(ray.dir)).Normalized(); Vector su; if (fabs(sw.x) > 0.1) su = Vector(0.0, 1.0, 0.0); else su = Vector(1.0, 0.0, 0.0); su = su.Cross(sw).Normalized(); Vector sv = sw.Cross(su); Vector d = (su * cos(r1) * r2s + sv * sin(r1) * r2s + sw * r2).Normalized(); double orientation = nl.Dot(d); //if ray is beneath surface , reflect the resulting ray around the perfect mirror ray if(orientation < 0) d = d - sw * 2.0 * sw.Dot(d); Color col = obj.color; return obj.emission + col.MultComponents(Radiance(Ray(hitpoint, d), depth, E)); }
void RadianceTexture() { // Loop over the rows and columns of the image and evaluate radiance and // covariance per pixel using Monte-Carlo. #pragma omp parallel for schedule(dynamic, 1) for (int y=0; y<height; y++){ Random rng(y + height*clock()); for (int x=0; x<width; x++) { int i=(width-x-1)*height+y; // Create the RNG and get the sub-pixel sample float dx = rng(); float dy = rng(); // Generate the pixel direction Vector d = cx*((dx + x)/float(width) - .5) + cy*((dy + y)/float(height) - .5) + cam.d; d.Normalize(); Ray ray(cam.o, d); Vector radiance = Radiance(spheres, ray, rng, 0, 1); bcg_img[i] = (float(nPasses)*bcg_img[i] + Vector::Dot(radiance, Vector(1,1,1))/3.0f) / float(nPasses+1); } } ++nPasses; }
// Lambert 面の Radiance の評価 Color PathTracer::Radiance_Lambert(const Scene &scene, const Ray &ray, Random &rnd, const int depth, Scene::IntersectionInformation &intersect, const Vector3 &normal, double russian_roulette_prob) { Color direct; // 直接光を評価する if (m_performNextEventEstimation && intersect.object->material.emission.lengthSq() == 0) { direct = DirectRadiance_Lambert(scene, ray, rnd, depth, true, intersect, normal); } Vector3 w,u,v; w = normal; if (fabs(normal.x) > EPS) { u = Vector3(0,1,0).cross(w); } else { u = Vector3(1,0,0).cross(w); } v = w.cross(u); double u1 = rnd.nextDouble(); double u2 = rnd.nextDouble(); // pdf is 1/PI //double r1 = PI*u1; // Φ //double r2 = 1-u2; // cosθ //double r3 = sqrt(1-r2*r2); // sinθ //double pdf = 1/PI; // pdf is cosθ/PI double r1 = 2*PI*u1; double r2 = sqrt(u2); // cosθ double r3 = sqrt(1-u2); // sinθ double pdf = r2/PI; //const double r1 = 2 * PI * rnd.nextDouble(); //const double r2 = rnd.nextDouble(), r2s = sqrt(r2); //Vector3 dir = u * cos(r1) * r2s + v * sin(r1) * r2s + w * sqrt(1.0-r2); //dir.normalize(); //weight = intersect.object->color / russian_roulette_probability; Vector3 dir = u*r3*cos(r1) + v*r3*sin(r1) + w*r2; dir.normalize(); //Color weight = intersect.object->color / PI * r2 / pdf / russian_roulette_prob; Color weight = intersect.texturedHitpointColor; Color income(0,0,0); if (intersect.object->material.emission.lengthSq() == 0) { income = Radiance(scene, Ray(intersect.hit.position, dir), rnd, depth + 1); } // direct はすでに反射率が乗算済みなので、weightを掛ける必要はない return ( Vector3(weight.x*income.x, weight.y*income.y, weight.z*income.z) + direct ) / russian_roulette_prob; }
// 鏡面反射 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); }
void PathTracer::ScanPixelsAndCastRays(const Scene &scene, int previous_samples, int next_samples) { m_processed_y_counts = 0; const size_t height = m_camera.GetScreenHeight(); const size_t width = m_camera.GetScreenWidth(); // trace all pixels const double averaging_factor = next_samples * m_supersamples * m_supersamples; #pragma omp parallel for schedule(dynamic, 1) for (int y = 0; y<(signed)height; y++) { Random rnd(y+1+previous_samples*height); for (int x = 0; x<(signed)width && m_enableRendering; x++) { const int index = x + (height - y - 1)*width; Color accumulated_radiance; // super-sampling for (int sy = 0; sy<m_supersamples && m_enableRendering; sy++) for (int sx = 0; sx < m_supersamples && m_enableRendering; sx++) { // (x,y)ピクセル内での位置: [0,1] const double rx = (2.0*sx + 1.0)/(2*m_supersamples); const double ry = (2.0*sy + 1.0)/(2*m_supersamples); Ray ray(m_camera.SampleRayForPixel(x + rx, y + ry, rnd)); // (m_samples)回サンプリングする for (int s=previous_samples+1; s<=next_samples; s++) { accumulated_radiance += Radiance(scene, ray, rnd, 0); m_omittedRayCount++; } } // img_n+c(x) = n/(n+c)*img_n(x) + 1/(n+c)*sum_{n+1}^{n+c}rad_i(x)/supersamples^2 m_result[index] = m_result[index] * (static_cast<double>(previous_samples) / next_samples) + accumulated_radiance / averaging_factor; } m_processed_y_counts++; //cerr << "y = " << y << ": " << static_cast<double>(m_processed_y_counts)/height*100 << "% finished" << endl; } }
Color PathTracer::specularBRDF(const Shape &obj, const Ray & ray, const Vector & hitpoint, const Vector & nl, int depth) const { Color col = obj.color; /* Return light emission mirror reflection (via recursive call using perfect reflection vector) */ return obj.emission + col.MultComponents(Radiance(Ray(hitpoint, ray.dir - nl * 2 * nl.Dot(ray.dir)), depth, 1)); }
Color PathTracer::refrBRDF(const Shape &obj, const Ray & ray, const Vector & hitpoint, const Vector & nl, int depth) const { Vector obj_normal = obj.get_normal(hitpoint); /* Otherwise object transparent, i.e. assumed dielectric glass material */ Ray reflRay = Ray(hitpoint, ray.dir - obj_normal * 2 * obj_normal.Dot(ray.dir)); /* Prefect reflection */ bool into = obj_normal.Dot(nl) > 0; /* Bool for checking if ray from outside going in */ double nc = 1; /* Index of refraction of air (approximately) */ double nt = 1.5; /* Index of refraction of glass (approximately) */ double nnt; Color col = obj.color; double Re, RP, TP, Tr; Vector tdir; if (into) /* Set ratio depending on hit from inside or outside */ nnt = nc / nt; else nnt = nt / nc; double ddn = ray.dir.Dot(nl); double cos2t = 1 - nnt * nnt * (1 - ddn * ddn); /* Check for total internal reflection, if so only reflect */ if (cos2t < 0) { return obj.emission + col.MultComponents(Radiance(reflRay, depth, 1)); } /* Otherwise reflection and/or refraction occurs */ /* Determine transmitted ray direction for refraction */ if (into) tdir = (ray.dir * nnt - obj_normal * (ddn * nnt + sqrt(cos2t))).Normalized(); else tdir = (ray.dir * nnt + obj_normal * (ddn * nnt + sqrt(cos2t))).Normalized(); /* Determine R0 for Schlick�s approximation */ double a = nt - nc; double b = nt + nc; double R0 = a * a / (b * b); /* Cosine of correct angle depending on outside/inside */ double c; if (into) c = 1 + ddn; else c = 1 - tdir.Dot(obj_normal); /* Compute Schlick�s approximation of Fresnel equation */ Re = R0 + (1 - R0) * c * c * c * c * c; /* Reflectance */ Tr = 1 - Re; /* Transmittance */ /* Probability for selecting reflectance or transmittance */ double P = .25 + .5 * Re; RP = Re / P; /* Scaling factors for unbiased estimator */ TP = Tr / (1 - P); if (depth < 3) { /* Initially both reflection and trasmission */ return obj.emission + col.MultComponents(Radiance(reflRay, depth, 1) * Re + Radiance(Ray(hitpoint, tdir), depth, 1) * Tr); } else if (drand48() < P) { return obj.emission + col.MultComponents(Radiance(reflRay, depth, 1) * RP); } return obj.emission + col.MultComponents(Radiance(Ray(hitpoint, tdir), depth, 1) * TP); }
Color PathTracer::calculatePixelColor(const Ray & ray) const { return Radiance(ray, 0, 1); }
Color PathTracer::translBRDF(const Shape &obj, const Ray & ray, const Vector & hitpoint, const Vector & nl, int depth) const { //cosine distribution?? const double r1 = 2.0 * M_PI * drand48(); //the exponent is the Transluency Factor, should be changable const double r2 = pow(drand48(), 1.0 / (1.0 + 100.0)); const double r2s = sqrt(1.0 - r2 * r2); //same as above Vector sw_g = (ray.dir - nl * 2.0 * nl.Dot(ray.dir)).Normalized(); Vector su_g; if (fabs(sw_g.x) > 0.1) su_g = Vector(0.0, 1.0, 0.0); else su_g = Vector(1.0, 0.0, 0.0); su_g = su_g.Cross(sw_g).Normalized(); Vector sv_g = sw_g.Cross(su_g); Vector glos_refl_d = (su_g * cos(r1) * r2s + sv_g * sin(r1) * r2s + sw_g * r2).Normalized(); Vector obj_normal = obj.get_normal(hitpoint); /* Otherwise object transparent, i.e. assumed dielectric glass material */ Ray reflRay = Ray(hitpoint, glos_refl_d); bool into = obj_normal.Dot(nl) > 0; /* Bool for checking if ray from outside going in */ double nc = 1; /* Index of refraction of air (approximately) */ double nt = 1.5; /* Index of refraction of glass (approximately) */ double nnt; Color col = obj.color; double Re, RP, TP, Tr; Vector tdir; if (into) /* Set ratio depending on hit from inside or outside */ nnt = nc / nt; else nnt = nt / nc; double ddn = ray.dir.Dot(nl); double cos2t = 1 - nnt * nnt * (1 - ddn * ddn); /* Check for total internal reflection, if so only reflect */ if (cos2t < 0) { return obj.emission + col.MultComponents(Radiance(reflRay, depth, 1)); } /* Otherwise reflection and/or refraction occurs */ //create Translucent refraction Vector const double r1p = 2.0 * M_PI * drand48(); const double r2p= pow(drand48(), 1.0 / (1.0 + 100.0)); const double r2sp = sqrt(1.0 - r2 * r2); Vector sw_p = (ray.dir); Vector su_p; if (fabs(sw_p.x) > 0.1) su_p = Vector(0.0, 1.0, 0.0); else su_p = Vector(1.0, 0.0, 0.0); su_p = su_p.Cross(sw_p).Normalized(); Vector sv_p = sw_p.Cross(su_p); Vector transl_d = (su_p * cos(r1) * r2sp + sv_p * sin(r1p) * r2sp + sw_p * r2p).Normalized(); /* Determine transmitted ray direction for refraction */ if (into) tdir = (transl_d * nnt - obj_normal * (ddn * nnt + sqrt(cos2t))).Normalized(); else tdir = (transl_d * nnt + obj_normal * (ddn * nnt + sqrt(cos2t))).Normalized(); /* Determine R0 for Schlick's approximation */ double a = nt - nc; double b = nt + nc; double R0 = a * a / (b * b); /* Cosine of correct angle depending on outside/inside */ double c; if (into) c = 1 + ddn; else c = 1 - tdir.Dot(obj_normal); /* Compute Schlick's approximation of Fresnel equation */ Re = R0 + (1 - R0) * c * c * c * c * c; /* Reflectance */ Tr = 1 - Re; /* Transmittance */ /* Probability for selecting reflectance or transmittance */ double P = .25 + .5 * Re; RP = Re / P; /* Scaling factors for unbiased estimator */ TP = Tr / (1 - P); if (depth < 3) { /* Initially both reflection and trasmission */ return obj.emission + col.MultComponents(Radiance(reflRay, depth, 1) * Re + Radiance(Ray(hitpoint, tdir), depth, 1) * Tr); } else if (drand48() < P) { return obj.emission + col.MultComponents(Radiance(reflRay, depth, 1) * RP); } return obj.emission + col.MultComponents(Radiance(Ray(hitpoint, tdir), depth, 1) * TP); }
Color PathTracer::diffuseBRDF(const Shape &obj, const Ray & ray, const Vector & hitpoint, const Vector & nl, int depth, int E) const { /* Compute random reflection vector on hemisphere */ double r1 = 2.0 * M_PI * drand48(); double r2 = drand48(); double r2s = sqrt(r2); Color col = obj.color; Vector e(0,0,0); /* Set up local orthogonal coordinate system u,v,w on surface */ Vector w = nl; Vector u; if (fabs(w.x) > .1) u = Vector(0.0, 1.0, 0.0).Cross(w).Normalized(); else u = (Vector(1.0, 0.0, 0.0).Cross(w)).Normalized(); Vector v = w.Cross(u); /* Random reflection vector d */ Vector d = (u * cos(r1) * r2s + v * sin(r1) * r2s + w * sqrt(1 - r2)).Normalized(); if(sampleLight) { /* Explicit computation of direct lighting */ for (int i = 0; i < numObjects; i++) { const Shape &cur_obj = *our_scene.get_shapes()[i]; if (cur_obj.emission.x <= 0 && cur_obj.emission.y <= 0 && cur_obj.emission.z <= 0) continue; /* Skip objects that are not light sources */ /* Randomly sample spherical light source from surface intersection */ /* Set up local orthogonal coordinate system su,sv,sw towards light source */ Vector cur_cent = cur_obj.get_position(); Vector sw = (cur_cent - hitpoint).Normalized(); Vector su; if (fabs(sw.x) > 0.1) su = Vector(0.0, 1.0, 0.0); else su = Vector(1.0, 0.0, 0.0); su = (sw.Cross(su)).Normalized(); Vector sv = sw.Cross(su); // next event estimation /* Create random sample direction l towards spherical light source */ double cos_a_max = sqrt(1.0 - cur_obj.radius * cur_obj.radius / (hitpoint - cur_cent).Dot(hitpoint - cur_cent)); double eps1 = drand48(); double eps2 = drand48(); double cos_a = 1.0 - eps1 + eps1 * cos_a_max; double sin_a = sqrt(1.0 - cos_a * cos_a); double phi = 2.0 * M_PI * eps2; Vector l = su * cos(phi) * sin_a + sv * sin(phi) * sin_a + sw * cos_a; l = l.Normalized(); /* Shoot shadow ray, check if intersection is with light source */ double t; int id = 0; if (Intersect(Ray(hitpoint,l), t, id) && id==i) { double omega = 2 * M_PI * (1 - cos_a_max); /* Add diffusely reflected light from light source; note constant BRDF 1/PI */ e = e + cur_obj.emission * l.Dot(nl) * omega * M_1_PI; } } return obj.emission * E + obj.color.MultComponents(e) + col.MultComponents(Radiance(Ray(hitpoint, d), depth, 0)); } else return obj.emission + col.MultComponents(Radiance(Ray(hitpoint, d), depth, 0)); /* Return potential light emission, direct lighting, and indirect lighting (via recursive call for Monte-Carlo integration */ }
inline Radiance operator*(const Spectrum& s, const Radiance& r){ return Radiance(r.r * s.r, r.g * s.g, r.b * s.b); }
Radiance operator- (const Radiance& o) const {return Radiance(r-o.r,g-o.g,b-o.b);}
Radiance operator+ (const Radiance& o) const {return Radiance(r+o.r,g+o.g,b+o.b);}
Radiance operator/ (float q) const {return Radiance(r/q, g/q, b/q);}
glm::vec3 PathTracing::Radiance(const Ray& ray, Random *rnd, int depth) { Intersection isect; bool hit = this->scene_->Intersect(ray, &isect); if (!hit) { return BACKGROUND_COLOR; } const Primitive* p = isect.hitPrimitive_; const dvec3 orienting_normal = dot(isect.normal_, ray.direction_) < 0.0 ? isect.normal_ : -isect.normal_; // russian roulette // TODO: 確率しきい値は,色の反射率にする float russian_roulette_prob = glm::max(p->material_.color_.r, p->material_.color_.g, p->material_.color_.b); if (depth > MAX_DEPTH) { russian_roulette_prob *= pow(0.5, depth - MAX_DEPTH); } if (depth > MIN_DEPTH) { if (rnd->next() >= russian_roulette_prob) { return p->material_.emission_; } } else { russian_roulette_prob = 1.0f; // 強制的にMIN_DEPTH回は実行 } vec3 incoming_radiance; vec3 weight(1.0); switch (p->material_.reftype_) { case REFLECTION_TYPE_DIFFUSE: { // orienting_normalの方向を基準とした正規直交基底(w, u, v)を作る。この基底に対する半球内で次のレイを飛ばす。 glm::dvec3 w, u, v; w = orienting_normal; if (fabs(w.x) > Constants::EPS) // ベクトルwと直交するベクトルを作る。w.xが0に近い場合とそうでない場合とで使うベクトルを変える。 u = normalize(cross(dvec3(0.0, 1.0, 0.0), w)); else u = normalize(cross(dvec3(1.0, 0.0, 0.0), w)); v = cross(w, u); // コサイン項を使った重点的サンプリング const double r1 = 2 * M_PI * rnd->next(); const double r2 = rnd->next(), r2s = sqrt(r2); dvec3 dir = normalize(( u * cos(r1) * r2s + v * sin(r1) * r2s + w * sqrt(1.0 - r2))); incoming_radiance = Radiance(Ray(isect.position_, dir), rnd, depth+1); // レンダリング方程式に対するモンテカルロ積分を考えると、outgoing_radiance = weight * incoming_radiance。 // ここで、weight = (ρ/π) * cosθ / pdf(ω) / R になる。 // ρ/πは完全拡散面のBRDFでρは反射率、cosθはレンダリング方程式におけるコサイン項、pdf(ω)はサンプリング方向についての確率密度関数。 // Rはロシアンルーレットの確率。 // 今、コサイン項に比例した確率密度関数によるサンプリングを行っているため、pdf(ω) = cosθ/π // よって、weight = ρ/ R。 weight = p->material_.color_ / (float)russian_roulette_prob; } break; case REFLECTION_TYPE_SPECULAR: { // 完全鏡面なのでレイの反射方向は決定的。 // ロシアンルーレットの確率で除算するのは上と同じ。 incoming_radiance = Radiance(Ray(isect.position_, ray.direction_ - isect.normal_ * 2.0 * dot(isect.normal_, ray.direction_)), rnd, depth+1); weight = p->material_.color_ / russian_roulette_prob; } break; case REFLECTION_TYPE_REFRACTION: { const Ray reflection_ray = Ray(isect.position_, ray.direction_ - isect.normal_ * 2.0 * dot(isect.normal_, ray.direction_)); const bool into = dot(isect.normal_, orienting_normal) > 0.0; // レイがオブジェクトから出るのか、入るのか // Snellの法則 const double nc = 1.0; // 真空の屈折率 const double nt = p->material_.Ior_; // オブジェクトの屈折率 const double nnt = into ? nc / nt : nt / nc; const double ddn = dot(ray.direction_, orienting_normal); const double cos2t = 1.0 - nnt * nnt * (1.0 - ddn * ddn); if (cos2t < 0.0) { // 全反射 incoming_radiance = Radiance(reflection_ray, rnd, depth+1); weight = p->material_.color_ / (float)russian_roulette_prob; break; } // 屈折の方向 const Ray refraction_ray = Ray(isect.position_, normalize(ray.direction_ * nnt - isect.normal_ * (into ? 1.0 : -1.0) * (ddn * nnt + sqrt(cos2t)))); // SchlickによるFresnelの反射係数の近似を使う const double a = nt - nc, b = nt + nc; const double R0 = (a * a) / (b * b); const double c = 1.0 - (into ? -ddn : dot(refraction_ray.direction_, -1.0 * orienting_normal)); const double Re = R0 + (1.0 - R0) * pow(c, 5.0); // 反射方向の光が反射してray.dirの方向に運ぶ割合。同時に屈折方向の光が反射する方向に運ぶ割合。 const double nnt2 = pow(into ? nc / nt : nt / nc, 2.0); // レイの運ぶ放射輝度は屈折率の異なる物体間を移動するとき、屈折率の比の二乗の分だけ変化する。 const double Tr = (1.0 - Re) * nnt2; // 屈折方向の光が屈折してray.dirの方向に運ぶ割合 // 一定以上レイを追跡したら屈折と反射のどちらか一方を追跡する。(さもないと指数的にレイが増える) // ロシアンルーレットで決定する。 const double probability = 0.25 + 0.5 * Re; if (depth > 2) { if (rnd->next() < probability) { // 反射 incoming_radiance = Radiance(reflection_ray, rnd, depth+1) * (float)Re; weight = p->material_.color_ / (float)probability * russian_roulette_prob; } else { // 屈折 incoming_radiance = Radiance(refraction_ray, rnd, depth+1) * (float)Tr; weight = p->material_.color_ / (float)(1.0 - probability) * russian_roulette_prob; } } else { // 屈折と反射の両方を追跡 incoming_radiance = Radiance(reflection_ray, rnd, depth+1) * (float)Re + Radiance(refraction_ray, rnd, depth+1) * (float)Tr; weight = p->material_.color_ / (russian_roulette_prob); } } break; default: break; } return p->material_.emission_ + incoming_radiance * weight; }
// 屈折面 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); }