void Path::AdvancePath(PathRenderEngine *renderEngine, Sampler *sampler, const RayBuffer *rayBuffer, SampleBuffer *sampleBuffer) { SLGScene *scene = renderEngine->scene; //-------------------------------------------------------------------------- // Select the path ray hit //-------------------------------------------------------------------------- const RayHit *rayHit = NULL; switch (state) { case EYE_VERTEX: if (scene->volumeIntegrator) { rayHit = rayBuffer->GetRayHit(currentPathRayIndex); // Use Russian Roulette to check if I have to do participating media computation or not if (sample.GetLazyValue() <= scene->volumeIntegrator->GetRRProbability()) { Ray volumeRay(pathRay.o, pathRay.d, 0.f, rayHit->Miss() ? std::numeric_limits<float>::infinity() : rayHit->t); scene->volumeIntegrator->GenerateLiRays(scene, &sample, volumeRay, volumeComp); radiance += volumeComp->GetEmittedLight(); if (volumeComp->GetRayCount() > 0) { // Do the EYE_VERTEX_VOLUME_STEP state = EYE_VERTEX_VOLUME_STEP; eyeHit = *(rayBuffer->GetRayHit(currentPathRayIndex)); return; } } } else rayHit = rayBuffer->GetRayHit(currentPathRayIndex); break; case NEXT_VERTEX: rayHit = rayBuffer->GetRayHit(currentPathRayIndex); break; case EYE_VERTEX_VOLUME_STEP: // Add scattered light radiance += throughput * volumeComp->CollectResults(rayBuffer) / scene->volumeIntegrator->GetRRProbability(); rayHit = &eyeHit; break; case TRANSPARENT_SHADOW_RAYS_STEP: rayHit = &eyeHit; break; case TRANSPARENT_ONLY_SHADOW_RAYS_STEP: case ONLY_SHADOW_RAYS: // Nothing break; default: assert(false); break; } //-------------------------------------------------------------------------- // Finish direct light sampling //-------------------------------------------------------------------------- if (((state == NEXT_VERTEX) || (state == ONLY_SHADOW_RAYS) || (state == TRANSPARENT_SHADOW_RAYS_STEP) || (state == TRANSPARENT_ONLY_SHADOW_RAYS_STEP)) && (tracedShadowRayCount > 0)) { unsigned int leftShadowRaysToTrace = 0; for (unsigned int i = 0; i < tracedShadowRayCount; ++i) { const RayHit *shadowRayHit = rayBuffer->GetRayHit(currentShadowRayIndex[i]); if (shadowRayHit->Miss()) { // Nothing was hit, light is visible radiance += lightColor[i] / lightPdf[i]; } else { // Something was hit check if it is transparent const unsigned int currentShadowTriangleIndex = shadowRayHit->index; const unsigned int currentShadowMeshIndex = scene->dataSet->GetMeshID(currentShadowTriangleIndex); Material *triMat = scene->objectMaterials[currentShadowMeshIndex]; if (triMat->IsShadowTransparent()) { // It is shadow transparent, I need to continue to trace the ray shadowRay[leftShadowRaysToTrace] = Ray( shadowRay[i](shadowRayHit->t), shadowRay[i].d, shadowRay[i].mint, shadowRay[i].maxt - shadowRayHit->t); lightColor[leftShadowRaysToTrace] = lightColor[i] * triMat->GetSahdowTransparency(); lightPdf[leftShadowRaysToTrace] = lightPdf[i]; leftShadowRaysToTrace++; } else { // Check if there is a texture with alpha TexMapInstance *tm = scene->objectTexMaps[currentShadowMeshIndex]; if (tm) { const TextureMap *map = tm->GetTexMap(); if (map->HasAlpha()) { const ExtMesh *mesh = scene->objects[currentShadowMeshIndex]; const UV triUV = mesh->InterpolateTriUV(scene->dataSet->GetMeshTriangleID(currentShadowTriangleIndex), shadowRayHit->b1, shadowRayHit->b2); const float alpha = map->GetAlpha(triUV); if (alpha < 1.f) { // It is shadow transparent, I need to continue to trace the ray shadowRay[leftShadowRaysToTrace] = Ray( shadowRay[i](shadowRayHit->t), shadowRay[i].d, shadowRay[i].mint, shadowRay[i].maxt - shadowRayHit->t); lightColor[leftShadowRaysToTrace] = lightColor[i] * (1.f - alpha); lightPdf[leftShadowRaysToTrace] = lightPdf[i]; leftShadowRaysToTrace++; } } } } } } if (leftShadowRaysToTrace > 0) { tracedShadowRayCount = leftShadowRaysToTrace; if ((state == ONLY_SHADOW_RAYS) || (state == TRANSPARENT_ONLY_SHADOW_RAYS_STEP)) state = TRANSPARENT_ONLY_SHADOW_RAYS_STEP; else { eyeHit = *rayHit; state = TRANSPARENT_SHADOW_RAYS_STEP; } return; } } //-------------------------------------------------------------------------- // Calculate next step //-------------------------------------------------------------------------- depth++; const bool missed = rayHit ? rayHit->Miss() : false; if (missed || (state == ONLY_SHADOW_RAYS) || (state == TRANSPARENT_ONLY_SHADOW_RAYS_STEP) || (depth >= renderEngine->maxPathDepth)) { if (missed && scene->infiniteLight && (scene->useInfiniteLightBruteForce || specularBounce)) { // Add the light emitted by the infinite light radiance += scene->infiniteLight->Le(pathRay.d) * throughput; } // Hit nothing/only shadow rays/maxdepth, terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); return; } // Something was hit const unsigned int currentTriangleIndex = rayHit->index; const unsigned int currentMeshIndex = scene->dataSet->GetMeshID(currentTriangleIndex); // Get the triangle const ExtMesh *mesh = scene->objects[currentMeshIndex]; const unsigned int triIndex = scene->dataSet->GetMeshTriangleID(currentTriangleIndex); // Get the material const Material *triMat = scene->objectMaterials[currentMeshIndex]; // Check if it is a light source if (triMat->IsLightSource()) { if (specularBounce) { // Only TriangleLight can be directly hit const LightMaterial *mLight = (LightMaterial *) triMat; Spectrum Le = mLight->Le(mesh, triIndex, -pathRay.d); radiance += Le * throughput; } // Terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); return; } //-------------------------------------------------------------------------- // Build the shadow rays (if required) //-------------------------------------------------------------------------- // Interpolate face normal Normal N = mesh->InterpolateTriNormal(triIndex, rayHit->b1, rayHit->b2); const SurfaceMaterial *triSurfMat = (SurfaceMaterial *) triMat; const Point hitPoint = pathRay(rayHit->t); const Vector wo = -pathRay.d; Spectrum surfaceColor; if (mesh->HasColors()) surfaceColor = mesh->InterpolateTriColor(triIndex, rayHit->b1, rayHit->b2); else surfaceColor = Spectrum(1.f, 1.f, 1.f); // Check if I have to apply texture mapping or normal mapping TexMapInstance *tm = scene->objectTexMaps[currentMeshIndex]; BumpMapInstance *bm = scene->objectBumpMaps[currentMeshIndex]; NormalMapInstance *nm = scene->objectNormalMaps[currentMeshIndex]; if (tm || bm || nm) { // Interpolate UV coordinates if required const UV triUV = mesh->InterpolateTriUV(triIndex, rayHit->b1, rayHit->b2); // Check if there is an assigned texture map if (tm) { const TextureMap *map = tm->GetTexMap(); // Apply texture mapping surfaceColor *= map->GetColor(triUV); // Check if the texture map has an alpha channel if (map->HasAlpha()) { const float alpha = map->GetAlpha(triUV); if ((alpha == 0.0f) || ((alpha < 1.f) && (sample.GetLazyValue() > alpha))) { pathRay = Ray(pathRay(rayHit->t), pathRay.d, RAY_EPSILON, pathRay.maxt - rayHit->t); state = NEXT_VERTEX; tracedShadowRayCount = 0; return; } } } // Check if there is an assigned bump/normal map if (bm || nm) { if (nm) { // Apply normal mapping const Spectrum color = nm->GetTexMap()->GetColor(triUV); const float x = 2.f * (color.r - 0.5f); const float y = 2.f * (color.g - 0.5f); const float z = 2.f * (color.b - 0.5f); Vector v1, v2; CoordinateSystem(Vector(N), &v1, &v2); N = Normalize(Normal( v1.x * x + v2.x * y + N.x * z, v1.y * x + v2.y * y + N.y * z, v1.z * x + v2.z * y + N.z * z)); } if (bm) { // Apply bump mapping const TextureMap *map = bm->GetTexMap(); const UV &dudv = map->GetDuDv(); const float b0 = map->GetColor(triUV).Filter(); const UV uvdu(triUV.u + dudv.u, triUV.v); const float bu = map->GetColor(uvdu).Filter(); const UV uvdv(triUV.u, triUV.v + dudv.v); const float bv = map->GetColor(uvdv).Filter(); const float scale = bm->GetScale(); const Vector bump(scale * (bu - b0), scale * (bv - b0), 1.f); Vector v1, v2; CoordinateSystem(Vector(N), &v1, &v2); N = Normalize(Normal( v1.x * bump.x + v2.x * bump.y + N.x * bump.z, v1.y * bump.x + v2.y * bump.y + N.y * bump.z, v1.z * bump.x + v2.z * bump.y + N.z * bump.z)); } } } // Flip the normal if required Normal shadeN = (Dot(pathRay.d, N) > 0.f) ? -N : N; tracedShadowRayCount = 0; const bool skipInfiniteLight = !renderEngine->onlySampleSpecular; const unsigned int lightCount = scene->GetLightCount(skipInfiniteLight); if (triSurfMat->IsDiffuse() && (scene->GetLightCount(skipInfiniteLight) > 0)) { // Direct light sampling: trace shadow rays switch (renderEngine->lightStrategy) { case ALL_UNIFORM: { // ALL UNIFORM direct sampling light strategy const Spectrum lightThroughtput = throughput * surfaceColor; for (unsigned int j = 0; j < lightCount; ++j) { // Select the light to sample const LightSource *light = scene->GetLight(j, skipInfiniteLight); for (unsigned int i = 0; i < renderEngine->shadowRayCount; ++i) { // Select a point on the light surface lightColor[tracedShadowRayCount] = light->Sample_L( scene, hitPoint, &shadeN, sample.GetLazyValue(), sample.GetLazyValue(), sample.GetLazyValue(), &lightPdf[tracedShadowRayCount], &shadowRay[tracedShadowRayCount]); // Scale light pdf for ALL_UNIFORM strategy lightPdf[tracedShadowRayCount] *= renderEngine->shadowRayCount; if (lightPdf[tracedShadowRayCount] <= 0.0f) continue; const Vector lwi = shadowRay[tracedShadowRayCount].d; lightColor[tracedShadowRayCount] *= lightThroughtput * Dot(shadeN, lwi) * triSurfMat->f(wo, lwi, shadeN); if (!lightColor[tracedShadowRayCount].Black()) tracedShadowRayCount++; } } break; } case ONE_UNIFORM: { // ONE UNIFORM direct sampling light strategy const Spectrum lightThroughtput = throughput * surfaceColor; for (unsigned int i = 0; i < renderEngine->shadowRayCount; ++i) { // Select the light to sample float lightStrategyPdf; const LightSource *light = scene->SampleAllLights(sample.GetLazyValue(), &lightStrategyPdf, skipInfiniteLight); // Select a point on the light surface lightColor[tracedShadowRayCount] = light->Sample_L( scene, hitPoint, &shadeN, sample.GetLazyValue(), sample.GetLazyValue(), sample.GetLazyValue(), &lightPdf[tracedShadowRayCount], &shadowRay[tracedShadowRayCount]); if (lightPdf[tracedShadowRayCount] <= 0.f) continue; const Vector lwi = shadowRay[tracedShadowRayCount].d; lightColor[tracedShadowRayCount] *= lightThroughtput * Dot(shadeN, lwi) * triSurfMat->f(wo, lwi, shadeN); if (!lightColor[tracedShadowRayCount].Black()) { lightPdf[tracedShadowRayCount] *= lightStrategyPdf * renderEngine->shadowRayCount; tracedShadowRayCount++; } } break; } default: assert(false); } } //-------------------------------------------------------------------------- // Build the next vertex path ray //-------------------------------------------------------------------------- float fPdf; Vector wi; const Spectrum f = triSurfMat->Sample_f(wo, &wi, N, shadeN, sample.GetLazyValue(), sample.GetLazyValue(), sample.GetLazyValue(), renderEngine->onlySampleSpecular, &fPdf, specularBounce) * surfaceColor; if ((fPdf <= 0.f) || f.Black()) { if (tracedShadowRayCount > 0) state = ONLY_SHADOW_RAYS; else { // Terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); } return; } throughput *= f / fPdf; if (depth > renderEngine->rrDepth) { // Russian Roulette switch (renderEngine->rrStrategy) { case PROBABILITY: { if (renderEngine->rrProb >= sample.GetLazyValue()) throughput /= renderEngine->rrProb; else { // Check if terminate the path or I have still to trace shadow rays if (tracedShadowRayCount > 0) state = ONLY_SHADOW_RAYS; else { // Terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); } return; } break; } case IMPORTANCE: { const float prob = Max(throughput.Filter(), renderEngine->rrImportanceCap); if (prob >= sample.GetLazyValue()) throughput /= prob; else { // Check if terminate the path or I have still to trace shadow rays if (tracedShadowRayCount > 0) state = ONLY_SHADOW_RAYS; else { // Terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); } return; } break; } default: assert(false); } } pathRay.o = hitPoint; pathRay.d = wi; state = NEXT_VERTEX; }
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; }
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; }