SampledSpectrum PathTracingRenderer::Job::contribution(const Scene &scene, const WavelengthSamples &initWLs, const Ray &initRay, IndependentLightPathSampler &pathSampler, ArenaAllocator &mem) const { WavelengthSamples wls = initWLs; Ray ray = initRay; SurfacePoint surfPt; SampledSpectrum alpha = SampledSpectrum::One; float initY = alpha.importance(wls.selectedLambda); SampledSpectrumSum sp(SampledSpectrum::Zero); uint32_t pathLength = 0; Intersection isect; if (!scene.intersect(ray, &isect)) return SampledSpectrum::Zero; isect.getSurfacePoint(&surfPt); Vector3D dirOut_sn = surfPt.shadingFrame.toLocal(-ray.dir); if (surfPt.isEmitting()) { EDF* edf = surfPt.createEDF(wls, mem); SampledSpectrum Le = surfPt.emittance(wls) * edf->evaluate(EDFQuery(), dirOut_sn); sp += alpha * Le; } if (surfPt.atInfinity) return sp; while (true) { ++pathLength; if (pathLength >= 100) break; Normal3D gNorm_sn = surfPt.shadingFrame.toLocal(surfPt.gNormal); BSDF* bsdf = surfPt.createBSDF(wls, mem); BSDFQuery fsQuery(dirOut_sn, gNorm_sn, wls.selectedLambda); // Next Event Estimation (explicit light sampling) if (bsdf->hasNonDelta()) { float lightProb; Light light; scene.selectLight(pathSampler.getLightSelectionSample(), &light, &lightProb); SLRAssert(!std::isnan(lightProb) && !std::isinf(lightProb), "lightProb: unexpected value detected: %f", lightProb); LightPosQuery lpQuery(ray.time, wls); LightPosQueryResult lpResult; SampledSpectrum M = light.sample(lpQuery, pathSampler.getLightPosSample(), &lpResult); SLRAssert(!std::isnan(lpResult.areaPDF)/* && !std::isinf(xpResult.areaPDF)*/, "areaPDF: unexpected value detected: %f", lpResult.areaPDF); if (scene.testVisibility(surfPt, lpResult.surfPt, ray.time)) { float dist2; Vector3D shadowDir = lpResult.surfPt.getDirectionFrom(surfPt.p, &dist2); Vector3D shadowDir_l = lpResult.surfPt.shadingFrame.toLocal(-shadowDir); Vector3D shadowDir_sn = surfPt.shadingFrame.toLocal(shadowDir); EDF* edf = lpResult.surfPt.createEDF(wls, mem); SampledSpectrum Le = M * edf->evaluate(EDFQuery(), shadowDir_l); float lightPDF = lightProb * lpResult.areaPDF; SLRAssert(!Le.hasNaN() && !Le.hasInf(), "Le: unexpected value detected: %s", Le.toString().c_str()); SampledSpectrum fs = bsdf->evaluate(fsQuery, shadowDir_sn); float cosLight = absDot(-shadowDir, lpResult.surfPt.gNormal); float bsdfPDF = bsdf->evaluatePDF(fsQuery, shadowDir_sn) * cosLight / dist2; float MISWeight = 1.0f; if (!lpResult.posType.isDelta() && !std::isinf(lpResult.areaPDF)) MISWeight = (lightPDF * lightPDF) / (lightPDF * lightPDF + bsdfPDF * bsdfPDF); SLRAssert(MISWeight <= 1.0f, "Invalid MIS weight: %g", MISWeight); float G = absDot(shadowDir_sn, gNorm_sn) * cosLight / dist2; sp += alpha * Le * fs * (G * MISWeight / lightPDF); SLRAssert(!std::isnan(G) && !std::isinf(G), "G: unexpected value detected: %f", G); } } // get a next direction by sampling BSDF. BSDFQueryResult fsResult; SampledSpectrum fs = bsdf->sample(fsQuery, pathSampler.getBSDFSample(), &fsResult); if (fs == SampledSpectrum::Zero || fsResult.dirPDF == 0.0f) break; if (fsResult.dirType.isDispersive()) { fsResult.dirPDF /= WavelengthSamples::NumComponents; wls.flags |= WavelengthSamples::LambdaIsSelected; } alpha *= fs * absDot(fsResult.dir_sn, gNorm_sn) / fsResult.dirPDF; SLRAssert(!alpha.hasInf() && !alpha.hasNaN(), "alpha: %s\nlength: %u, cos: %g, dirPDF: %g", alpha.toString().c_str(), pathLength, absDot(fsResult.dir_sn, gNorm_sn), fsResult.dirPDF); Vector3D dirIn = surfPt.shadingFrame.fromLocal(fsResult.dir_sn); ray = Ray(surfPt.p, dirIn, ray.time, Ray::Epsilon); // find a next intersection point. isect = Intersection(); if (!scene.intersect(ray, &isect)) break; isect.getSurfacePoint(&surfPt); dirOut_sn = surfPt.shadingFrame.toLocal(-ray.dir); // implicit light sampling if (surfPt.isEmitting()) { float bsdfPDF = fsResult.dirPDF; EDF* edf = surfPt.createEDF(wls, mem); SampledSpectrum Le = surfPt.emittance(wls) * edf->evaluate(EDFQuery(), dirOut_sn); float lightProb = scene.evaluateProb(Light(isect.obj)); float dist2 = surfPt.getSquaredDistance(ray.org); float lightPDF = lightProb * surfPt.evaluateAreaPDF() * dist2 / absDot(ray.dir, surfPt.gNormal); SLRAssert(!Le.hasNaN() && !Le.hasInf(), "Le: unexpected value detected: %s", Le.toString().c_str()); SLRAssert(!std::isnan(lightPDF)/* && !std::isinf(lightPDF)*/, "lightPDF: unexpected value detected: %f", lightPDF); float MISWeight = 1.0f; if (!fsResult.dirType.isDelta()) MISWeight = (bsdfPDF * bsdfPDF) / (lightPDF * lightPDF + bsdfPDF * bsdfPDF); SLRAssert(MISWeight <= 1.0f, "Invalid MIS weight: %g", MISWeight); sp += alpha * Le * MISWeight; } if (surfPt.atInfinity) break; // Russian roulette float continueProb = std::min(alpha.importance(wls.selectedLambda) / initY, 1.0f); if (pathSampler.getPathTerminationSample() < continueProb) alpha /= continueProb; else break; } return sp; }
_Check_return_ VOODOO_METHODDEF(VSCore::Init)(_In_z_ CONST wchar_t * config) { if (config) { m_Parser->Add(VSTR("config"), config, VSVar_System); } else { m_Parser->Add(VSTR("config"), VSTR("VoodooShader.xml"), VSVar_System); } // Load variables, built-in first { wchar_t buffer[MAX_PATH]; ZeroMemory(buffer, MAX_PATH); // Global path GetVoodooPath(buffer); m_Parser->Add(VSTR("path"), buffer, VSVar_System); // Bin prefix GetVoodooBinPrefix(buffer); m_Parser->Add(VSTR("prefix"), buffer, VSVar_System); // Framework bin path GetVoodooBinPath(buffer); m_Parser->Add(VSTR("binpath"), buffer, VSVar_System); // Target HMODULE pTarget = GetModuleHandle(NULL); GetModuleFileName(pTarget, buffer, MAX_PATH); m_Parser->Add(VSTR("target"), buffer, VSVar_System); // Local path PathRemoveFileSpec(buffer); PathAddBackslash(buffer); m_Parser->Add(VSTR("local"), buffer, VSVar_System); // Startup path GetCurrentDirectory(MAX_PATH, buffer); PathAddBackslash(buffer); m_Parser->Add(VSTR("startup"), buffer, VSVar_System); GetModuleFileName(gCoreHandle, buffer, MAX_PATH); m_Parser->Add(VSTR("core"), buffer, VSVar_System); } // Command line processing LPWSTR cmdline = GetCommandLine(); m_Parser->Add(VSTR("args"), cmdline, VSVar_System); int cmdargc = 0; LPWSTR * cmdargv = CommandLineToArgvW(cmdline, &cmdargc); m_Parser->Add(VSTR("argc"), StringFormat(VSTR("%d")) << cmdargc, VSVar_System); for (int i = 0; i < cmdargc; ++i) { m_Parser->Add(StringFormat(VSTR("argv_%d")) << i, cmdargv[i], VSVar_System); } // Load the config try { m_ConfigFile = new pugi::xml_document(); // Try loading the config file from each major location String configPath = m_Parser->Parse(VSTR("$(config)"), VSParse_PathCanon); pugi::xml_parse_result result = m_ConfigFile->load_file(configPath.GetData()); if (!result) { configPath = m_Parser->Parse(VSTR("$(startup)\\$(config)"), VSParse_PathCanon); result = m_ConfigFile->load_file(configPath.GetData()); if (!result) { configPath = m_Parser->Parse(VSTR("$(local)\\$(config)"), VSParse_PathCanon); result = m_ConfigFile->load_file(configPath.GetData()); if (!result) { configPath = m_Parser->Parse(VSTR("$(path)\\$(config)"), VSParse_PathCanon); result = m_ConfigFile->load_file(configPath.GetData()); if (!result) { Throw(VOODOO_CORE_NAME, VSTR("Unable to find or parse config file."), nullptr); } } } } // Start setting things up pugi::xml_node configRoot = static_cast<pugi::xml_node>(*m_ConfigFile); pugi::xml_node globalNode = configRoot.select_single_node(L"/VoodooConfig/Global").node(); if (!globalNode) { Throw(VOODOO_CORE_NAME, VSTR("Could not find global config node."), nullptr); } // Create query for node text, used multiple times pugi::xpath_query xpq_getName(L"./@name"); pugi::xpath_query xpq_getText(L"./text()"); // Load variables { pugi::xpath_query xpq_getVariables(L"./Variables/Variable"); pugi::xpath_node_set nodes = xpq_getVariables.evaluate_node_set(globalNode); pugi::xpath_node_set::const_iterator iter = nodes.begin(); while (iter != nodes.end()) { String name = xpq_getName.evaluate_string(*iter).c_str(); String value = xpq_getText.evaluate_string(*iter).c_str(); m_Parser->Add(name, value); ++iter; } } // Open the logger as early as possible pugi::xpath_query logfQuery(L"./Log/File/text()"); pugi::xpath_query logaQuery(L"./Log/Append/text()"); pugi::xpath_query loglQuery(L"./Log/Level/text()"); String logFile = m_Parser->Parse(logfQuery.evaluate_string(globalNode)); String logLevelStr = m_Parser->Parse(loglQuery.evaluate_string(globalNode)); String logAppendStr = m_Parser->Parse(logaQuery.evaluate_string(globalNode)); LogLevel logLevel = VSLog_Default; try { logLevel = (LogLevel)stoi(logLevelStr.ToString()); } catch (const std::exception & exc) { UNREFERENCED_PARAMETER(exc); logLevel = VSLog_Default; } bool logAppend = logAppendStr.Compare(VSTR("true"), false) || logAppendStr.StartsWith("1"); m_Logger->Open(logFile, logAppend); m_Logger->SetFilter(logLevel); // Log extended build information String configMsg = m_Parser->Parse(VSTR("Config loaded from '$(config)'.")); m_Logger->LogMessage(VSLog_CoreNotice, VOODOO_CORE_NAME, configMsg); m_Logger->LogMessage(VSLog_CoreNotice, VOODOO_CORE_NAME, VOODOO_GLOBAL_COPYRIGHT_FULL); // Load plugins, starting with the core m_Server->LoadPlugin(this, VSTR("$(core)")); { pugi::xpath_query xpq_getPluginPaths(L"./Plugins/Path"); pugi::xpath_query xpq_getFilter(L"./@filter"); pugi::xpath_node_set nodes = xpq_getPluginPaths.evaluate_node_set(globalNode); pugi::xpath_node_set::const_iterator iter = nodes.begin(); while (iter != nodes.end()) { String filter = xpq_getFilter.evaluate_string(*iter); String path = xpq_getText.evaluate_string(*iter); m_Server->LoadPath(this, path, filter); ++iter; } } { pugi::xpath_query xpq_getPluginFiles(L"./Plugins/File"); pugi::xpath_node_set nodes = xpq_getPluginFiles.evaluate_node_set(globalNode); pugi::xpath_node_set::const_iterator iter = nodes.begin(); while (iter != nodes.end()) { String file = xpq_getText.evaluate_string(*iter); m_Server->LoadPlugin(this, file); ++iter; } } // Lookup classes pugi::xpath_query fsQuery(L"./Classes/FileSystem/text()"); pugi::xpath_query hookQuery(L"./Classes/HookManager/text()"); String fsClass = m_Parser->Parse(fsQuery.evaluate_string(globalNode).c_str()); String hookClass = m_Parser->Parse(hookQuery.evaluate_string(globalNode).c_str()); // Load less vital classes ObjectRef coreplugin = m_Server->CreateObject(this, hookClass); IHookManager * phm = nullptr; if (coreplugin && SUCCEEDED(coreplugin->QueryInterface(IID_IHookManager, (IObject**)&phm)) && phm) { m_HookManager = phm; phm->Release(); } else { StringFormat fmt(VSTR("Unable to create hook manager (class %1%).")); fmt << hookClass; Throw(VOODOO_CORE_NAME, fmt, this); } coreplugin = m_Server->CreateObject(this, fsClass); IFileSystem * pfs = nullptr; if (coreplugin && SUCCEEDED(coreplugin->QueryInterface(IID_IFileSystem, (IObject**)&pfs)) && pfs) { m_FileSystem = pfs; pfs->Release(); } else { StringFormat fmt(VSTR("Unable to create file system (class %1%).")); fmt << fsClass; Throw(VOODOO_CORE_NAME, fmt, this); } // ICore done loading m_Logger->LogMessage(VSLog_CoreInfo, VOODOO_CORE_NAME, VSTR("Core initialization complete.")); // Call finalization events this->CallEvent(EventIds::Finalize, 0, nullptr); // Return return VSF_OK; } catch (const Exception & exc) { if (m_Logger) { m_Logger->LogMessage(VSLog_CoreError, VOODOO_CORE_NAME, StringFormat(VSTR("Exception during Core creation: %1%")) << exc.strwhat()); } else { GlobalLog(VSTR("Unlogged exception during core creation: %S\n"), exc.what()); } return VSF_FAIL; } catch (const std::exception & exc) { if (m_Logger) { m_Logger->LogMessage(VSLog_CoreError, VOODOO_CORE_NAME, StringFormat(VSTR("Error during Core creation: %1%")) << exc.what()); } else { GlobalLog(VSTR("Unlogged exception during core creation: %S\n"), exc.what()); } return VSF_FAIL; } }