Vec3 PhotonMapRenderer::GlossyRay(const Vec3& w, float exponent, Random& rand) { real r1 = 2.f*(PI*rand.F32()); real r2 = rand.F32(); real cosTheta = powf(1.f-r2, 1.0f/(exponent+1.0f)); real sinTheta = sqrtf(1.0f - cosTheta*cosTheta); real rx = cosf(r1) * sinTheta; real ry = sinf(r1) * sinTheta; real rz = cosTheta; Vec3 u = ((fabs(w.x) > .1f ? Vec3(0.f, 1.f, 0.f) : Vec3(1.f, 0.f, 0.f)) ^ w).normalize(); // binormal Vec3 v = w ^ u; // tangent // ucosφsinθ + vsinφsinθ + wcosθ return (u*rx + v*ry + w*rz).normalize(); }
void PhotonMapRenderer::TracePhoton(const Ray& r, const Vec3& power, PathInfo pathInfo, Random& rand) { // max refl if (++pathInfo.depth > pPmConf_->maxPhotonBounce) { return; } HitRecord rec; rec.hitLit = false; if (!pScene_->Intersect(r, EPSILON, REAL_MAX, rec)) return; Vec3 x = r.o + r.d * rec.t; Vec3 n = rec.normal; Vec3 nl = n.dot(r.d) < 0.f ? n : n*-1.f; // 交点の法線, 裏面ヒット考慮 Vec3 color = rec.color; Refl_t refl = rec.pOldMaterial->refl; // Ideal DIFFUSE reflection if (refl == DIFF || refl == LIGHT) { // 光源表面はLambert面という事にしておく pathInfo.diffuseDepth++; bool direct = pathInfo.diffuseDepth == 1; pPhotonMap_->store(power.e, x.e, r.d.e, direct, pathInfo.lightNo); float ave_refl = (color.x + color.y + color.z) / 3.f; if (rand.F32() < ave_refl) { // 法線となす角のcosに比例する分布で反射方向を決める Vec3 d = Ray::CosRay(nl, rand); // lambertのBRDFかけなくていいのか? // cos分布でレイを発している(cosが掛けてある) // / probability cos // / lambert brdf PI real ave_refl_inv = 1.0f / ave_refl; Vec3 refPower = power.mult(color) * ave_refl_inv; TracePhoton(Ray(x,d), refPower, pathInfo, rand); } //fprintf(stderr, "p (%f %f %f) (%f %f %f) (%f %f %f)\n", power[0], power[1], power[2], pos[0], pos[1], pos[2], dir[0], dir[1], dir[2]); return; } // Ideal SPECULAR reflection else if (refl == SPEC) { pathInfo.specularDepth++; Ray refl(x, r.d - n * 2.f * n.dot(r.d)); TracePhoton(refl, power, pathInfo, rand); return; } else if (refl == PHONGMETAL) { pathInfo.glossyDepth++; Vec3 rdir = r.d - n * 2.f * n.dot(r.d); // reflected ray Vec3 d = GlossyRay(rdir, 10, rand); Vec3 mx = x + n*1e-4f; TracePhoton(Ray(mx, d), power, pathInfo, rand); return; } pathInfo.specularDepth++; // Ideal dielectric REFRACTION Ray reflRay(x, r.d - n * 2.f * n.dot(r.d)); bool into = n.dot(nl) > 0.f; // Ray from outside going in? real airRefrIdx = 1.f; real refrIdx = rec.pOldMaterial->refractiveIndex; real nnt = into ? airRefrIdx/refrIdx : refrIdx/airRefrIdx; real ddn = r.d.dot(nl); // レイと法線のcos real cos2t; // Total internal reflection if ((cos2t = 1.f - nnt * nnt * (1.f - ddn * ddn)) < 0.f) { TracePhoton(reflRay, power, pathInfo, rand); return; } // 屈折方向 Vec3 tdir = (r.d * nnt - n * ((into ? 1.f : -1.f) * (ddn * nnt + sqrtf(cos2t)))).normalize(); real a = refrIdx - airRefrIdx; real b = refrIdx + airRefrIdx; real R0 = a * a / (b * b); real c = 1.f - (into ? -ddn : tdir.dot(n)); real Re = R0 + (1.f - R0)*c*c*c*c*c; real P = Re; if (rand.F32() < P) TracePhoton(reflRay, power, pathInfo, rand); // 反射 else TracePhoton(Ray(x, tdir), power.mult(color), pathInfo, rand); // 屈折 return; }
Vec3 PhotonMapRenderer::Irradiance(const Ray &r, PathInfo pathInfo, Random& rand) { // max refl if (++pathInfo.depth > pPmRenConf_->maxRayBounce) { return Vec3(); } HitRecord rec; rec.hitLit = true; if (!pScene_->Intersect(r, EPSILON, REAL_MAX, rec)) return pConf_->bgColor; //const Shape& obj = *g_shapes[id]; // the hit object Vec3 x = r.o + r.d * rec.t; Vec3 n = rec.normal; Vec3 nl = n.dot(r.d) < 0.f ? n : n * -1.f; // 交点の法線 Vec3 color = rec.color; Refl_t refl = rec.pOldMaterial->refl; // 0.5にしたらカラーが反射率になってるから暗くなるだけ。IDEALでない反射は扱えない。カラーと混ぜるとかもない。 // Ideal DIFFUSE reflection if (refl == DIFF) { pathInfo.diffuseDepth++; Vec3 irrad; //fprintf(stderr, "irrad %f %f %f\r", irrad[0], irrad[1], irrad[2]); if (pPmRenConf_->finalGethering && pathInfo.diffuseDepth <= 1) { for (u32 i=0; i<pPmRenConf_->nFinalGetheringRays; i++) { real r1 = 2.f*(PI*rand.F32()); real r2 = rand.F32(); // => 1-cos^2θ = 1-sqrt(1-r_2)^2 = r_2 real r2s = sqrtf(r2); // => sinθ = sqrt(1-cos^2θ) = sqrt(r_2) Vec3 w = nl; // normal Vec3 u = ((fabs(w.x) > .1f ? Vec3(0.f, 1.f, 0.f) : Vec3(1.f, 0.f, 0.f)) ^ w).normalize(); // binormal Vec3 v = w ^ u; // tangent // ucosφsinθ + vsinφsinθ + wcosθ Vec3 d = (u*cosf(r1)*r2s + v*sinf(r1)*r2s + w*sqrtf(1-r2)).normalize(); PathInfo pathInfo2(pathInfo); irrad += Irradiance(Ray(x, d), pathInfo2, rand);// * nl.dot(d) / nl.dot(d); // 入射方向と法線のcosθ掛けるのとimportance samplingしたので確率密度関数cosθで割るのとで // 相殺するような。 } irrad /= (float)pPmRenConf_->nFinalGetheringRays; } else { pPhotonMap_->irradiance_estimate(irrad.e, x.e, nl, pPmConf_->estimateDist, pPmConf_->nEstimatePhotons); } return Vec3(irrad.x * color.x, irrad.y * color.y, irrad.z * color.z); } else if (refl == SPEC) { // Ideal SPECULAR reflection return Irradiance(Ray(x,r.d-n*2.f*n.dot(r.d)), pathInfo, rand); } else if (refl == PHONGMETAL) { // Imperfect SPECULAR reflection // 指数的に追跡回数が増えるのを防ぐ if (pathInfo.glossyDepth >= pPmRenConf_->nMaxGlossyBounce) { // Ideal SPECULAR reflectionで近似 return Irradiance(Ray(x,r.d-n*2.f*n.dot(r.d)), pathInfo, rand); } pathInfo.glossyDepth++; Vec3 irrad; const u32 nSamples = pPmRenConf_->nGlossyRays; for (u32 i = 0; i < nSamples; i++) { Vec3 rdir = r.d - n * 2.f * n.dot(r.d); // reflected ray rdir = GlossyRay(rdir, 10, rand); Vec3 mx = x + n*1e-4f; // 自己ヒットしないようにちょっと浮かす irrad += Irradiance(Ray(mx, rdir), pathInfo, rand); } return irrad / (float)nSamples; } else if (refl == REFR) { // Ideal dielectric REFRACTION Vec3 rdir = r.d - n * 2.f * n.dot(r.d); //Vec3 mx = x + rdir * EPSILON; // 自己ヒット抑止にレイ始点をちょっと進めてみる Ray reflRay(x, rdir); bool into = n.dot(nl) > 0.f; // Ray from outside going in? real airRefrIdx = 1.f; real refrIdx = rec.pOldMaterial->refractiveIndex; real nnt = into ? airRefrIdx/refrIdx : refrIdx/airRefrIdx; real ddn = r.d.dot(nl); // レイと法線のcos real cos2t; // Total internal reflection if ((cos2t = 1.f-nnt * nnt * (1.f - ddn * ddn)) < 0.f) return Irradiance(reflRay, pathInfo, rand); // 屈折方向 Vec3 tdir = (r.d * nnt - n * ((into ? 1.f : -1.f) * (ddn * nnt + sqrtf(cos2t)))).normalize(); //mx = x + rdir * EPSILON; // 自己ヒット抑止にレイ始点をちょっと進めてみる real a = refrIdx - airRefrIdx; real b = refrIdx + airRefrIdx; real R0 = a * a / (b * b); real c = 1.f - (into ? -ddn : tdir.dot(n)); real Re = R0 + (1.f - R0)*c*c*c*c*c; // レイの運ぶ放射輝度は屈折率の異なる物体間を移動するとき、屈折率の比の2乗の分だけ変化する // 屈折率が単位立体角あたりの値だから real nnt2 = nnt * nnt; real Tr = (1.f - Re) * nnt2; // 反射屈折両方トレース return Irradiance(reflRay, pathInfo, rand) * Re + Irradiance(Ray(x,tdir), pathInfo, rand).mult(color) * Tr; } // refl == LIGHT return ((AreaLightShape*)rec.pShape)->SelfIrradiance(); }