रूसी रूले को समझने के लिए, आइए एक बहुत ही बुनियादी पिछड़े पथ अनुरेखक को देखें:
void RenderPixel(uint x, uint y, UniformSampler *sampler) {
Ray ray = m_scene->Camera.CalculateRayFromPixel(x, y, sampler);
float3 color(0.0f);
float3 throughput(1.0f);
// Bounce the ray around the scene
for (uint bounces = 0; bounces < 10; ++bounces) {
m_scene->Intersect(ray);
// The ray missed. Return the background color
if (ray.geomID == RTC_INVALID_GEOMETRY_ID) {
color += throughput * float3(0.846f, 0.933f, 0.949f);
break;
}
// We hit an object
// Fetch the material
Material *material = m_scene->GetMaterial(ray.geomID);
// The object might be emissive. If so, it will have a corresponding light
// Otherwise, GetLight will return nullptr
Light *light = m_scene->GetLight(ray.geomID);
// If we hit a light, add the emmisive light
if (light != nullptr) {
color += throughput * light->Le();
}
float3 normal = normalize(ray.Ng);
float3 wo = normalize(-ray.dir);
float3 surfacePos = ray.org + ray.dir * ray.tfar;
// Get the new ray direction
// Choose the direction based on the material
float3 wi = material->Sample(wo, normal, sampler);
float pdf = material->Pdf(wi, normal);
// Accumulate the brdf attenuation
throughput = throughput * material->Eval(wi, wo, normal) / pdf;
// Shoot a new ray
// Set the origin at the intersection point
ray.org = surfacePos;
// Reset the other ray properties
ray.dir = wi;
ray.tnear = 0.001f;
ray.tfar = embree::inf;
ray.geomID = RTC_INVALID_GEOMETRY_ID;
ray.primID = RTC_INVALID_GEOMETRY_ID;
ray.instID = RTC_INVALID_GEOMETRY_ID;
ray.mask = 0xFFFFFFFF;
ray.time = 0.0f;
}
m_scene->Camera.FrameBuffer.SplatPixel(x, y, color);
}
अर्थात। हम दृश्य के चारों ओर उछलते हैं, रंग और प्रकाश क्षीणन जमा करते हैं जैसे हम जाते हैं। पूरी तरह से गणितीय निष्पक्ष होने के लिए, बाउंस होना चाहिए को अनंत तक जाना । लेकिन यह अवास्तविक है, और जैसा कि आपने उल्लेख किया, दृष्टिगत रूप से आवश्यक नहीं; अधिकांश दृश्यों के लिए, निश्चित संख्या के बाद, 10 का कहना है, अंतिम रंग में योगदान की मात्रा बहुत कम है।
इसलिए कंप्यूटिंग संसाधनों को बचाने के लिए, कई पथ ट्रैकरों में बाउंस की संख्या की एक कठिन सीमा है। यह पूर्वाग्रह जोड़ता है।
उस ने कहा, यह चुनना मुश्किल है कि उस कठिन सीमा को क्या होना चाहिए। 2 बाउंस के बाद कुछ दृश्य बहुत अच्छे लगते हैं; अन्य (ट्रांसमिशन या SSS के साथ) 10 या 20 तक लग सकते हैं।
यदि हम बहुत कम चुनते हैं, तो छवि स्पष्ट रूप से पक्षपाती होगी। लेकिन अगर हम बहुत अधिक चुनते हैं, तो हम गणना ऊर्जा और समय बर्बाद कर रहे हैं।
इसे हल करने का एक तरीका, जैसा कि आपने उल्लेख किया है, हम क्षीणन की कुछ सीमा तक पहुँचने के बाद पथ को समाप्त कर सकते हैं। यह पूर्वाग्रह भी जोड़ता है।
एक दहलीज के बाद clamping, काम करेगा , लेकिन फिर, हम दहलीज का चयन कैसे करते हैं? यदि हम बहुत बड़ा चुनते हैं, तो छवि नेत्रहीन पक्षपाती, बहुत छोटी होगी, और हम संसाधनों को बर्बाद कर रहे हैं।
रूसी रूले ने निष्पक्ष तरीके से इन समस्याओं को हल करने का प्रयास किया। सबसे पहले, यहाँ कोड है:
void RenderPixel(uint x, uint y, UniformSampler *sampler) {
Ray ray = m_scene->Camera.CalculateRayFromPixel(x, y, sampler);
float3 color(0.0f);
float3 throughput(1.0f);
// Bounce the ray around the scene
for (uint bounces = 0; bounces < 10; ++bounces) {
m_scene->Intersect(ray);
// The ray missed. Return the background color
if (ray.geomID == RTC_INVALID_GEOMETRY_ID) {
color += throughput * float3(0.846f, 0.933f, 0.949f);
break;
}
// We hit an object
// Fetch the material
Material *material = m_scene->GetMaterial(ray.geomID);
// The object might be emissive. If so, it will have a corresponding light
// Otherwise, GetLight will return nullptr
Light *light = m_scene->GetLight(ray.geomID);
// If we hit a light, add the emmisive light
if (light != nullptr) {
color += throughput * light->Le();
}
float3 normal = normalize(ray.Ng);
float3 wo = normalize(-ray.dir);
float3 surfacePos = ray.org + ray.dir * ray.tfar;
// Get the new ray direction
// Choose the direction based on the material
float3 wi = material->Sample(wo, normal, sampler);
float pdf = material->Pdf(wi, normal);
// Accumulate the brdf attenuation
throughput = throughput * material->Eval(wi, wo, normal) / pdf;
// Russian Roulette
// Randomly terminate a path with a probability inversely equal to the throughput
float p = std::max(throughput.x, std::max(throughput.y, throughput.z));
if (sampler->NextFloat() > p) {
break;
}
// Add the energy we 'lose' by randomly terminating paths
throughput *= 1 / p;
// Shoot a new ray
// Set the origin at the intersection point
ray.org = surfacePos;
// Reset the other ray properties
ray.dir = wi;
ray.tnear = 0.001f;
ray.tfar = embree::inf;
ray.geomID = RTC_INVALID_GEOMETRY_ID;
ray.primID = RTC_INVALID_GEOMETRY_ID;
ray.instID = RTC_INVALID_GEOMETRY_ID;
ray.mask = 0xFFFFFFFF;
ray.time = 0.0f;
}
m_scene->Camera.FrameBuffer.SplatPixel(x, y, color);
}
रूसी रूले बेतरतीब ढंग से थ्रूपुट के बराबर एक संभावना के साथ एक मार्ग को समाप्त करता है। तो कम थ्रूपुट वाले रास्ते जो दृश्य में ज्यादा योगदान नहीं देंगे, समाप्त होने की अधिक संभावना है।
अगर हम वहाँ रुकते हैं, तो हम अभी भी पक्षपाती हैं। हम उस पथ की ऊर्जा को खो देते हैं जिसे हम अनियमित रूप से समाप्त करते हैं। इसे निष्पक्ष बनाने के लिए, हम गैर-समाप्त रास्तों की ऊर्जा को समाप्त करने की उनकी संभावना से बढ़ावा देते हैं। यह यादृच्छिक होने के साथ-साथ रूसी रूले को निष्पक्ष बनाता है।
अपने अंतिम प्रश्नों के उत्तर देने के लिए:
- क्या रूसी रूले निष्पक्ष परिणाम देता है?
- एक निष्पक्ष परिणाम के लिए रूसी रूले आवश्यक है?
- आप निष्पक्ष से क्या मतलब है पर निर्भर करता है। यदि आप गणितीय रूप से मतलब रखते हैं, तो हाँ। हालांकि, यदि आप नेत्रहीन का मतलब है, तो नहीं। आपको बस आपको अधिकतम पथ गहराई और कटऑफ सीमा को बहुत सावधानी से चुनना होगा। यह बहुत थकाऊ हो सकता है क्योंकि यह दृश्य से दृश्य में बदल सकता है।
- क्या आप एक निश्चित संभावना (कट-ऑफ) का उपयोग कर सकते हैं, और फिर 'खोई हुई' ऊर्जा का पुनर्वितरण कर सकते हैं। क्या यह निष्पक्ष है?
- यदि आप एक निश्चित संभावना का उपयोग करते हैं, तो आप पूर्वाग्रह जोड़ रहे हैं। 'खोई' ऊर्जा का पुनर्वितरण करके, आप पूर्वाग्रह को कम करते हैं, लेकिन यह अभी भी गणितीय रूप से पक्षपाती है। पूरी तरह से निष्पक्ष होने के लिए, यह यादृच्छिक होना चाहिए।
- यदि ऊर्जा को पुनर्वितरित किए बिना किरण को समाप्त करके खो जाने वाली ऊर्जा अंततः वैसे भी खो जाती है (जैसा कि किरणों को पुनर्वितरित किया जाता है, उसे अंततः समाप्त कर दिया जाता है), यह कैसे स्थिति में सुधार करता है?
- रूसी रूले केवल उछलता है। यह पूरी तरह से नमूना नहीं निकालता है। इसके अलावा, 'खोई हुई' ऊर्जा का हिसाब बाउंस को खत्म करने के लिए किया जाता है। तो ऊर्जा के लिए 'अंततः वैसे भी खो जाने' का एकमात्र तरीका पूरी तरह से एक काला कमरा होगा।
अंत में, रूसी रूले एक बहुत ही सरल एल्गोरिथ्म है जो बहुत कम मात्रा में अतिरिक्त कम्प्यूटेशनल संसाधनों का उपयोग करता है। बदले में, यह बड़ी मात्रा में कम्प्यूटेशनल संसाधनों को बचा सकता है। इसलिए, मैं वास्तव में इसका उपयोग नहीं करने का एक कारण नहीं देख सकता।
to be completely unbiased it must be random
। मुझे लगता है कि आप अभी भी गणित-ओके परिणाम प्राप्त कर सकते हैं नमूनों के भिन्नात्मक भार का उपयोग करके, बाइनरी पास / ड्रॉप के बजाय जो रूसी रूले लगाता है, यह सिर्फ इतना है कि रूलेट तेजी से अभिसरण करेगा क्योंकि यह एक आदर्श महत्व नमूना का संचालन कर रहा है।