Русская рулетка действительно ответ?

21

Я видел, что в некоторых реализациях Path Tracing подход, называемый Russian Roulette, используется для отбраковки некоторых путей и распределения их вклада среди других путей.

Я понимаю, что вместо того, чтобы следовать по пути до тех пор, пока он не упадет ниже определенного порогового значения вклада, а затем отказаться от него, используется другой порог, и пути, чей вклад ниже этого порога, заканчиваются только с небольшой вероятностью. У других путей их вклад увеличен на величину, соответствующую разделению потерянной энергии от завершенного пути. Мне не ясно, должно ли это исправить смещение, вносимое техникой, или сама техника необходима, чтобы избежать смещения.

  • Русская рулетка дает непредвзятый результат?
  • Есть русская рулетка необходима для беспристрастного результата?

То есть, будет ли использование крошечного порога и просто прекращение пути в тот момент, когда он падает ниже этого порога, даст более смещенный или менее смещенный результат?

Учитывая произвольно большое количество выборок, сходятся ли оба подхода на несмещенном результирующем изображении?

Я пытаюсь понять основную причину использования подхода «Русская рулетка». Есть ли значительная разница в скорости или качестве?


Я понимаю, что энергия перераспределяется среди других лучей, чтобы сохранить полную энергию. Однако, может ли это перераспределение все еще не быть выполнено, если луч был прекращен при падении ниже фиксированного порога, а не имел случайно определенный срок службы после достижения этого порога?

И наоборот, если энергия, которая была бы потеряна при прекращении луча без перераспределения его энергии, в конечном итоге теряется в любом случае (поскольку лучи, на которые он перераспределяется, также со временем заканчиваются), как это улучшит ситуацию?

Trichoplax
источник

Ответы:

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. 2 отскока от большого героя Диснея 6 9 отказов от большого героя Диснея 6

Если мы выберем слишком низкое, изображение будет заметно смещено. Но если мы выбираем слишком высоко, мы тратим энергию и время вычислений.

Как вы заметили, один из способов решить эту проблему - завершить путь после того, как мы достигнем некоторого порога затухания. Это также добавляет уклон.

Зажим после порога, сработает , но опять же, как выбрать порог? Если мы выберем слишком большой, изображение будет заметно смещенным, слишком маленьким, и мы тратим ресурсы.

Русская рулетка пытается решить эти проблемы непредвзято. Во-первых, вот код:

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. Если энергия, которая будет потеряна при прекращении луча без перераспределения его энергии, в конечном итоге будет потеряна (так как лучи, на которые он перераспределяется, также в конечном итоге прекращаются), как это улучшит ситуацию?
    • Русская рулетка только останавливает подпрыгивание. Это не удаляет образец полностью. Кроме того, «потерянная» энергия учитывается в скачках до завершения. Таким образом, единственный способ для энергии, которая в конечном итоге «все равно будет потеряна», - это иметь полностью черную комнату.

В конце концов, Русская рулетка - это очень простой алгоритм, который использует очень небольшое количество дополнительных вычислительных ресурсов. В обмен на это можно сэкономить большое количество вычислительных ресурсов. Поэтому я не вижу причины не использовать его.

RichieSams
источник
честно говоря, я не совсем уверен в этом to be completely unbiased it must be random. Я думаю, что вы все еще можете получить математические результаты, используя дробное взвешивание сэмплов, а не двоичную передачу / раздачу, которую навязывает русская рулетка, просто рулетка будет сходиться быстрее, потому что она выполняет выборку идеальной важности.
v.oddou
9

Сама техника русской рулетки - это способ завершения пути без внесения системного смещения. Принцип довольно прост: если в определенной вершине у вас есть 10% шанс произвольно заменить энергию на 0, и если вы делаете это бесконечное количество раз, вы увидите на 10% меньше энергии. Увеличение энергии просто компенсирует это. Если вы не компенсируете энергию, потерянную из-за завершения пути, то русская рулетка будет предвзятой, но вся техника является полезным способом избежать смещения.

Если бы я был противником, пытавшимся доказать, что метод «завершать пути, вклад которого меньше некоторого небольшого фиксированного значения» является предвзятым, я бы построил сцену с огнями, настолько тусклыми, что участвующие пути всегда будут меньше этого значения. Возможно, я имитирую камеру при слабом освещении.

Но, конечно, вы всегда можете выставить фиксированное значение в качестве настраиваемого параметра для пользователя, чтобы он мог отбросить его еще дальше, если его сцена окажется в условиях низкой освещенности. Так что давайте на минуту проигнорируем этот пример.

Что произойдет, если я рассмотрю объект, освещенный множеством путей с очень низкой энергией, которые собираются параболическим отражателем ? Низкоэнергетические пути не обязательно проносятся без разбора таким образом, которым можно полностью пренебречь. Аналогично, рассуждения применимы, например, к отрезанию путей после фиксированного числа отскоков: вы можете построить сцену с путем, который отразит серию из 20 зеркал, прежде чем ударить по объекту.

Другой способ взглянуть на это: если вы установите вклад пути в 0 после того, как он упадет ниже некоторого фиксированного эпсилона, как вы исправите эту потерю энергии? Вы не просто уменьшаете общую энергию на какую-то долю. Вы ничего не знаете о том, сколько энергии вы пренебрегаете, потому что вы отключаете на некотором пороге вклада, прежде чем узнаете другой фактор: падающую энергию.

Джон Калсбек
источник
8

Просто чтобы расширить некоторые другие ответы, доказательство того, что Русская рулетка не дает необъективного результата, очень просто.

F

Fзнак равноF1++FN

Заменить каждый термин на:

Fя'знак равно{1пяFяс вероятностью пя0в противном случае

Потом:

Е[Fя']знак равнопя×1пяЕ[Fя]+(1-пя)×0знак равноЕ[Fя]

пяF

Псевдоним
источник