bool Manipulator::canManipulate(Ray3f ray,Box3f box,Mat4f* T) { //nothing to do if (!box.isValid()) return false; Vec3f size=box.size(); Mat4f Direct=(*T) * getTransformationToBox(box); Mat4f Inverse=Direct.invert(); // the ray is in world coordinate Vec3f P1=Inverse * (ray.origin ); Vec3f P2=Inverse * (ray.origin+ray.dir); // should be the unit bounding ball not the bounding box, but seems good enough (probably better) // is objects does not overlap too much! float epsilon=1e-4f; Box3f unit_box( Vec3f( size[0]?-1:-epsilon, size[1]?-1:-epsilon, size[2]?-1:-epsilon), Vec3f( size[0]?+1:+epsilon, size[1]?+1:+epsilon, size[2]?+1:+epsilon)); float tmin,tmax; return (Ray3f(P1,P2-P1).intersectBox(tmin,tmax,unit_box) && tmin>0); }
TEST(BBoxTest, intersectWithRay) { const BBox3f bounds(Vec3f(-12.0f, -3.0f, 4.0f), Vec3f( 8.0f, 9.0f, 8.0f)); Vec3f normal; float distance = bounds.intersectWithRay(Ray3f(Vec3f::Null, Vec3f::NegZ)); ASSERT_TRUE(Math::isnan(distance)); distance = bounds.intersectWithRay(Ray3f(Vec3f::Null, Vec3f::PosZ), &normal); ASSERT_FALSE(Math::isnan(distance)); ASSERT_FLOAT_EQ(4.0f, distance); ASSERT_VEC_EQ(Vec3f::NegZ, normal); const Vec3f origin = Vec3f(-10.0f, -7.0f, 14.0f); const Vec3f diff = Vec3f(-2.0f, 3.0f, 8.0f) - origin; const Vec3f dir = diff.normalized(); distance = bounds.intersectWithRay(Ray3f(origin, dir), &normal); ASSERT_FALSE(Math::isnan(distance)); ASSERT_FLOAT_EQ(diff.length(), distance); ASSERT_VEC_EQ(Vec3f::PosZ, normal); }
Color3f Li(const Scene *scene, Sampler *sampler, const Ray3f &ray) const { /* Find the surface that is visible in the requested direction */ Intersection its; //check if the ray intersects the scene if (!scene->rayIntersect(ray, its)) { //check if a distant disk light is set const Emitter* distantsDisk = scene->getDistantEmitter(); if(distantsDisk == nullptr ) return Color3f(0.0f); //sample the distant disk light Vector3f d = ray.d; return distantsDisk->sampleL(d); } //get the Number of lights from the scene const std::vector<Emitter *> lights = scene->getEmitters(); uint32_t nLights = lights.size(); Color3f tp(1.0f, 1.0f, 1.0f); Color3f L(0.0f, 0.0f, 0.0f); Ray3f pathRay(ray.o, ray.d); bool deltaFlag = true; while(true) { if (its.mesh->isEmitter() && deltaFlag) { const Emitter* areaLightEM = its.mesh->getEmitter(); const areaLight* aEM = static_cast<const areaLight *> (areaLightEM); L += tp * aEM->sampleL(-pathRay.d, its.shFrame.n, its); } //Light sampling //randomly select a lightsource uint32_t var = uint32_t(std::min(sampler->next1D()*nLights, float(nLights) - 1.0f)); //init the light color Color3f Li(0.0f, 0.0f, 0.0f); Color3f Ld(1.0f, 1.0f, 1.0f); //create a sample for the light const BSDF* curBSDF = its.mesh->getBSDF(); const Point2f lightSample = sampler->next2D(); VisibilityTester vis; Vector3f wo; float lightpdf; float bsdfpdf; Normal3f n = its.shFrame.n; deltaFlag = curBSDF->isDeltaBSDF(); //sample the light { Li = lights[var]->sampleL(its.p, Epsilon, lightSample , &wo, &lightpdf, &vis); lightpdf /= float(nLights); //check if the pdf of the sample is greater than 0 and if the color is not black if(lightpdf > 0 && Li.maxCoeff() != 0.0f) { //calculate the cosine term wi in my case the vector to the light float cosTerm = std::abs(n.dot(wo)); const BSDFQueryRecord queryEM = BSDFQueryRecord(its.toLocal(- pathRay.d), its.toLocal(wo), EMeasure::ESolidAngle, sampler); Color3f f = curBSDF->eval(queryEM); if(f.maxCoeff() > 0.0f && f.minCoeff() >= 0.0f && vis.Unoccluded(scene)) { bsdfpdf = curBSDF->pdf(queryEM); float weight = BalanceHeuristic(float(1), lightpdf, float(1), bsdfpdf); if(curBSDF->isDeltaBSDF()) weight = 1.0f; if(bsdfpdf > 0.0f) { Ld = (weight * f * Li * cosTerm) / lightpdf; L += tp * Ld; } else { //cout << "bsdfpdf = " << bsdfpdf << endl; //cout << "f = " << f << endl; } } } } //Material part BSDFQueryRecord queryMats = BSDFQueryRecord(its.toLocal(-pathRay.d), Vector3f(0.0f), EMeasure::ESolidAngle, sampler); Color3f fi = curBSDF->sample(queryMats, sampler->next2D()); bsdfpdf = curBSDF->pdf(queryMats); lightpdf = 0.0f; if(fi.maxCoeff() > 0.0f && fi.minCoeff() >= 0.0f) { if(bsdfpdf > 0.0f) { Ray3f shadowRay(its.p, its.toWorld(queryMats.wo)); Intersection lightIsect; if (scene->rayIntersect(shadowRay, lightIsect)) { if(lightIsect.mesh->isEmitter()){ const Emitter* areaLightEMcur = lightIsect.mesh->getEmitter(); const areaLight* aEMcur = static_cast<const areaLight *> (areaLightEMcur); Li = aEMcur->sampleL(-shadowRay.d, lightIsect.shFrame.n, lightIsect); lightpdf = aEMcur->pdf(its.p, (lightIsect.p - its.p).normalized(), lightIsect.p, Normal3f(lightIsect.shFrame.n)); } } else { const Emitter* distantsDisk = scene->getDistantEmitter(); if(distantsDisk != nullptr ) { //check if THIS is right! Li = distantsDisk->sampleL(lightIsect.toWorld(queryMats.wo)); lightpdf = distantsDisk->pdf(Point3f(0.0f), wo, Point3f(0.0f), Normal3f(0.0f)); } } lightpdf /= float(nLights); //calculate the weights float weight = BalanceHeuristic(float(1), bsdfpdf, float(1), lightpdf); //check if the lightcolor is not black if(Li.maxCoeff() > 0.0f && lightpdf > 0.0f ) { //wo in my case the vector to the light Ld = weight * Li * fi; L += tp * Ld; } } tp *= fi; } else { break; } wo = its.toWorld(queryMats.wo); pathRay = Ray3f(its.p, wo); if (!scene->rayIntersect(pathRay, its)) { const Emitter* distantsDisk = scene->getDistantEmitter(); if(distantsDisk != nullptr ) { //sample the distant disk light Vector3f d = pathRay.d; L += tp * distantsDisk->sampleL(d); } break; } float maxCoeff = tp.maxCoeff(); float q = std::min(0.99f, maxCoeff); if(q < sampler->next1D()){ break; } tp /= q; } return L; }
Ray3f Camera::getRay(Vector2f ndcPosition) const { checkTransforms(); Vector3f localPosition = ndcToLocalTransform.transform(Vector3f({ndcPosition[0], +1.f, ndcPosition[1]}), 1); return Ray3f(getPosition(), getLocalToWorldTransform().transform(localPosition, 1)); }
void preprocess(const Scene *scene) { /* Create a sample generator for the preprocess step */ Sampler *sampler = static_cast<Sampler *>( NoriObjectFactory::createInstance("independent", PropertyList())); Emitter* distantsDisk = scene->getDistantEmitter(); if(distantsDisk != nullptr ) { float lngstDir = scene->getBoundingBox().getLongestDirection(); distantsDisk->setMaxRadius(lngstDir); } /* Allocate memory for the photon map */ m_photonMap = std::unique_ptr<PhotonMap>(new PhotonMap()); m_photonMap->reserve(m_photonCount); /* Estimate a default photon radius */ if (m_photonRadius == 0) m_photonRadius = scene->getBoundingBox().getExtents().norm() / 500.0f; int storedPhotons = 0; const std::vector<Emitter *> lights = scene->getEmitters(); int nLights = lights.size(); Color3f tp(1.0f, 1.0f, 1.0f); cout << "Starting to create "<< m_photonCount << " photons!" << endl; int percentDone= 0; int onePercent = int(floor(m_photonCount / 100.0)); // create the expected number of photons while(storedPhotons < m_photonCount) { //uniformly sample 1 light (assuming that we only have area lights) int var = int(std::min(sampler->next1D()*nLights, float(nLights) - 1.0f)); const areaLight* curLight = static_cast<const areaLight *> (lights[var]); //sample a photon Photon curPhoton; Vector3f unQuantDir(0.0f,0.0f,0.0f); curLight->samplePhoton(sampler, curPhoton, 1, nLights, unQuantDir); Color3f alpha = curPhoton.getPower(); Color3f tp(1.0f, 1.0f, 1.0f); //trace the photon Intersection its; Ray3f photonRay(curPhoton.getPosition(), unQuantDir); m_shootedRays++; if (scene->rayIntersect(photonRay, its)) { while(true) { const BSDF* curBSDF = its.mesh->getBSDF(); if (curBSDF->isDiffuse()) { //store the photon m_photonMap->push_back(Photon( its.p /* Position */, -photonRay.d /* Direction*/, tp * alpha /* Power */ )); storedPhotons++; } if(!(storedPhotons < m_photonCount)) break; BSDFQueryRecord query = BSDFQueryRecord(its.toLocal(-photonRay.d), Vector3f(0.0f), EMeasure::ESolidAngle); Color3f fi = curBSDF->sample(query, sampler->next2D()); if(fi.maxCoeff() == 0.0f) break; tp *= fi; Vector3f wo = its.toWorld(query.wo); photonRay = Ray3f(its.p, wo); //ray escapes the scene if (!scene->rayIntersect(photonRay, its)) break; //stop critirium russian roulette float q = tp.maxCoeff(); if(q < sampler->next1D()) break; tp /= q; } } if(onePercent != 0) { if(storedPhotons % onePercent == 0){ int percent = int(floor(storedPhotons / onePercent)); if(percent % 10 == 0 && percentDone != percent){ percentDone = percent; cout << percent << "%" << endl; } } } } /* Build the photon map */ m_photonMap->build(); }
Color3f Li(const Scene *scene, Sampler *sampler, const Ray3f &ray) const { /* Find the surface that is visible in the requested direction */ Intersection its; //check if the ray intersects the scene if (!scene->rayIntersect(ray, its)) { return Color3f(0.0f); } Color3f tp(1.0f, 1.0f, 1.0f); Color3f L(0.0f, 0.0f, 0.0f); Ray3f pathRay(ray.o, ray.d); while(true) { //get the radiance of hitten object if (its.mesh->isEmitter() ) { const Emitter* areaLightEM = its.mesh->getEmitter(); const areaLight* aEM = static_cast<const areaLight *> (areaLightEM); L += tp * aEM->sampleL(-pathRay.d, its.shFrame.n, its); } //get the asigned BSDF const BSDF* curBSDF = its.mesh->getBSDF(); //transform to the local frame BSDFQueryRecord query = BSDFQueryRecord(its.toLocal(-pathRay.d), Vector3f(0.0f), EMeasure::ESolidAngle); //Normal3f n = its.shFrame.n; if(curBSDF->isDiffuse()) { std::vector<uint32_t> results; m_photonMap->search(its.p, m_photonRadius, results); Color3f Li(0.0f, 0.0f, 0.0f); int k = results.size(); //cout << k << endl; if(k > 0) { //cout << results.size() << " Photons found!" << endl; //get the power from all photons //for (uint32_t i : results) //const Photon &photonk = (*m_photonMap)[k-1]; Color3f Lindir(0.0f, 0.0f, 0.0f); for (int i = 0; i < k; ++i) { const Photon &photon = (*m_photonMap)[results[i]]; Vector3f wi = its.toLocal(photon.getDirection()); Vector3f wo = its.toLocal(its.shFrame.n); BSDFQueryRecord dummy = BSDFQueryRecord(wi, wo, EMeasure::ESolidAngle); Color3f f = curBSDF->eval(dummy); Lindir += (tp * f) * photon.getPower() / (M_PI * m_photonRadius * m_photonRadius); } Li += Lindir; //cout << "Li = " << Li.toString() << endl; if(Li.maxCoeff() > 0.0f) L += Li / m_shootedRays; } break; } //sample the BRDF Color3f fi = curBSDF->sample(query, sampler->next2D()); //check for black brdf if(fi.maxCoeff() > 0.0f) { tp *= fi; } else { //stop // hit a black brdf break; } Vector3f wo = its.toWorld(query.wo); pathRay = Ray3f(its.p, wo); //ray escapes the scene if (!scene->rayIntersect(pathRay, its)) break; //stop critirium russian roulette float maxCoeff = tp.maxCoeff(); float q = std::min(0.99f, maxCoeff); if(q < sampler->next1D()) break; tp /= q; } return L; }
Color3f Li(const Scene *scene, Sampler *sampler, const Ray3f &r) const { /* Find the surface that is visible in the requested direction */ Intersection its; Ray3f ray(r); if (!scene->rayIntersect(ray, its)) return Color3f(0.0f); Color3f radiance(0.0f); bool specularBounce = false; Color3f pathThroughput(1.0f); for ( int bounces = 0; ; ++bounces ) { const Luminaire* luminaire = its.mesh->getLuminaire(); if ((bounces == 0 || specularBounce) && luminaire != NULL) { Vector3f wo = (-ray.d).normalized(); Color3f emission = luminaire->le(its.p, its.shFrame.n, wo); radiance += pathThroughput*emission; } const Texture* texture = its.mesh->getTexture(); Color3f texel(1.0f); if ( texture ) { texel = texture->lookUp(its.uv.x(), its.uv.y()); } const BSDF* bsdf = its.mesh->getBSDF(); // sample illumination from lights, add to path contribution if (!bsdf->isSpecular()){ radiance += pathThroughput*UniformSampleAllLights(scene, ray, its, sampler, m_samplePolicy)*texel; } // sample bsdf to get new path direction BSDFQueryRecord bRec(its.toLocal((-ray.d)).normalized()); Color3f f = bsdf->sample(bRec, sampler->next2D() ); if (f.isZero() ) { // farther path no contribution break; } specularBounce = bsdf->isSpecular(); Vector3f d = its.toWorld(bRec.wo); f *= texel; pathThroughput *= f; ray = Ray3f(its.p, d ); // possibly termination if (bounces > kSampleDepth) { #if 0 float continueProbability = std::min( 0.5f, pathThroughput.y() ); if ( sampler->next1D() > continueProbability ) { break; } #else float continueProbability = std::max(f.x(), std::max(f.y(), f.z())); if ( sampler->next1D() > continueProbability ) { break; } #endif pathThroughput /= continueProbability; } if (bounces == m_maxDepth) { break; } // find next vertex of path if ( !scene->rayIntersect(ray, its) ) { break; } } return radiance; }