// 鏡面反射 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); }
Color PathTracer::Radiance(const Scene &scene, const Ray &ray, Random &rnd, const int depth) { Scene::IntersectionInformation intersect; m_checkIntersectionCount++; bool intersected = scene.CheckIntersection(ray, intersect); Vector3 normal; if (intersected) { normal = intersect.hit.normal.dot(ray.dir) < 0.0 ? intersect.hit.normal : intersect.hit.normal * -1.0; } Color income;// = DirectRadiance(scene, ray, rnd, depth, intersected, intersect, normal); income += Radiance_internal(scene, ray, rnd, depth, intersected, intersect, normal); return income; }
// 屈折面 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); }
// 直接光による Radiance の評価 Color PathTracer::DirectRadiance_Lambert(const Scene &scene, const Ray &ray, Random &rnd, const int depth, const bool intersected, Scene::IntersectionInformation &intersect, const Vector3 &normal) { assert(intersected); // ライトに当たっていたら無視 if (dynamic_cast<LightBase *>(intersect.object) != nullptr) { return Color(0, 0, 0); } const vector<LightBase *> &lights = scene.GetLights(); if (lights.size() == 0) return scene.Background(); // pick a random light according to light power double totalPower = 0.0; vector<double> eachLightProbability, accumulatedProbability; for (size_t i = 0; i < lights.size(); i++) { double power = lights[i]->TotalPower(); eachLightProbability.push_back(power); accumulatedProbability.push_back(totalPower + power); totalPower += power; } // 確率へ正規化 for (size_t i = 0; i < lights.size(); i++) { eachLightProbability[i] /= totalPower; accumulatedProbability[i] /= totalPower; } static const int NumberOfLightSamples = 1; Color income; for (int lightCount = 0; lightCount < NumberOfLightSamples; lightCount++) { double next = rnd.nextDouble(); int index = 0; for (size_t i = 0; i < lights.size(); i++) { if (next <= accumulatedProbability[i]) { index = i; break; } } const LightBase *selectedLight = lights[index]; // pick a one point Vector3 point, light_normal; double pdf = 0.0; //selectedLight->SampleOnePoint(point, light_normal, pdf, rnd); if (!selectedLight->SampleOnePointWithTargetPoint(point, light_normal, pdf, intersect.hit.position, rnd)) { continue; } Vector3 dir((point - intersect.hit.position)); dir.normalize(); double cos_shita = dir.dot(normal); double light_cos_shita = -dir.dot(light_normal); if (cos_shita < 0 || light_cos_shita < 0) { // cannot reach to the light continue; } // check visibility Scene::IntersectionInformation hit; if (scene.CheckIntersection(Ray(intersect.hit.position, dir), hit)) { if (dynamic_cast<LightBase *>(hit.object) == selectedLight) { // visible // BRDF = color/PI double G = cos_shita * light_cos_shita / (hit.hit.distance * hit.hit.distance); Vector3 reflect_rate(intersect.texturedHitpointColor / PI * G / (pdf * eachLightProbability[index])); income.x += reflect_rate.x * hit.object->material.emission.x; income.y += reflect_rate.y * hit.object->material.emission.y; income.z += reflect_rate.z * hit.object->material.emission.z; // direct_illum = 1/N*ΣL_e*BRDF*G*V/pdf(light) m_hitToLightCount++; } } m_omittedRayCount++; } return income / NumberOfLightSamples; }