俄罗斯轮盘真的是答案吗?


21

我已经看到,在“路径跟踪”的某些实现中,一种名为“俄罗斯轮盘赌”的方法用于剔除某些路径并在其他路径之间共享它们的作用。

我知道,与其遵循一条路径直到其下降到某个贡献阈值以下,然后放弃它,而是使用一个不同的阈值,并且贡献小于该阈值的路径仅以很小的概率终止。其他路径的贡献增加了相应于共享来自终止路径的损耗能量的量。我不清楚这是否是为了纠正该技术带来的偏差,还是整个技术本身是否需要避免偏差。

  • 俄罗斯轮盘赌给出公正的结果吗?
  • 是否需要俄罗斯轮盘才能获得公正的结果?

也就是说,使用很小的阈值并在路径下降到该阈值以下时立即终止路径会产生更大的偏差还是更少的偏差?

给定任意数量的样本,两种方法都将收敛于无偏的结果图像上吗?

我想了解使用俄罗斯轮盘赌方法的根本原因。速度或质量上有明显差异吗?


我知道能量会在其他射线中重新分配,以保持总能量。但是,如果光线在下降到固定阈值以下时终止,而不是在达到该阈值后具有随机确定的寿命,则是否仍不能进行这种重新分配?

相反,如果最终由于终止光线而不重新分配能量而损失的能量最终仍然丢失了(因为最终将重新分配光线的能量也终止了),这如何改善这种情况?

Answers:


26

为了理解俄罗斯轮盘赌,让我们看一个非常基本的后向路径跟踪器:

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。 迪士尼大英雄6反弹2次 迪士尼大英雄6弹跳9

如果我们选择太低,图像将明显偏斜。但是,如果选择的太高,则会浪费计算能力和时间。

正如您所指出的,解决此问题的一种方法是在达到一定衰减阈值后终止路径。这也增加了偏见。

在阈值之后进行夹紧将起作用,但是再次,我们如何选择阈值?如果选择太大,图像将明显偏斜,太小,并且浪费资源。

俄罗斯轮盘赌试图以公正的方式解决这些问题。首先,下面是代码:

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);
}

俄罗斯轮盘随机终止路径的概率与吞吐量成反比。因此,吞吐率低而不会对场景产生太大影响的路径更有可能被终止。

如果我们停在那里,我们仍然有偏见。我们“失去”了我们随机终止的路径的能量。为了使其无偏,我们通过终止路径的可能性来提高未终止路径的能量。加上随机性,这使得俄罗斯轮盘不偏不倚。

要回答您的最后一个问题:

  1. 俄罗斯轮盘赌给出公正的结果吗?
  2. 是否需要俄罗斯轮盘才能获得公正的结果?
    • 取决于您的偏见。如果您的数学意思是,那么可以。但是,如果您的意思是视觉上的意思,那么不会。您只需要非常仔细地选择最大路径深度和截止阈值即可。这可能非常乏味,因为它可能因场景而异。
  3. 您可以使用固定的概率(截止),然后重新分配“丢失”的能量吗?这是公正的吗?
    • 如果您使用固定的概率,则会增加偏差。通过重新分配“丢失”的能量,可以减少偏差,但在数学上仍然有偏差。要完全不偏不倚,它必须是随机的。
  4. 如果通过终止射线而不重新分配其能量而损失的能量最终仍将丢失(因为最终将重新分配射线的射线也终止了),这如何改善情况?
    • 俄罗斯轮盘赌只会停止弹跳。它不能完全去除样品。同样,“丢失”的能量在反弹到终止为止。因此,能量“最终无论如何都会丢失”的唯一方法是拥有一个完全黑的房间。

最后,Russian Roulette是一种非常简单的算法,它使用了非常少量的额外计算资源。作为交换,它可以节省大量的计算资源。因此,我看不出使用它的原因。


老实说我不太确定to be completely unbiased it must be random。我认为您仍然可以通过使用样本加权的小数权重而不是俄罗斯轮盘赌强加的二进制通过/丢弃来获得数学确定的结果,只是因为轮盘赌正在运行一个非常重要的抽样,因此轮盘会收敛得更快。
v.oddou

9

俄罗斯轮盘赌技术本身就是一种在不引入系统偏见的情况下终止路径的方法。原理很简单:如果在特定的顶点上有10%的机会任意将能量替换为0,并且您进行无限次,则看到的能量将减少10%。能量提升可以弥补这一点。如果您没有补偿由于路径终止而造成的能量损失,那么俄罗斯轮盘赌会产生偏差,但是整个技术都是避免偏差的有用方法。

如果我是一个对手,希望证明“贡献小于某些固定值的终结路径”技术是有偏见的,那么我将构建一个灯光昏暗的场景,以使贡献路径始终小于该值。也许我在模拟低照度相机。

但是,当然,您始终可以将固定值作为可调整的参数提供给用户,因此,即使场景恰好是昏暗的环境,他们也可以将其进一步降低。因此,让我们暂时忽略该示例。

如果我考虑一个物体被抛物面反射器收集的许多非常低能量的路径照亮,会发生什么?低能耗路径不一定会以您可以完全忽略的方式随意反弹。类似地,推理也适用于,例如,在固定数量的反弹之后切断路径:您可以构造一个场景,该场景的路径可以在击中对象之前从一系列20个反射镜中反弹。

另一种看待它的方法:如果将路径的贡献值降低到某个固定的epsilon以下后将其贡献设置为0,您如何校正该能量损失?您并不是简单地将总能量降低一小部分。您对忽略多少能量一无所知,因为您在知道另一个因素(入射能量)之前就已在某个贡献阈值处截止。


8

只是为了扩展其他一些答案,俄罗斯轮盘赌不会产生偏见的证据非常简单。

F

F=F1个++Fñ

将每个术语替换为:

F一世={1个p一世F一世很有可能 p一世0除此以外

然后:

Ë[F一世]=p一世×1个p一世Ë[F一世]+1个-p一世×0=Ë[F一世]

p一世F

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.