为了理解俄罗斯轮盘赌,让我们看一个非常基本的后向路径跟踪器:
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次)后,对最终颜色的贡献量非常小。
因此,为了节省计算资源,许多路径跟踪器对跳出次数有严格的限制。这增加了偏见。
也就是说,很难选择该硬限制应该是多少。反弹2次后,某些场景看起来不错;其他(例如,使用传输或SSS)可能最多需要10或20。
如果我们选择太低,图像将明显偏斜。但是,如果选择的太高,则会浪费计算能力和时间。
正如您所指出的,解决此问题的一种方法是在达到一定衰减阈值后终止路径。这也增加了偏见。
在阈值之后进行夹紧将起作用,但是再次,我们如何选择阈值?如果选择太大,图像将明显偏斜,太小,并且浪费资源。
俄罗斯轮盘赌试图以公正的方式解决这些问题。首先,下面是代码:
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是一种非常简单的算法,它使用了非常少量的额外计算资源。作为交换,它可以节省大量的计算资源。因此,我看不出不使用它的原因。
to be completely unbiased it must be random
。我认为您仍然可以通过使用样本加权的小数权重而不是俄罗斯轮盘赌强加的二进制通过/丢弃来获得数学确定的结果,只是因为轮盘赌正在运行一个非常重要的抽样,因此轮盘会收敛得更快。