void UniformResamplingRecursiveMISBPTRenderer::connectLightVerticesToSensor(uint32_t threadID, uint32_t tileID, const Vec4u& viewport) const { auto rcpPathCount = 1.f / m_nLightPathCount; auto mis = [&](float v) { return Mis(v); }; for(const auto& directImportanceSample: m_DirectImportanceSampleTilePartitionning[tileID]) { auto pLightVertex = &m_LightPathBuffer[directImportanceSample.m_nLightVertexIndex]; auto totalDepth = 1 + pLightVertex->m_nDepth; if(!acceptPathDepth(totalDepth) || totalDepth > m_nMaxDepth) { continue; } auto pixel = directImportanceSample.m_Pixel; auto pixelID = BnZ::getPixelIndex(pixel, getFramebufferSize()); PixelSensor sensor(getSensor(), pixel, getFramebufferSize()); auto contrib = rcpPathCount * connectVertices(*pLightVertex, SensorVertex(&sensor, directImportanceSample.m_LensSample), getScene(), m_nLightPathCount, mis); if(isInvalidMeasurementEstimate(contrib)) { reportInvalidContrib(threadID, tileID, pixelID, [&]() { debugLog() << "s = " << (pLightVertex->m_nDepth + 1) << ", t = " << 1 << std::endl; }); } accumulate(FINAL_RENDER, pixelID, Vec4f(contrib, 0.f)); accumulate(FINAL_RENDER_DEPTH1 + totalDepth - 1u, pixelID, Vec4f(contrib, 0.f)); auto strategyOffset = computeBPTStrategyOffset(totalDepth + 1, pLightVertex->m_nDepth + 1); accumulate(BPT_STRATEGY_s0_t2 + strategyOffset, pixelID, Vec4f(contrib, 0)); } }
void UniformResamplingRecursiveMISBPTRenderer::processSample(uint32_t threadID, uint32_t tileID, uint32_t pixelID, uint32_t sampleID, uint32_t x, uint32_t y) const { auto mis = [&](float v) { return Mis(v); }; ThreadRNG rng(*this, threadID); PixelSensor pixelSensor(getSensor(), Vec2u(x, y), getFramebufferSize()); auto pixelSample = getPixelSample(threadID, sampleID); auto sensorSample = getFloat2(threadID); auto fImportanceScale = 1.f / getSppCount(); BDPTPathVertex eyeVertex(getScene(), pixelSensor, sensorSample, pixelSample, m_nLightPathCount, mis); if(eyeVertex.m_Power == zero<Vec3f>() || !eyeVertex.m_fPathPdf) { return; } // Sample the light path to connect with the eye path auto lightPathIdx = clamp(std::size_t(getFloat(threadID) * m_nLightPathCount), std::size_t(0), m_nLightPathCount - 1); auto pLightPath = m_LightPathBuffer.getSlicePtr(lightPathIdx); auto maxEyePathDepth = getMaxEyePathDepth(); auto maxLightPathDepth = getMaxLightPathDepth(); auto extendEyePath = [&]() -> bool { return eyeVertex.m_nDepth < maxEyePathDepth && eyeVertex.extend(eyeVertex, getScene(), getSppCount(), false, rng, mis); }; // Iterate over an eye path do { // Intersection with light source auto totalLength = eyeVertex.m_nDepth; if(acceptPathDepth(totalLength) && totalLength <= m_nMaxDepth) { auto contrib = fImportanceScale * computeEmittedRadiance(eyeVertex, getScene(), m_LightSampler, getSppCount(), mis); if(isInvalidMeasurementEstimate(contrib)) { reportInvalidContrib(threadID, tileID, pixelID, [&]() { debugLog() << "s = " << 0 << ", t = " << (eyeVertex.m_nDepth + 1) << std::endl; }); } accumulate(FINAL_RENDER, pixelID, Vec4f(contrib, 0)); accumulate(FINAL_RENDER_DEPTH1 + totalLength - 1u, pixelID, Vec4f(contrib, 0)); auto strategyOffset = computeBPTStrategyOffset(totalLength + 1, 0u); accumulate(BPT_STRATEGY_s0_t2 + strategyOffset, pixelID, Vec4f(contrib, 0)); } // Connections if(eyeVertex.m_Intersection) { // Direct illumination auto totalLength = eyeVertex.m_nDepth + 1; if(acceptPathDepth(totalLength) && totalLength <= m_nMaxDepth) { float lightPdf; auto pLight = m_LightSampler.sample(getScene(), getFloat(threadID), lightPdf); auto s2D = getFloat2(threadID); auto contrib = fImportanceScale * connectVertices(eyeVertex, EmissionVertex(pLight, lightPdf, s2D), getScene(), getSppCount(), mis); if(isInvalidMeasurementEstimate(contrib)) { reportInvalidContrib(threadID, tileID, pixelID, [&]() { debugLog() << "s = " << 1 << ", t = " << (eyeVertex.m_nDepth + 1) << std::endl; }); } accumulate(FINAL_RENDER, pixelID, Vec4f(contrib, 0)); accumulate(FINAL_RENDER_DEPTH1 + totalLength - 1u, pixelID, Vec4f(contrib, 0)); auto strategyOffset = computeBPTStrategyOffset(totalLength + 1, 1); accumulate(BPT_STRATEGY_s0_t2 + strategyOffset, pixelID, Vec4f(contrib, 0)); } // Connection with each light vertex for(auto j = 0u; j < maxLightPathDepth; ++j) { auto pLightVertex = pLightPath + j; auto totalLength = eyeVertex.m_nDepth + pLightVertex->m_nDepth + 1; if(pLightVertex->m_fPathPdf > 0.f && acceptPathDepth(totalLength) && totalLength <= m_nMaxDepth) { auto contrib = fImportanceScale * connectVertices(eyeVertex, *pLightVertex, getScene(), getSppCount(), mis); if(isInvalidMeasurementEstimate(contrib)) { reportInvalidContrib(threadID, tileID, pixelID, [&]() { debugLog() << "s = " << (pLightVertex->m_nDepth + 1) << ", t = " << (eyeVertex.m_nDepth + 1) << std::endl; }); } accumulate(FINAL_RENDER, pixelID, Vec4f(contrib, 0)); accumulate(FINAL_RENDER_DEPTH1 + totalLength - 1u, pixelID, Vec4f(contrib, 0)); auto strategyOffset = computeBPTStrategyOffset(totalLength + 1, pLightVertex->m_nDepth + 1); accumulate(BPT_STRATEGY_s0_t2 + strategyOffset, pixelID, Vec4f(contrib, 0)); } } } } while(extendEyePath()); }
void BidirPathTracing::runIteration(int iter) { lightPathNum = height * width; cameraPathNum = lightPathNum; lightStateIndex.resize(lightPathNum); memset(&lightStateIndex[0] , 0 , lightStateIndex.size() * sizeof(int)); lightStates.reserve(lightPathNum); lightStates.clear(); cameraStates.reserve(cameraPathNum); cameraStates.clear(); // generating light paths for (int pathIndex = 0; pathIndex < lightPathNum; pathIndex++) { BidirPathState lightState; bool deltaLight; generateLightSample(lightState); int s = 1; for (;; lightState.pathLength++ , s++) { Ray ray(lightState.origin + lightState.dir * EPS , lightState.dir); Intersection inter; if (scene.intersect(ray , inter) == NULL) break; Vector3 hitPos = inter.p; BSDF bsdf(-ray.dir , inter , scene); if (!bsdf.isValid()) break; lightState.pos = hitPos; lightState.bsdf = bsdf; if (lightState.pathLength > 1 || lightState.isFiniteLight) { lightState.dVCM *= mis(SQR(inter.t)); } lightState.dVCM /= mis(std::abs(bsdf.cosWi())); lightState.dVC /= mis(std::abs(bsdf.cosWi())); if (!bsdf.isDelta) lightStates.push_back(lightState); // connect to camera if (!bsdf.isDelta) { if (lightState.pathLength + 1 >= minPathLength && lightState.pathLength + 1 == controlLength) { Vector3 imagePos = scene.camera.worldToRaster.tPoint(hitPos); if (scene.camera.checkRaster(imagePos.x , imagePos.y)) { Color3 res = connectToCamera(lightState , hitPos , bsdf); // weight //Real weight = 1.f / (lightState.pathLength + 1 - lightState.specularVertexNum); film->addColor((int)imagePos.x , (int)imagePos.y , res); } } } if (lightState.pathLength + 2 > maxPathLength) break; if (!sampleScattering(bsdf , hitPos , lightState)) break; } lightStateIndex[pathIndex] = (int)lightStates.size(); } // generating camera paths for (int index = 0; index < cameraPathNum; index++) { int pathIndex = index % (height * width); if (pathIndex == 111 * 512 + 364) { int flag = 1; } BidirPathState cameraState; Vector3 screenSample = generateCameraSample(pathIndex , cameraState); Color3 color(0); for (;; cameraState.pathLength++) { Ray ray(cameraState.origin + cameraState.dir * EPS , cameraState.dir); Intersection inter; if (scene.intersect(ray , inter) == NULL) { /* if (scene.background != NULL) { if (cameraState.pathLength >= minPathLength) { // weight Real weight = 1.f / (cameraState.pathLength - cameraState.specularVertexNum); color = color + (cameraState.throughput | getLightRadiance(scene.background , cameraState , Vector3(0) , ray.dir)) * weight; } } */ break; } Vector3 hitPos = inter.p; BSDF bsdf(-ray.dir , inter , scene); if (!bsdf.isValid()) break; cameraState.dVCM *= mis(SQR(inter.t)); cameraState.dVCM /= mis(std::abs(bsdf.cosWi())); cameraState.dVC /= mis(std::abs(bsdf.cosWi())); if (inter.matId < 0) { AbstractLight *light = scene.lights[-inter.matId - 1]; if (cameraState.pathLength >= minPathLength && cameraState.pathLength == controlLength) { // weight //Real weight = 1.f / (cameraState.pathLength - cameraState.specularVertexNum); color = color + (cameraState.throughput | getLightRadiance(light , cameraState , hitPos , ray.dir)); } break; } if (cameraState.pathLength >= maxPathLength) break; // vertex connection: connect to light source if (!bsdf.isDelta) { if (cameraState.pathLength + 1 >= minPathLength && cameraState.pathLength + 1 == controlLength) { // weight Real weight = 1.f / (cameraState.pathLength + 1.f - cameraState.specularVertexNum); color = color + (cameraState.throughput | getDirectIllumination(cameraState , hitPos , bsdf)) * weight; } } // vertex connection: connect to light vertices if (!bsdf.isDelta) { int st , ed; if (pathIndex == 0) st = 0; else st = lightStateIndex[pathIndex - 1]; ed = lightStateIndex[pathIndex]; for (int i = st; i < ed; i++) { BidirPathState& lightState = lightStates[i]; if (lightState.pathLength + 1 + cameraState.pathLength < minPathLength) continue; if (lightState.pathLength + 1 + cameraState.pathLength > maxPathLength) break; if (lightState.bsdf.isDelta) continue; Color3 tmp = connectVertices(lightState , bsdf , hitPos , cameraState); // weight Real weight = 1.f / (lightState.pathLength + 1.f + cameraState.pathLength - lightState.specularVertexNum - cameraState.specularVertexNum); if (lightState.pathLength + 1 + cameraState.pathLength == controlLength) color = color + (cameraState.throughput | lightState.throughput | tmp) * weight; } } if (!sampleScattering(bsdf , hitPos , cameraState)) break; } film->addColor((int)screenSample.x , (int)screenSample.y , color); } }