void PhotonMapping::TracePhoton(const Vec3f &position, const Vec3f &direction, const Vec3f &energy, int iter) { if(iter>args->num_bounces){ return; } Hit h = Hit(); Ray R = Ray(position, direction); bool intersect = raytracer->CastRay(R, h, false); if(!intersect){ return; } Material *m = h.getMaterial(); Vec3f normal = h.getNormal(); Vec3f point = R.pointAtParameter(h.getT()); Vec3f opDirec = direction; opDirec.Negate(); opDirec.Normalize(); Vec3f diffuse = m->getDiffuseColor(), reflec = m->getReflectiveColor(); double diffuseAnswer = diffuse.x()+diffuse.y()+diffuse.z(); double reflecAnswer = reflec.x()+reflec.y()+reflec.z(); double total = reflecAnswer+diffuseAnswer; diffuseAnswer /= total; reflecAnswer /= total; double seed = GLOBAL_mtrand.rand(); if(seed <= diffuseAnswer && seed >= 0){ Vec3f newEnergy = energy * diffuse; Vec3f newPosition = point; Vec3f newDirection = Vec3f(GLOBAL_mtrand.rand(),GLOBAL_mtrand.rand(),GLOBAL_mtrand.rand()); newDirection.Normalize(); Photon answer = Photon(point,opDirec,newEnergy,iter+1); kdtree->AddPhoton(answer); TracePhoton(newPosition, newDirection, newEnergy, iter+1); } else if(seed>diffuseAnswer && seed <= 1){ Vec3f newEnergy = energy * reflec; Vec3f newPosition = point; Vec3f newDirection = direction - 2 * direction.Dot3(normal) * normal; Photon answer = Photon(point,opDirec,newEnergy,iter+1); kdtree->AddPhoton(answer); TracePhoton(newPosition, newDirection, newEnergy, iter+1); } // ============================================== // ASSIGNMENT: IMPLEMENT RECURSIVE PHOTON TRACING // ============================================== // Trace the photon through the scene. At each diffuse or // reflective bounce, store the photon in the kd tree. // One optimization is to *not* store the first bounce, since that // direct light can be efficiently computed using classic ray // tracing. }
void PhotonMappingRenderer::GenericPhotonMapGeneration(PhotonKdtree& photonMap, int totalPhotons) { float totalLightIntensity = 0.f; size_t totalLights = storedScene->GetTotalLights(); for (size_t i = 0; i < totalLights; ++i) { const Light* currentLight = storedScene->GetLightObject(i); if (!currentLight) { continue; } totalLightIntensity = glm::length(currentLight->GetLightColor()); } // Shoot photons -- number of photons for light is proportional to the light's intensity relative to the total light intensity of the scene. for (size_t i = 0; i < totalLights; ++i) { const Light* currentLight = storedScene->GetLightObject(i); if (!currentLight) { continue; } const float proportion = glm::length(currentLight->GetLightColor()) / totalLightIntensity; const int totalPhotonsForLight = static_cast<const int>(proportion * totalPhotons); const glm::vec3 photonIntensity = currentLight->GetLightColor() / static_cast<float>(totalPhotonsForLight); for (int j = 0; j < totalPhotonsForLight; ++j) { Ray photonRay; std::vector<char> path; path.push_back('L'); currentLight->GenerateRandomPhotonRay(photonRay); TracePhoton(photonMap, &photonRay, photonIntensity, path, 1.f, maxPhotonBounces); } } }
void PhotonMapping::TracePhoton(const Vec3f &position, const Vec3f &direction, const Vec3f &energy, int iter) { // ============================================== // ASSIGNMENT: IMPLEMENT RECURSIVE PHOTON TRACING // ============================================== // Trace the photon through the scene. At each diffuse or // reflective bounce, store the photon in the kd tree. // One optimization is to *not* store the first bounce, since that // direct light can be efficiently computed using classic ray // tracing. //do ray cast Ray r(position,direction*(1/direction.Length())); Hit h; raytracer->CastRay(r,h,true); if (h.getT()>1000) return; MTRand mtrand; Vec3f refl = h.getMaterial()->getReflectiveColor(); Vec3f diff = h.getMaterial()->getDiffuseColor(); double ran=mtrand.rand(); if (iter==0) ran= mtrand.rand(refl.Length()+diff.Length()); //std::cout<<iter<<" "<<h.getT()<<" "<<refl.Length()+diff.Length()<<std::endl; //send reflective photon if (iter<args->num_bounces&&ran<=refl.Length()) TracePhoton(r.pointAtParameter(h.getT()),r.getDirection()-2*(r.getDirection().Dot3(h.getNormal()))*h.getNormal(),energy,iter+1); else if (iter<args->num_bounces&&ran<=refl.Length()+diff.Length()) TracePhoton(r.pointAtParameter(h.getT()),RandomDiffuseDirection(h.getNormal()),energy,iter+1); else { Photon p(position,direction,energy,iter); kdtree->AddPhoton(p); } }
TracePhoton RectangularLight::samplePhoton() { std::vector<Vec2f> samples(2); randomSampler->generateSamples(2, samples); Mat44f rot = Mat44f::I(); rot.rotateTo(Vec3f(0,0,1), Vec3f(0,-1,0)); Vec3f warpedPoint = Vec3f(size.x*samples[0].x + minPosition.x, minPosition.y, size.y*samples[0].y + minPosition.z); Vec3f warpedDirection = rot * cosineWarping->warp(samples[1]); TracePhoton photon = TracePhoton(warpedPoint, warpedDirection, power); return photon; }
void PhotonMapping::TracePhotons() { std::cout << "trace photons" << std::endl; // first, throw away any existing photons delete kdtree; // consruct a kdtree to store the photons BoundingBox *bb = mesh->getBoundingBox(); Vec3f min = bb->getMin(); Vec3f max = bb->getMax(); Vec3f diff = max-min; min -= 0.001*diff; max += 0.001*diff; kdtree = new KDTree(BoundingBox(min,max)); // photons emanate from the light sources const std::vector<Face*>& lights = mesh->getLights(); // compute the total area of the lights double total_lights_area = 0; for (unsigned int i = 0; i < lights.size(); i++) { total_lights_area += lights[i]->getArea(); } // shoot a constant number of photons per unit area of light source // (alternatively, this could be based on the total energy of each light) for (unsigned int i = 0; i < lights.size(); i++) { double my_area = lights[i]->getArea(); int num = args->num_photons_to_shoot * my_area / total_lights_area; // the initial energy for this photon Vec3f energy = my_area/double(num) * lights[i]->getMaterial()->getEmittedColor(); Vec3f normal = lights[i]->computeNormal(); for (int j = 0; j < num; j++) { Vec3f start = lights[i]->RandomPoint(); // the initial direction for this photon (for diffuse light sources) Vec3f direction = RandomDiffuseDirection(normal); TracePhoton(start,direction,energy,0); } } }
void GLCanvas::keyboard(unsigned char key, int x, int y) { args->raytracing_animation = false; switch (key) { // RAYTRACING STUFF case 'r': case 'R': { // animate raytracing of the scene args->gather_indirect=false; args->raytracing_animation = !args->raytracing_animation; if (args->raytracing_animation) { // time the animation rendering_time = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::system_clock::now().time_since_epoch()).count(); raytracing_skip = 1; //my_max(args->width,args->height) / 10; raytracing_x = 0; raytracing_y = 0; display(); // clear out any old rendering printf ("raytracing animation started, press 'R' to stop\n"); } else { printf ("raytracing animation stopped, press 'R' to start\n"); // Print time to render rendering_time = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::system_clock::now().time_since_epoch()).count() - rendering_time; std::cout << "Rendering completed in " << rendering_time/1000000.0 << " seconds." << std::endl; } break; } case 't': case 'T': { // visualize the ray tree for the pixel at the current mouse position int i = x; int j = glutGet(GLUT_WINDOW_HEIGHT)-y; RayTree::Activate(); raytracing_skip = 1; //TraceRay(i,j); //photon_mapping->setupVBOs(); //photon_mapping->initializeVBOs(); std::cout << "about to trace photons " << std::endl; TracePhoton(i,j); //photon_mapping->drawVBOs(); //RayTree::drawVBOs(); RayTree::Deactivate(); // redraw std::cout << "about to call setup VBOs after trace photons" << std::endl; RayTree::setupVBOs(); radiosity->setupVBOs(); photon_mapping->setupVBOs(); glutPostRedisplay(); break; } case 'l': case 'L': { // toggle photon rendering args->render_photons = !args->render_photons; glutPostRedisplay(); break; } case 'k': case 'K': { // toggle photon rendering args->render_kdtree = !args->render_kdtree; glutPostRedisplay(); break; } case 'p': case 'P': { // toggle photon rendering photon_mapping->TracePhotons(); // TODO: remove //photon_mapping->printKDTree(); photon_mapping->setupVBOs(); RayTree::setupVBOs(); glutPostRedisplay(); break; } case 'g': case 'G': { args->gather_indirect = true; args->raytracing_animation = !args->raytracing_animation; if (args->raytracing_animation) { // time the animation rendering_time = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::system_clock::now().time_since_epoch()).count(); raytracing_x = 0; raytracing_y = 0; raytracing_skip = 1;//my_max(args->width,args->height) / 10; display(); // clear out any old rendering printf ("photon mapping animation started, press 'G' to stop\n"); } else { printf ("photon mapping animation stopped, press 'G' to start\n"); // Print time to render rendering_time = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::system_clock::now().time_since_epoch()).count() - rendering_time; std::cout << "Rendering stopped after " << rendering_time/1000000.0 << " seconds." << std::endl; } break; } case 's': case 'S': { // subdivide the mesh for radiosity radiosity->Cleanup(); radiosity->getMesh()->Subdivision(); radiosity->Reset(); radiosity->setupVBOs(); glutPostRedisplay(); break; } // VISUALIZATIONS case 'w': case 'W': { // render wireframe mode args->wireframe = !args->wireframe; glutPostRedisplay(); break; } case 'v': case 'V': { std::cout << "RENDER_MATERIALS is the only mode\n"; break; } case 'q': case 'Q': { // quit delete GLCanvas::photon_mapping; delete GLCanvas::raytracer; delete GLCanvas::radiosity; delete GLCanvas::mesh; exit(0); break; } default: printf("UNKNOWN KEYBOARD INPUT '%c'\n", key); } }
void PhotonMappingRenderer::TracePhoton(PhotonKdtree& photonMap, Ray* photonRay, glm::vec3 lightIntensity, std::vector<char>& path, float currentIOR, int remainingBounces) { // check the recursive base case if (remainingBounces < 0) { return; } assert(photonRay); IntersectionState state(0, 0); state.currentIOR = currentIOR; // Trace the photon ray and test for intersections bool didIntersect = storedScene->Trace(photonRay, &state); if (!didIntersect) { return; } // Get the normal to the intersection glm::vec3 normal = state.ComputeNormal(); // Move the ray to the intersection point + a little offset glm::vec3 intersectionPoint = state.intersectionRay.GetRayPosition(state.intersectionT); photonRay->SetRayPosition(intersectionPoint + normal * SMALL_EPSILON); if (path.size() > 1) { // This photon did NOT come directly from the light, so // create and store a new Photon in the photon map Ray toLightRay(glm::vec3(photonRay->GetPosition()), -photonRay->GetRayDirection(), photonRay->GetMaxT()); Photon myPhoton; myPhoton.position = glm::vec3(photonRay->GetPosition()); myPhoton.intensity = lightIntensity; myPhoton.toLightRay = toLightRay; photonMap.insert(myPhoton); } // --------determine whether this photon is scattered or absorbed-------- const MeshObject* hitMeshObject = state.intersectedPrimitive->GetParentMeshObject(); const Material* hitMaterial = hitMeshObject->GetMaterial(); glm::vec3 diffuseResponse = hitMaterial->GetBaseDiffuseReflection(); // the probability of reflection is the max of the RGB components of the diffuse response float max = diffuseResponse.x; if (diffuseResponse.y > max) { max = diffuseResponse.y; } if (diffuseResponse.z > max) { max = diffuseResponse.z; } // 0 to RAND_MAX float rand_f = static_cast<float>(rand()); // 0 to 1 rand_f /= RAND_MAX; if (rand_f > max) { // photon absorbed return; } // ------scatter the photon------ // sample the hemisphere around the point in order to // pick a diffuse reflection direction // 0 to RAND_MAX float u1 = static_cast<float>(rand()); float u2 = static_cast<float>(rand()); // 0 to 1 u1 /= RAND_MAX; u2 /= RAND_MAX; float r = sqrt(u1); float theta = 2 * PI * u2; float x = r * cos(theta); float y = r * sin(theta); float z = sqrt(1 - u1); glm::vec3 newDir = glm::normalize(glm::vec3(x, y, z)); // ---transform from tangent space to world space--- // pick one of (1, 0, 0) (0, 1, 0) (0, 0, 1) as long as // dot product is not close to 1 (therefore not parallel) // to cross the normal with glm::vec3 candidate(1.f, 0.f, 0.f); float dot = glm::dot(normal, candidate); if (dot > 0.9) { candidate = glm::vec3(0.f, 1.f, 0.f); } glm::vec3 tangent = glm::cross(normal, candidate); glm::vec3 bitangent = glm::cross(normal, tangent); // construct 3x3 matrix where columns of the matrix are the // tangent, bitangent, and normal vectors (in that order) glm::mat3 mat(tangent, bitangent, normal); // multiply 3x3 matrix by the newDir vector in the previous step // to get the diffuse reflection ray direction in world space glm::vec3 worldDir = mat * newDir; photonRay->SetRayDirection(worldDir); // append to path vector, decrement remaining bounces path.push_back('L'); remainingBounces--; // recursive call TracePhoton(photonMap, photonRay, lightIntensity, path, currentIOR, remainingBounces); }