Rus Ruletini anlamak için, çok temel bir geri yol izleyicisine bakalım:
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);
}
IE. sahne etrafında zıplıyoruz, giderken renk ve ışık zayıflaması biriktiriyoruz. Tamamen matematiksel olarak tarafsız olması için, sıçramalar gerektiğini sonsuza gidin. Ancak bu gerçekçi değildir ve belirttiğiniz gibi görsel olarak gerekli değildir; çoğu sahne için, belirli bir sayıda sıçramadan sonra, örneğin 10, son renge katkı miktarı çok azdır.
Bu nedenle, bilgisayar kaynaklarını kurtarmak için, birçok yol izleyicisinin sıçrama sayısı için zor bir sınırı vardır. Bu önyargı ekler.
Bununla birlikte, bu zor sınırın ne olması gerektiğini seçmek zor. Bazı sahneler 2 sekmeden sonra harika görünüyor; diğerleri (örneğin iletim veya SSS ile) 10 veya 20'ye kadar sürebilir.
Çok düşük seçersek, görüntü görünür şekilde önyargılı olur. Ama çok yüksek seçersek, hesaplama enerjisini ve zamanını boşa harcıyoruz.
Bunu çözmenin bir yolu, belirttiğiniz gibi, bir miktar zayıflama eşiğine ulaştıktan sonra yolu sonlandırmaktır. Bu da önyargı ekler.
Bir eşikten sonra sıkıştırma, işe yarayacak , ancak yine eşiği nasıl seçeceğiz? Çok büyük seçersek, görüntü görünür şekilde önyargılı, çok küçük olacak ve kaynakları israf edeceğiz.
Rus Ruleti bu sorunları tarafsız bir şekilde çözmeye çalışır. İlk olarak, kod İşte:
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);
}
Rus Ruleti, verime ters orantılı bir yolu rastgele bir şekilde sonlandırır. Dolayısıyla, sahneye fazla katkıda bulunmayacak şekilde düşük iş hacmine sahip yolların sonlandırılması daha olasıdır.
Orada durursak, hala önyargılıyız. Rasgele sonlandırdığımız yolun enerjisini 'kaybediyoruz'. Tarafsız hale getirmek için, sonlandırılmayan yolların enerjisini, sonlandırılma olasılıklarıyla arttırırız. Bu, rastgele olmakla birlikte Rus Ruletini tarafsız kılar.
Son sorularınızı cevaplamak için:
- Rus Ruleti tarafsız bir sonuç veriyor mu?
- Tarafsız bir sonuç için Rus Ruleti gerekli mi?
- Tarafsız ile ne demek istediğine bağlı. Matematiksel olarak demek istiyorsan, evet. Ancak, görsel olarak demek istiyorsan, hayır. Maksimum yol derinliğini ve kesim eşiğini çok dikkatli bir şekilde seçmeniz yeterlidir. Sahneden sahneye değişebileceğinden bu çok sıkıcı olabilir.
- Sabit bir olasılık (kesme) kullanabilir ve daha sonra 'kayıp' enerjiyi yeniden dağıtabilir misiniz? Bu tarafsız mı?
- Sabit bir olasılık kullanırsanız, yanlılık eklersiniz. 'Kayıp' enerjiyi yeniden dağıtarak önyargıyı azaltırsınız, ancak yine de matematiksel olarak önyargılıdır. Tamamen tarafsız olması için rastgele olması gerekir.
- Enerjisini yeniden dağıtmadan bir ışını sonlandırarak kaybedilecek olan enerji nihayetinde kaybolursa (yeniden dağıtıldığı ışınlar da sona erdiğinden), bu durumu nasıl iyileştirir?
- Rus Ruleti sadece zıplamayı durdurur. Örneği tamamen çıkarmaz. Ayrıca, 'kayıp' enerji fesih kadar zıplamalarda muhasebeleştirilir. Böylece, enerjinin 'nihayetinde kaybolmasının' tek yolu tamamen siyah bir odaya sahip olmak olacaktır.
Sonunda, Rus Ruleti, çok az miktarda ekstra hesaplama kaynağı kullanan çok basit bir algoritmadır. Buna karşılık, büyük miktarda hesaplama kaynağı kurtarabilir. Bu nedenle, gerçekten bir neden göremiyorum değil kullanmak için.
to be completely unbiased it must be random
. Rus ruletinin uyguladığı ikili geçiş / bırakma yerine numunelerin kesirli ağırlıklarını kullanarak hala matematiksel sonuçları elde edebileceğinizi düşünüyorum, sadece ruletin daha hızlı birleşeceği için mükemmel bir önem örneklemesi yapıyor.