เพื่อที่จะเข้าใจรูเล็ตรัสเซียมาดูที่เส้นทางย้อนกลับพื้นฐาน:
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 เราเด้งไปรอบ ๆ ฉากสะสมสีและลดทอนแสงเมื่อเราไป เพื่อที่จะไม่เอนเอียงทางคณิตศาสตร์อย่างสมบูรณ์การตีกลับควรไปที่อินฟินิตี้ แต่นี่เป็นสิ่งที่ไม่สมจริงและตามที่คุณสังเกตเห็นไม่จำเป็นต้องใช้สายตา สำหรับฉากส่วนใหญ่หลังจากที่มีการตีกลับมาเป็นจำนวนมากให้บอกว่า 10 จำนวนของการมีส่วนร่วมกับสีสุดท้ายนั้นน้อยมาก
ดังนั้นเพื่อประหยัดทรัพยากรในการคำนวณ Tracers เส้นทางจำนวนมากมีการ จำกัด จำนวนการตีกลับอย่างหนัก นี่เป็นการเพิ่มอคติ
ที่กล่าวว่ามันยากที่จะเลือกสิ่งที่ขีด จำกัด ที่ควรจะเป็น บางฉากดูดีหลังจากการตีกลับ 2 ครั้ง อื่น ๆ (พูดด้วยการส่งหรือ SSS) อาจใช้เวลานานถึง 10 หรือ 20
หากเราเลือกต่ำเกินไปภาพจะลำเอียงอย่างเห็นได้ชัด แต่ถ้าเราเลือกสูงเกินไปเราจะสูญเสียพลังงานและเวลาในการคำนวณ
วิธีหนึ่งในการแก้ไขปัญหานี้ตามที่คุณระบุไว้คือการยุติเส้นทางหลังจากที่เราถึงเกณฑ์การลดทอนแล้ว สิ่งนี้ยังเพิ่มอคติ
การยึดตามขีด จำกัด จะทำงานได้ แต่อีกครั้งเราจะเลือกขีด จำกัด ได้อย่างไร หากเราเลือกขนาดใหญ่เกินไปภาพจะมีอคติอย่างเห็นได้ชัดเล็กเกินไปและเราสิ้นเปลืองทรัพยากร
Russian Roulette พยายามแก้ไขปัญหาเหล่านี้อย่างเป็นกลาง ก่อนอื่นนี่คือรหัส:
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
จริงๆ ฉันคิดว่าคุณยังสามารถรับผลลัพธ์ทางคณิตศาสตร์ได้โดยใช้การสุ่มตัวอย่างแบบเศษส่วนแทนที่จะใช้รหัสผ่าน / วางที่รูเล็ตรัสเซียกำหนด