Để hiểu về Roulette Nga, chúng ta hãy xem xét một công cụ tìm đường lùi rất cơ bản:
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);
}
I E. chúng tôi tung lên xung quanh khung cảnh, tích lũy màu sắc và suy giảm ánh sáng khi chúng tôi đi. Để hoàn toàn không thiên vị về mặt toán học, các lần thoát phải đi đến vô cùng. Nhưng điều này là không thực tế, và như bạn đã lưu ý, không cần thiết về mặt trực quan; đối với hầu hết các cảnh, sau một số lần thoát nhất định, giả sử là 10, số tiền đóng góp cho màu cuối cùng là rất rất nhỏ.
Vì vậy, để tiết kiệm tài nguyên máy tính, nhiều công cụ theo dõi đường dẫn có giới hạn cứng đối với số lần thoát. Điều này thêm sự thiên vị.
Điều đó nói rằng, thật khó để chọn giới hạn cứng đó là gì. Một số cảnh trông tuyệt vời sau 2 lần nảy; những người khác (nói với truyền hoặc SSS) có thể mất tới 10 hoặc 20.
Nếu chúng ta chọn quá thấp, hình ảnh sẽ bị sai lệch rõ ràng. Nhưng nếu chúng ta chọn quá cao, chúng ta sẽ lãng phí năng lượng và thời gian tính toán.
Một cách để giải quyết điều này, như bạn đã lưu ý, là chấm dứt con đường sau khi chúng ta đạt đến ngưỡng suy giảm nào đó. Điều này cũng thêm sự thiên vị.
Kẹp sau một ngưỡng, sẽ hoạt động , nhưng một lần nữa, làm thế nào để chúng ta chọn ngưỡng? Nếu chúng ta chọn quá lớn, hình ảnh sẽ bị sai lệch rõ ràng, quá nhỏ và chúng ta đang lãng phí tài nguyên.
Russian Roulette cố gắng giải quyết những vấn đề này một cách không thiên vị. Đầu tiên, đây là 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;
// 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);
}
Russian Roulette chấm dứt ngẫu nhiên một con đường với xác suất nghịch đảo với thông lượng. Vì vậy, các đường dẫn có thông lượng thấp sẽ không đóng góp nhiều cho cảnh có nhiều khả năng bị chấm dứt.
Nếu chúng ta dừng lại ở đó, chúng ta vẫn thiên vị. Chúng ta 'mất' năng lượng của con đường chúng ta chấm dứt ngẫu nhiên. Để làm cho nó không thiên vị, chúng tôi tăng cường năng lượng của các đường dẫn không bị chấm dứt bởi xác suất của chúng bị chấm dứt. Điều này, cùng với việc là ngẫu nhiên, làm cho Roulette Nga không thiên vị.
Để trả lời câu hỏi cuối cùng của bạn:
- Russian Roulette có cho kết quả không thiên vị không?
- Russian Roulette có cần thiết cho một kết quả không thiên vị không?
- Phụ thuộc vào những gì bạn có nghĩa là không thiên vị. Nếu bạn có ý nghĩa toán học, thì có. Tuy nhiên, nếu bạn có nghĩa là trực quan, thì không. Bạn chỉ cần chọn cho bạn độ sâu đường dẫn tối đa và ngưỡng cắt rất rất cẩn thận. Điều này có thể rất tẻ nhạt vì nó có thể thay đổi từ cảnh này sang cảnh khác.
- Bạn có thể sử dụng một xác suất cố định (cắt bỏ), và sau đó phân phối lại năng lượng 'mất'. Đây có phải là thiên vị?
- Nếu bạn sử dụng một xác suất cố định, bạn đang thêm sai lệch. Bằng cách phân phối lại năng lượng 'mất', bạn giảm bớt sự thiên vị, nhưng nó vẫn thiên về toán học. Để hoàn toàn không thiên vị, nó phải là ngẫu nhiên.
- Nếu năng lượng sẽ bị mất bằng cách chấm dứt một tia mà không phân phối lại thì cuối cùng năng lượng cũng bị mất (vì các tia được phân phối lại cuối cùng cũng bị chấm dứt), làm thế nào để cải thiện tình hình này?
- Russian Roulette chỉ dừng lại nảy. Nó không loại bỏ hoàn toàn mẫu. Ngoài ra, năng lượng 'mất' được tính trong các lần nảy cho đến khi chấm dứt. Vì vậy, cách duy nhất để năng lượng "cuối cùng bị mất" sẽ là có một căn phòng hoàn toàn tối đen.
Cuối cùng, Russian Roulette là một thuật toán rất đơn giản sử dụng một lượng rất nhỏ tài nguyên tính toán bổ sung. Đổi lại, nó có thể tiết kiệm một lượng lớn tài nguyên tính toán. Vì vậy, tôi thực sự không thể thấy một lý do để không sử dụng nó.
to be completely unbiased it must be random
. Tôi nghĩ rằng bạn vẫn có thể nhận được kết quả toán học bằng cách sử dụng các mẫu vật phân đoạn, thay vì truyền / thả nhị phân mà roulette Nga áp đặt, chỉ là roulette sẽ hội tụ nhanh hơn vì nó vận hành lấy mẫu quan trọng hoàn hảo.